Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package mockit.asm.constantPool;
import javax.annotation.*;
import mockit.asm.jvmConstants.*;
import mockit.asm.types.*;
import mockit.asm.util.*;
import mockit.internal.util.*;
import static mockit.asm.jvmConstants.ConstantPoolTypes.*;
import static mockit.internal.util.ClassLoad.OBJECT;
/**
* Allows the constant pool for a classfile to be created from scratch, when that classfile itself is being generated or
* modified from an existing class file.
*/
@SuppressWarnings({"ClassWithTooManyFields", "OverlyCoupledClass"})
public final class ConstantPoolGeneration
{
/**
* The constant pool of the class file being generated/modified.
*/
@Nonnull private final ByteVector pool;
/**
* The constant pool's hash table data.
*/
@Nonnull private Item[] items;
/**
* The threshold of the constant pool's hash table.
*/
@Nonnegative private int threshold;
/**
* Index of the next item to be added in the constant pool.
*/
@Nonnegative private int index;
@Nonnull private final StringItem reusableUTF8Item;
@Nonnull private final StringItem reusableStringItem;
@Nonnull private final NameAndTypeItem reusableNameTypeItem;
@Nonnull private final ClassMemberItem reusableClassMemberItem;
@Nonnull private final IntItem reusableIntItem;
@Nonnull private final LongItem reusableLongItem;
@Nonnull private final FloatItem reusableFloatItem;
@Nonnull private final DoubleItem reusableDoubleItem;
@Nonnull private final MethodHandleItem reusableMethodHandleItem;
@Nonnull private final DynamicItem reusableDynamicItem;
/**
* A type table used to temporarily store internal names that will not necessarily be stored in the constant pool.
* This type table is used by the control flow and data flow analysis algorithm to compute stack map frames from scratch.
* This array associates to each index i the TypeTableItem whose index is i.
* All TypeTableItem objects stored in this array are also stored in the {@link #items} hash table.
* These two arrays allow to retrieve an Item from its index or, conversely, to get the index of an Item from its
* value. Each TypeTableItem stores an internal name in its {@link TypeTableItem#typeDesc} field.
*/
private TypeTableItem[] typeTable;
/**
* Number of elements in the {@link #typeTable} array.
*/
private short typeCount;
@Nonnull private final NormalTypeTableItem reusableNormalItem;
@Nonnull private final UninitializedTypeTableItem reusableUninitializedItem;
@Nonnull private final MergedTypeTableItem reusableMergedItem;
@SuppressWarnings("OverlyCoupledMethod")
public ConstantPoolGeneration() {
pool = new ByteVector();
items = new Item[256];
//noinspection NumericCastThatLosesPrecision
threshold = (int) (0.75d * items.length);
index = 1;
reusableUTF8Item = new StringItem();
reusableStringItem = new StringItem();
reusableNameTypeItem = new NameAndTypeItem(0);
reusableClassMemberItem = new ClassMemberItem(0);
reusableIntItem = new IntItem(0);
reusableLongItem = new LongItem(0);
reusableFloatItem = new FloatItem(0);
reusableDoubleItem = new DoubleItem(0);
reusableMethodHandleItem = new MethodHandleItem(0);
reusableDynamicItem = new DynamicItem(0);
reusableNormalItem = new NormalTypeTableItem();
reusableUninitializedItem = new UninitializedTypeTableItem();
reusableMergedItem = new MergedTypeTableItem();
}
/**
* Adds an UTF8 string to the constant pool of the class being built. Does nothing if the constant pool already contains a similar item.
*
* @param value the String value.
* @return the index of a new or already existing UTF8 item.
*/
@Nonnegative
public int newUTF8(@Nonnull String value) {
reusableUTF8Item.set(UTF8, value);
StringItem result = get(reusableUTF8Item);
if (result == null) {
pool.putByte(UTF8).putUTF8(value);
result = new StringItem(index++, reusableUTF8Item);
put(result);
}
return result.index;
}
/**
* Adds a class reference to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param internalName the internal name of the class.
* @return the index of a new or already existing class reference item.
*/
@Nonnegative
public int newClass(@Nonnull String internalName) {
return newClassItem(internalName).index;
}
/**
* Adds a class reference to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param internalName the internal name of the class.
* @return a new or already existing class reference item.
*/
@Nonnull
public StringItem newClassItem(@Nonnull String internalName) {
return newStringItem(CLASS, internalName);
}
/**
* Adds a string to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param type one of {@link ConstantPoolTypes#STR}, {@link ConstantPoolTypes#CLASS} or {@link ConstantPoolTypes#MTYPE}
* @param value the String value.
* @return a new or already existing string item.
*/
@Nonnull
private StringItem newStringItem(int type, @Nonnull String value) {
reusableStringItem.set(type, value);
StringItem result = get(reusableStringItem);
if (result == null) {
int itemIndex = newUTF8(value);
pool.put12(type, itemIndex);
result = new StringItem(index++, reusableStringItem);
put(result);
}
return result;
}
/**
* Adds a method handle to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @return a new or an already existing method type reference item.
*/
@Nonnull
public MethodHandleItem newMethodHandleItem(@Nonnull MethodHandle methodHandle) {
reusableMethodHandleItem.set(methodHandle);
MethodHandleItem result = get(reusableMethodHandleItem);
if (result == null) {
int tag = methodHandle.tag;
int memberType = tag == MethodHandle.Tag.INVOKEINTERFACE ? IMETH : METH;
ClassMemberItem memberItem = newClassMemberItem(memberType, methodHandle.owner, methodHandle.name, methodHandle.desc);
pool.put11(HANDLE, tag).putShort(memberItem.index);
result = new MethodHandleItem(index++, reusableMethodHandleItem);
put(result);
}
return result;
}
@Nonnull
private ClassMemberItem newClassMemberItem(int type, @Nonnull String owner, @Nonnull String name, @Nonnull String desc) {
reusableClassMemberItem.set(type, owner, name, desc);
ClassMemberItem result = get(reusableClassMemberItem);
if (result == null) {
int ownerItemIndex = newClass(owner);
int nameAndTypeItemIndex = newNameType(name, desc);
put122(type, ownerItemIndex, nameAndTypeItemIndex);
result = new ClassMemberItem(index++, reusableClassMemberItem);
put(result);
}
return result;
}
/**
* Adds a field reference to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param owner the internal name of the field's owner class
* @param name the field's name
* @param desc the field's descriptor
* @return a new or already existing field reference item
*/
@Nonnull
public ClassMemberItem newFieldItem(@Nonnull String owner, @Nonnull String name, @Nonnull String desc) {
return newClassMemberItem(FIELD, owner, name, desc);
}
/**
* Adds a method reference to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param owner the internal name of the method's owner class
* @param name the method's name
* @param desc the method's descriptor
* @param itf true if owner is an interface
* @return a new or already existing method reference item
*/
@Nonnull
public ClassMemberItem newMethodItem(@Nonnull String owner, @Nonnull String name, @Nonnull String desc, boolean itf) {
return newClassMemberItem(itf ? IMETH : METH, owner, name, desc);
}
/**
* Adds an integer to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param value the int value
* @return a new or already existing int item
*/
@Nonnull
public IntItem newInteger(int value) {
reusableIntItem.setValue(value);
IntItem result = get(reusableIntItem);
if (result == null) {
pool.putByte(INT).putInt(value);
result = new IntItem(index++, reusableIntItem);
put(result);
}
return result;
}
/**
* Adds a float to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param value the float value
* @return a new or already existing float item
*/
@Nonnull
public FloatItem newFloat(float value) {
reusableFloatItem.set(value);
FloatItem result = get(reusableFloatItem);
if (result == null) {
pool.putByte(FLOAT).putInt(reusableFloatItem.intVal);
result = new FloatItem(index++, reusableFloatItem);
put(result);
}
return result;
}
/**
* Adds a long to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param value the long value
* @return a new or already existing long item
*/
@Nonnull
public LongItem newLong(long value) {
reusableLongItem.setValue(value);
LongItem result = get(reusableLongItem);
if (result == null) {
pool.putByte(LONG).putLong(value);
result = new LongItem(index, reusableLongItem);
index += 2;
put(result);
}
return result;
}
/**
* Adds a double to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param value the double value
* @return a new or already existing double item
*/
@Nonnull
public DoubleItem newDouble(double value) {
reusableDoubleItem.set(value);
DoubleItem result = get(reusableDoubleItem);
if (result == null) {
pool.putByte(DOUBLE).putLong(reusableDoubleItem.longVal);
result = new DoubleItem(index, reusableDoubleItem);
index += 2;
put(result);
}
return result;
}
/**
* Adds a name and type to the constant pool of the class being built. Does nothing if the constant pool already contains a similar item.
*
* @param name a name
* @param desc a type descriptor
* @return the index of a new or already existing name and type item
*/
@Nonnegative
private int newNameType(@Nonnull String name, @Nonnull String desc) {
reusableNameTypeItem.set(name, desc);
NameAndTypeItem result = get(reusableNameTypeItem);
if (result == null) {
int nameItemIndex = newUTF8(name);
int descItemIndex = newUTF8(desc);
put122(NAME_TYPE, nameItemIndex, descItemIndex);
result = new NameAndTypeItem(index++, reusableNameTypeItem);
put(result);
}
return result.index;
}
/**
* Adds a number or string constant to the constant pool of the class being built.
* Does nothing if the constant pool already contains a similar item.
*
* @param cst the value of the constant to be added to the constant pool, which must be an {@link Integer}, a {@link Float}, a
* {@link Long}, a {@link Double}, a {@link String}, or a {@link JavaType}
* @return a new or already existing constant item with the given value
*/
@Nonnull
public Item newConstItem(@Nonnull Object cst) {
if (cst instanceof String) {
return newStringItem(STR, (String) cst);
}
if (cst instanceof Number) {
return newNumberItem((Number) cst);
}
if (cst instanceof Character) {
return newInteger((int) (Character) cst);
}
if (cst instanceof Boolean) {
int val = (Boolean) cst ? 1 : 0;
return newInteger(val);
}
if (cst instanceof ReferenceType) {
String typeDesc = ((ReferenceType) cst).getInternalName();
return cst instanceof MethodType ? newStringItem(MTYPE, typeDesc) : newClassItem(typeDesc);
}
if (cst instanceof PrimitiveType) {
String typeDesc = ((PrimitiveType) cst).getDescriptor();
return newClassItem(typeDesc);
}
if (cst instanceof MethodHandle) {
return newMethodHandleItem((MethodHandle) cst);
}
throw new IllegalArgumentException("value " + cst);
}
@Nonnull
private Item newNumberItem(@Nonnull Number cst) {
if (cst instanceof Float) {
return newFloat(cst.floatValue());
}
if (cst instanceof Long) {
return newLong(cst.longValue());
}
if (cst instanceof Double) {
return newDouble(cst.doubleValue());
}
return newInteger(cst.intValue());
}
/**
* Adds the given internal name to {@link #typeTable} and returns its index.
* Does nothing if the type table already contains this internal name.
*
* @param type the internal name to be added to the type table
* @return the index of this internal name in the type table
*/
@Nonnegative
public int addNormalType(@Nonnull String type) {
reusableNormalItem.set(type);
TypeTableItem result = get(reusableNormalItem);
if (result == null) {
result = new NormalTypeTableItem(++typeCount, reusableNormalItem);
addToTypeTable(result);
}
return result.index;
}
/**
* Adds the given "uninitialized" type to {@link #typeTable} and returns its index.
* This method is used for UNINITIALIZED types, made of an internal name and a bytecode offset.
*
* @param type the internal name to be added to the type table
* @param offset the bytecode offset of the NEW instruction that created this UNINITIALIZED type value
* @return the index of this internal name in the type table
*/
@Nonnegative
public int addUninitializedType(@Nonnull String type, @Nonnegative int offset) {
reusableUninitializedItem.set(type, offset);
TypeTableItem result = get(reusableUninitializedItem);
if (result == null) {
result = new UninitializedTypeTableItem(++typeCount, reusableUninitializedItem);
addToTypeTable(result);
}
return result.index;
}
private void addToTypeTable(@Nonnull TypeTableItem newItem) {
put(newItem);
if (typeTable == null) {
typeTable = new TypeTableItem[16];
}
int newItemIndex = typeCount;
enlargeTypeTableIfNeeded(newItemIndex);
typeTable[newItemIndex] = newItem;
}
private void enlargeTypeTableIfNeeded(@Nonnegative int newItemIndex) {
int currentTypeCount = typeTable.length;
if (newItemIndex == currentTypeCount) {
TypeTableItem[] newTable = new TypeTableItem[2 * currentTypeCount];
System.arraycopy(typeTable, 0, newTable, 0, currentTypeCount);
typeTable = newTable;
}
}
/**
* Returns the index of the common super type of the two given types. This method calls {@link #getCommonSuperClass} and caches the
* result in the {@link #items} hash table to speedup future calls with the same parameters.
*
* @param type1 index of an internal name in {@link #typeTable}
* @param type2 index of an internal name in {@link #typeTable}
* @return the index of the common super type of the two given types
*/
@Nonnegative
public int getMergedType(@Nonnegative int type1, @Nonnegative int type2) {
reusableMergedItem.set(type1, type2);
MergedTypeTableItem result = get(reusableMergedItem);
if (result == null) {
String type1Desc = getInternalName(type1);
String type2Desc = getInternalName(type2);
String commonSuperClass = getCommonSuperClass(type1Desc, type2Desc);
reusableMergedItem.commonSuperTypeIndex = addNormalType(commonSuperClass);
result = new MergedTypeTableItem(reusableMergedItem);
put(result);
}
return result.commonSuperTypeIndex;
}
/**
* Returns the common super type of the two given types. The default implementation of this method loads the two given classes and
* uses the java.lang.Class methods to find the common super class. It can be overridden to compute this common super type in other ways,
* in particular without actually loading any class, or to take into account the class that is currently being generated by this
* ClassWriter, which can of course not be loaded since it is under construction.
*
* @param type1 the internal name of a class
* @param type2 the internal name of another class
* @return the internal name of the common super class of the two given classes
*/
@Nonnull
private static String getCommonSuperClass(@Nonnull String type1, @Nonnull String type2) {
// Reimplemented to avoid "duplicate class definition" errors.
String class1 = type1;
String class2 = type2;
while (true) {
if (OBJECT.equals(class1) || OBJECT.equals(class2)) {
return OBJECT;
}
String superClass = ClassLoad.whichIsSuperClass(class1, class2);
if (superClass != null) {
return superClass;
}
class1 = ClassLoad.getSuperClass(class1);
class2 = ClassLoad.getSuperClass(class2);
if (class1.equals(class2)) {
return class1;
}
}
}
@Nonnull
public String getInternalName(@Nonnegative int typeTableIndex) {
TypeTableItem typeTableItem = typeTable[typeTableIndex]; // Normal or Uninitialized
return typeTableItem.typeDesc;
}
@Nonnull
public UninitializedTypeTableItem getUninitializedItemValue(@Nonnegative int typeTableIndex) {
return (UninitializedTypeTableItem) typeTable[typeTableIndex];
}
@Nullable
public Item getItem(@Nonnegative int itemHashCode) { return items[itemHashCode % items.length]; }
/**
* Returns the constant pool's hash table item which is equal to the given item.
*
* @param key a constant pool item
* @return the constant pool's hash table item which is equal to the given item, or null if there is no such item
*/
@Nullable
private I get(@Nonnull I key) {
Item item = getItem(key.getHashCode());
int keyType = key.getType();
while (item != null && (item.getType() != keyType || !key.isEqualTo(item))) {
item = item.getNext();
}
//noinspection unchecked
return (I) item;
}
/**
* Puts the given item in the constant pool's hash table. The hash table must not already contains this item.
*
* @param item the item to be added to the constant pool's hash table
*/
private void put(@Nonnull Item item) {
resizeItemArrayIfNeeded();
item.setNext(items);
}
private void resizeItemArrayIfNeeded() {
if (index + typeCount > threshold) {
int ll = items.length;
int nl = ll * 2 + 1;
Item[] newItems = new Item[nl];
for (int l = ll - 1; l >= 0; l--) {
Item j = items[l];
put(newItems, j);
}
items = newItems;
//noinspection NumericCastThatLosesPrecision
threshold = (int) (nl * 0.75);
}
}
private static void put(@Nonnull Item[] newItems, @Nullable Item item) {
while (item != null) {
Item next = item.getNext();
item.setNext(newItems);
//noinspection AssignmentToMethodParameter
item = next;
}
}
/**
* Puts one byte and two shorts into the constant pool.
*/
private void put122(int b, int s1, int s2) {
pool.put12(b, s1).putShort(s2);
}
@Nonnegative
public int getSize() { return pool.getLength(); }
public void checkConstantPoolMaxSize() {
if (index > 0xFFFF) {
throw new RuntimeException("Class file too large!");
}
}
public void put(@Nonnull ByteVector out) {
out.putShort(index).putByteVector(pool);
}
public void copy(@Nonnull byte[] code, @Nonnegative int off, @Nonnegative int header, @Nonnull Item[] cpItems) {
pool.putByteArray(code, off, header - off);
items = cpItems;
int ll = cpItems.length;
//noinspection NumericCastThatLosesPrecision
threshold = (int) (0.75d * ll);
index = ll;
}
@Nonnull
public DynamicItem createDynamicItem(int type, @Nonnull String name, @Nonnull String desc, @Nonnegative int bsmIndex) {
reusableDynamicItem.set(type, name, desc, bsmIndex);
DynamicItem result = get(reusableDynamicItem);
if (result == null) {
int nameAndTypeItemIndex = newNameType(name, desc);
put122(type, bsmIndex, nameAndTypeItemIndex);
result = new DynamicItem(index++, reusableDynamicItem);
put(result);
}
return result;
}
}