Java tutorial
// ASM: a very small and fast Java bytecode manipulation framework // Copyright (c) 2000-2011 INRIA, France Telecom // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. Neither the name of the copyright holders nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. package org.objectweb.asm; /** * The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type * table entries of a class. * * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVMS * 4.4</a> * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS * 4.7.23</a> * @author Eric Bruneton */ final class SymbolTable { /** * The ClassWriter to which this SymbolTable belongs. This is only used to get access to {@link * ClassWriter#getCommonSuperClass} and to serialize custom attributes with {@link * Attribute#write}. */ final ClassWriter classWriter; /** * The ClassReader from which this SymbolTable was constructed, or {@literal null} if it was * constructed from scratch. */ private final ClassReader sourceClassReader; /** The major version number of the class to which this symbol table belongs. */ private int majorVersion; /** The internal name of the class to which this symbol table belongs. */ private String className; /** * The total number of {@link Entry} instances in {@link #entries}. This includes entries that are * accessible (recursively) via {@link Entry#next}. */ private int entryCount; /** * A hash set of all the entries in this SymbolTable (this includes the constant pool entries, the * bootstrap method entries and the type table entries). Each {@link Entry} instance is stored at * the array index given by its hash code modulo the array size. If several entries must be stored * at the same array index, they are linked together via their {@link Entry#next} field. The * factory methods of this class make sure that this table does not contain duplicated entries. */ private Entry[] entries; /** * The number of constant pool items in {@link #constantPool}, plus 1. The first constant pool * item has index 1, and long and double items count for two items. */ private int constantPoolCount; /** * The content of the ClassFile's constant_pool JVMS structure corresponding to this SymbolTable. * The ClassFile's constant_pool_count field is <i>not</i> included. */ private ByteVector constantPool; /** * The number of bootstrap methods in {@link #bootstrapMethods}. Corresponds to the * BootstrapMethods_attribute's num_bootstrap_methods field value. */ private int bootstrapMethodCount; /** * The content of the BootstrapMethods attribute 'bootstrap_methods' array corresponding to this * SymbolTable. Note that the first 6 bytes of the BootstrapMethods_attribute, and its * num_bootstrap_methods field, are <i>not</i> included. */ private ByteVector bootstrapMethods; /** * The actual number of elements in {@link #typeTable}. These elements are stored from index 0 to * typeCount (excluded). The other array entries are empty. */ private int typeCount; /** * An ASM specific 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 used to compute stack map frames from scratch. This array stores {@link * Symbol#TYPE_TAG} and {@link Symbol#UNINITIALIZED_TYPE_TAG}) Symbol. The type symbol at index * {@code i} has its {@link Symbol#index} equal to {@code i} (and vice versa). */ private Entry[] typeTable; /** * Constructs a new, empty SymbolTable for the given ClassWriter. * * @param classWriter a ClassWriter. */ SymbolTable(final ClassWriter classWriter) { this.classWriter = classWriter; this.sourceClassReader = null; this.entries = new Entry[256]; this.constantPoolCount = 1; this.constantPool = new ByteVector(); } /** * Constructs a new SymbolTable for the given ClassWriter, initialized with the constant pool and * bootstrap methods of the given ClassReader. * * @param classWriter a ClassWriter. * @param classReader the ClassReader whose constant pool and bootstrap methods must be copied to * initialize the SymbolTable. */ SymbolTable(final ClassWriter classWriter, final ClassReader classReader) { this.classWriter = classWriter; this.sourceClassReader = classReader; // Copy the constant pool binary content. byte[] inputBytes = classReader.classFileBuffer; int constantPoolOffset = classReader.getItem(1) - 1; int constantPoolLength = classReader.header - constantPoolOffset; constantPoolCount = classReader.getItemCount(); constantPool = new ByteVector(constantPoolLength); constantPool.putByteArray(inputBytes, constantPoolOffset, constantPoolLength); // Add the constant pool items in the symbol table entries. Reserve enough space in 'entries' to // avoid too many hash set collisions (entries is not dynamically resized by the addConstant* // method calls below), and to account for bootstrap method entries. entries = new Entry[constantPoolCount * 2]; char[] charBuffer = new char[classReader.getMaxStringLength()]; boolean hasBootstrapMethods = false; int itemIndex = 1; while (itemIndex < constantPoolCount) { int itemOffset = classReader.getItem(itemIndex); int itemTag = inputBytes[itemOffset - 1]; int nameAndTypeItemOffset; switch (itemTag) { case Symbol.CONSTANT_FIELDREF_TAG: case Symbol.CONSTANT_METHODREF_TAG: case Symbol.CONSTANT_INTERFACE_METHODREF_TAG: nameAndTypeItemOffset = classReader.getItem(classReader.readUnsignedShort(itemOffset + 2)); addConstantMemberReference(itemIndex, itemTag, classReader.readClass(itemOffset, charBuffer), classReader.readUTF8(nameAndTypeItemOffset, charBuffer), classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer)); break; case Symbol.CONSTANT_INTEGER_TAG: case Symbol.CONSTANT_FLOAT_TAG: addConstantIntegerOrFloat(itemIndex, itemTag, classReader.readInt(itemOffset)); break; case Symbol.CONSTANT_NAME_AND_TYPE_TAG: addConstantNameAndType(itemIndex, classReader.readUTF8(itemOffset, charBuffer), classReader.readUTF8(itemOffset + 2, charBuffer)); break; case Symbol.CONSTANT_LONG_TAG: case Symbol.CONSTANT_DOUBLE_TAG: addConstantLongOrDouble(itemIndex, itemTag, classReader.readLong(itemOffset)); break; case Symbol.CONSTANT_UTF8_TAG: addConstantUtf8(itemIndex, classReader.readUtf(itemIndex, charBuffer)); break; case Symbol.CONSTANT_METHOD_HANDLE_TAG: int memberRefItemOffset = classReader.getItem(classReader.readUnsignedShort(itemOffset + 1)); nameAndTypeItemOffset = classReader.getItem(classReader.readUnsignedShort(memberRefItemOffset + 2)); addConstantMethodHandle(itemIndex, classReader.readByte(itemOffset), classReader.readClass(memberRefItemOffset, charBuffer), classReader.readUTF8(nameAndTypeItemOffset, charBuffer), classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer)); break; case Symbol.CONSTANT_DYNAMIC_TAG: case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG: hasBootstrapMethods = true; nameAndTypeItemOffset = classReader.getItem(classReader.readUnsignedShort(itemOffset + 2)); addConstantDynamicOrInvokeDynamicReference(itemTag, itemIndex, classReader.readUTF8(nameAndTypeItemOffset, charBuffer), classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer), classReader.readUnsignedShort(itemOffset)); break; case Symbol.CONSTANT_STRING_TAG: case Symbol.CONSTANT_CLASS_TAG: case Symbol.CONSTANT_METHOD_TYPE_TAG: case Symbol.CONSTANT_MODULE_TAG: case Symbol.CONSTANT_PACKAGE_TAG: addConstantUtf8Reference(itemIndex, itemTag, classReader.readUTF8(itemOffset, charBuffer)); break; default: throw new IllegalArgumentException(); } itemIndex += (itemTag == Symbol.CONSTANT_LONG_TAG || itemTag == Symbol.CONSTANT_DOUBLE_TAG) ? 2 : 1; } // Copy the BootstrapMethods, if any. if (hasBootstrapMethods) { copyBootstrapMethods(classReader, charBuffer); } } /** * Read the BootstrapMethods 'bootstrap_methods' array binary content and add them as entries of * the SymbolTable. * * @param classReader the ClassReader whose bootstrap methods must be copied to initialize the * SymbolTable. * @param charBuffer a buffer used to read strings in the constant pool. */ private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) { // Find attributOffset of the 'bootstrap_methods' array. byte[] inputBytes = classReader.classFileBuffer; int currentAttributeOffset = classReader.getFirstAttributeOffset(); for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) { String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer); if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) { bootstrapMethodCount = classReader.readUnsignedShort(currentAttributeOffset + 6); break; } currentAttributeOffset += 6 + classReader.readInt(currentAttributeOffset + 2); } if (bootstrapMethodCount > 0) { // Compute the offset and the length of the BootstrapMethods 'bootstrap_methods' array. int bootstrapMethodsOffset = currentAttributeOffset + 8; int bootstrapMethodsLength = classReader.readInt(currentAttributeOffset + 2) - 2; bootstrapMethods = new ByteVector(bootstrapMethodsLength); bootstrapMethods.putByteArray(inputBytes, bootstrapMethodsOffset, bootstrapMethodsLength); // Add each bootstrap method in the symbol table entries. int currentOffset = bootstrapMethodsOffset; for (int i = 0; i < bootstrapMethodCount; i++) { int offset = currentOffset - bootstrapMethodsOffset; int bootstrapMethodRef = classReader.readUnsignedShort(currentOffset); currentOffset += 2; int numBootstrapArguments = classReader.readUnsignedShort(currentOffset); currentOffset += 2; int hashCode = classReader.readConst(bootstrapMethodRef, charBuffer).hashCode(); while (numBootstrapArguments-- > 0) { int bootstrapArgument = classReader.readUnsignedShort(currentOffset); currentOffset += 2; hashCode ^= classReader.readConst(bootstrapArgument, charBuffer).hashCode(); } add(new Entry(i, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode & 0x7FFFFFFF)); } } } /** * Returns the ClassReader from which this SymbolTable was constructed. * * @return the ClassReader from which this SymbolTable was constructed, or {@literal null} if it * was constructed from scratch. */ ClassReader getSource() { return sourceClassReader; } /** * Returns the major version of the class to which this symbol table belongs. * * @return the major version of the class to which this symbol table belongs. */ int getMajorVersion() { return majorVersion; } /** * Returns the internal name of the class to which this symbol table belongs. * * @return the internal name of the class to which this symbol table belongs. */ String getClassName() { return className; } /** * Sets the major version and the name of the class to which this symbol table belongs. Also adds * the class name to the constant pool. * * @param majorVersion a major ClassFile version number. * @param className an internal class name. * @return the constant pool index of a new or already existing Symbol with the given class name. */ int setMajorVersionAndClassName(final int majorVersion, final String className) { this.majorVersion = majorVersion; this.className = className; return addConstantClass(className).index; } /** * Returns the number of items in this symbol table's constant_pool array (plus 1). * * @return the number of items in this symbol table's constant_pool array (plus 1). */ int getConstantPoolCount() { return constantPoolCount; } /** * Returns the length in bytes of this symbol table's constant_pool array. * * @return the length in bytes of this symbol table's constant_pool array. */ int getConstantPoolLength() { return constantPool.length; } /** * Puts this symbol table's constant_pool array in the given ByteVector, preceded by the * constant_pool_count value. * * @param output where the JVMS ClassFile's constant_pool array must be put. */ void putConstantPool(final ByteVector output) { output.putShort(constantPoolCount).putByteArray(constantPool.data, 0, constantPool.length); } /** * Returns the size in bytes of this symbol table's BootstrapMethods attribute. Also adds the * attribute name in the constant pool. * * @return the size in bytes of this symbol table's BootstrapMethods attribute. */ int computeBootstrapMethodsSize() { if (bootstrapMethods != null) { addConstantUtf8(Constants.BOOTSTRAP_METHODS); return 8 + bootstrapMethods.length; } else { return 0; } } /** * Puts this symbol table's BootstrapMethods attribute in the given ByteVector. This includes the * 6 attribute header bytes and the num_bootstrap_methods value. * * @param output where the JVMS BootstrapMethods attribute must be put. */ void putBootstrapMethods(final ByteVector output) { if (bootstrapMethods != null) { output.putShort(addConstantUtf8(Constants.BOOTSTRAP_METHODS)).putInt(bootstrapMethods.length + 2) .putShort(bootstrapMethodCount).putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); } } // ----------------------------------------------------------------------------------------------- // Generic symbol table entries management. // ----------------------------------------------------------------------------------------------- /** * Returns the list of entries which can potentially have the given hash code. * * @param hashCode a {@link Entry#hashCode} value. * @return the list of entries which can potentially have the given hash code. The list is stored * via the {@link Entry#next} field. */ private Entry get(final int hashCode) { return entries[hashCode % entries.length]; } /** * Puts the given entry in the {@link #entries} hash set. This method does <i>not</i> check * whether {@link #entries} already contains a similar entry or not. {@link #entries} is resized * if necessary to avoid hash collisions (multiple entries needing to be stored at the same {@link * #entries} array index) as much as possible, with reasonable memory usage. * * @param entry an Entry (which must not already be contained in {@link #entries}). * @return the given entry */ private Entry put(final Entry entry) { if (entryCount > (entries.length * 3) / 4) { int currentCapacity = entries.length; int newCapacity = currentCapacity * 2 + 1; Entry[] newEntries = new Entry[newCapacity]; for (int i = currentCapacity - 1; i >= 0; --i) { Entry currentEntry = entries[i]; while (currentEntry != null) { int newCurrentEntryIndex = currentEntry.hashCode % newCapacity; Entry nextEntry = currentEntry.next; currentEntry.next = newEntries[newCurrentEntryIndex]; newEntries[newCurrentEntryIndex] = currentEntry; currentEntry = nextEntry; } } entries = newEntries; } entryCount++; int index = entry.hashCode % entries.length; entry.next = entries[index]; return entries[index] = entry; } /** * Adds the given entry in the {@link #entries} hash set. This method does <i>not</i> check * whether {@link #entries} already contains a similar entry or not, and does <i>not</i> resize * {@link #entries} if necessary. * * @param entry an Entry (which must not already be contained in {@link #entries}). */ private void add(final Entry entry) { entryCount++; int index = entry.hashCode % entries.length; entry.next = entries[index]; entries[index] = entry; } // ----------------------------------------------------------------------------------------------- // Constant pool entries management. // ----------------------------------------------------------------------------------------------- /** * Adds a number or string constant to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value the value of the constant to be added to the constant pool. This parameter must be * an {@link Integer}, {@link Byte}, {@link Character}, {@link Short}, {@link Boolean}, {@link * Float}, {@link Long}, {@link Double}, {@link String}, {@link Type} or {@link Handle}. * @return a new or already existing Symbol with the given value. */ Symbol addConstant(final Object value) { if (value instanceof Integer) { return addConstantInteger(((Integer) value).intValue()); } else if (value instanceof Byte) { return addConstantInteger(((Byte) value).intValue()); } else if (value instanceof Character) { return addConstantInteger(((Character) value).charValue()); } else if (value instanceof Short) { return addConstantInteger(((Short) value).intValue()); } else if (value instanceof Boolean) { return addConstantInteger(((Boolean) value).booleanValue() ? 1 : 0); } else if (value instanceof Float) { return addConstantFloat(((Float) value).floatValue()); } else if (value instanceof Long) { return addConstantLong(((Long) value).longValue()); } else if (value instanceof Double) { return addConstantDouble(((Double) value).doubleValue()); } else if (value instanceof String) { return addConstantString((String) value); } else if (value instanceof Type) { Type type = (Type) value; int typeSort = type.getSort(); if (typeSort == Type.OBJECT) { return addConstantClass(type.getInternalName()); } else if (typeSort == Type.METHOD) { return addConstantMethodType(type.getDescriptor()); } else { // type is a primitive or array type. return addConstantClass(type.getDescriptor()); } } else if (value instanceof Handle) { Handle handle = (Handle) value; return addConstantMethodHandle(handle.getTag(), handle.getOwner(), handle.getName(), handle.getDesc(), handle.isInterface()); } else if (value instanceof ConstantDynamic) { ConstantDynamic constantDynamic = (ConstantDynamic) value; return addConstantDynamic(constantDynamic.getName(), constantDynamic.getDescriptor(), constantDynamic.getBootstrapMethod(), constantDynamic.getBootstrapMethodArgumentsUnsafe()); } else { throw new IllegalArgumentException("value " + value); } } /** * Adds a CONSTANT_Class_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value the internal name of a class. * @return a new or already existing Symbol with the given value. */ Symbol addConstantClass(final String value) { return addConstantUtf8Reference(Symbol.CONSTANT_CLASS_TAG, value); } /** * Adds a CONSTANT_Fieldref_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param owner the internal name of a class. * @param name a field name. * @param descriptor a field descriptor. * @return a new or already existing Symbol with the given value. */ Symbol addConstantFieldref(final String owner, final String name, final String descriptor) { return addConstantMemberReference(Symbol.CONSTANT_FIELDREF_TAG, owner, name, descriptor); } /** * Adds a CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to the constant pool of this * symbol table. Does nothing if the constant pool already contains a similar item. * * @param owner the internal name of a class. * @param name a method name. * @param descriptor a method descriptor. * @param isInterface whether owner is an interface or not. * @return a new or already existing Symbol with the given value. */ Symbol addConstantMethodref(final String owner, final String name, final String descriptor, final boolean isInterface) { int tag = isInterface ? Symbol.CONSTANT_INTERFACE_METHODREF_TAG : Symbol.CONSTANT_METHODREF_TAG; return addConstantMemberReference(tag, owner, name, descriptor); } /** * Adds a CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to * the constant pool of this symbol table. Does nothing if the constant pool already contains a * similar item. * * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG} * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}. * @param owner the internal name of a class. * @param name a field or method name. * @param descriptor a field or method descriptor. * @return a new or already existing Symbol with the given value. */ private Entry addConstantMemberReference(final int tag, final String owner, final String name, final String descriptor) { int hashCode = hash(tag, owner, name, descriptor); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.owner.equals(owner) && entry.name.equals(name) && entry.value.equals(descriptor)) { return entry; } entry = entry.next; } constantPool.put122(tag, addConstantClass(owner).index, addConstantNameAndType(name, descriptor)); return put(new Entry(constantPoolCount++, tag, owner, name, descriptor, 0, hashCode)); } /** * Adds a new CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info * to the constant pool of this symbol table. * * @param index the constant pool index of the new Symbol. * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG} * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}. * @param owner the internal name of a class. * @param name a field or method name. * @param descriptor a field or method descriptor. */ private void addConstantMemberReference(final int index, final int tag, final String owner, final String name, final String descriptor) { add(new Entry(index, tag, owner, name, descriptor, 0, hash(tag, owner, name, descriptor))); } /** * Adds a CONSTANT_String_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value a string. * @return a new or already existing Symbol with the given value. */ Symbol addConstantString(final String value) { return addConstantUtf8Reference(Symbol.CONSTANT_STRING_TAG, value); } /** * Adds a CONSTANT_Integer_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value an int. * @return a new or already existing Symbol with the given value. */ Symbol addConstantInteger(final int value) { return addConstantIntegerOrFloat(Symbol.CONSTANT_INTEGER_TAG, value); } /** * Adds a CONSTANT_Float_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value a float. * @return a new or already existing Symbol with the given value. */ Symbol addConstantFloat(final float value) { return addConstantIntegerOrFloat(Symbol.CONSTANT_FLOAT_TAG, Float.floatToRawIntBits(value)); } /** * Adds a CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol table. * Does nothing if the constant pool already contains a similar item. * * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. * @param value an int or float. * @return a constant pool constant with the given tag and primitive values. */ private Symbol addConstantIntegerOrFloat(final int tag, final int value) { int hashCode = hash(tag, value); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { return entry; } entry = entry.next; } constantPool.putByte(tag).putInt(value); return put(new Entry(constantPoolCount++, tag, value, hashCode)); } /** * Adds a new CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol * table. * * @param index the constant pool index of the new Symbol. * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. * @param value an int or float. */ private void addConstantIntegerOrFloat(final int index, final int tag, final int value) { add(new Entry(index, tag, value, hash(tag, value))); } /** * Adds a CONSTANT_Long_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value a long. * @return a new or already existing Symbol with the given value. */ Symbol addConstantLong(final long value) { return addConstantLongOrDouble(Symbol.CONSTANT_LONG_TAG, value); } /** * Adds a CONSTANT_Double_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value a double. * @return a new or already existing Symbol with the given value. */ Symbol addConstantDouble(final double value) { return addConstantLongOrDouble(Symbol.CONSTANT_DOUBLE_TAG, Double.doubleToRawLongBits(value)); } /** * Adds a CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol table. * Does nothing if the constant pool already contains a similar item. * * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}. * @param value a long or double. * @return a constant pool constant with the given tag and primitive values. */ private Symbol addConstantLongOrDouble(final int tag, final long value) { int hashCode = hash(tag, value); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { return entry; } entry = entry.next; } int index = constantPoolCount; constantPool.putByte(tag).putLong(value); constantPoolCount += 2; return put(new Entry(index, tag, value, hashCode)); } /** * Adds a new CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol * table. * * @param index the constant pool index of the new Symbol. * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}. * @param value a long or double. */ private void addConstantLongOrDouble(final int index, final int tag, final long value) { add(new Entry(index, tag, value, hash(tag, value))); } /** * Adds a CONSTANT_NameAndType_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param name a field or method name. * @param descriptor a field or method descriptor. * @return a new or already existing Symbol with the given value. */ int addConstantNameAndType(final String name, final String descriptor) { final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG; int hashCode = hash(tag, name, descriptor); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.name.equals(name) && entry.value.equals(descriptor)) { return entry.index; } entry = entry.next; } constantPool.put122(tag, addConstantUtf8(name), addConstantUtf8(descriptor)); return put(new Entry(constantPoolCount++, tag, name, descriptor, hashCode)).index; } /** * Adds a new CONSTANT_NameAndType_info to the constant pool of this symbol table. * * @param index the constant pool index of the new Symbol. * @param name a field or method name. * @param descriptor a field or method descriptor. */ private void addConstantNameAndType(final int index, final String name, final String descriptor) { final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG; add(new Entry(index, tag, name, descriptor, hash(tag, name, descriptor))); } /** * Adds a CONSTANT_Utf8_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param value a string. * @return a new or already existing Symbol with the given value. */ int addConstantUtf8(final String value) { int hashCode = hash(Symbol.CONSTANT_UTF8_TAG, value); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == Symbol.CONSTANT_UTF8_TAG && entry.hashCode == hashCode && entry.value.equals(value)) { return entry.index; } entry = entry.next; } constantPool.putByte(Symbol.CONSTANT_UTF8_TAG).putUTF8(value); return put(new Entry(constantPoolCount++, Symbol.CONSTANT_UTF8_TAG, value, hashCode)).index; } /** * Adds a new CONSTANT_String_info to the constant pool of this symbol table. * * @param index the constant pool index of the new Symbol. * @param value a string. */ private void addConstantUtf8(final int index, final String value) { add(new Entry(index, Symbol.CONSTANT_UTF8_TAG, value, hash(Symbol.CONSTANT_UTF8_TAG, value))); } /** * Adds a CONSTANT_MethodHandle_info to the constant pool of this symbol table. Does nothing if * the constant pool already contains a similar item. * * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link * Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. * @param owner the internal name of a class of interface. * @param name a field or method name. * @param descriptor a field or method descriptor. * @param isInterface whether owner is an interface or not. * @return a new or already existing Symbol with the given value. */ Symbol addConstantMethodHandle(final int referenceKind, final String owner, final String name, final String descriptor, final boolean isInterface) { final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG; // Note that we don't need to include isInterface in the hash computation, because it is // redundant with owner (we can't have the same owner with different isInterface values). int hashCode = hash(tag, owner, name, descriptor, referenceKind); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.data == referenceKind && entry.owner.equals(owner) && entry.name.equals(name) && entry.value.equals(descriptor)) { return entry; } entry = entry.next; } if (referenceKind <= Opcodes.H_PUTSTATIC) { constantPool.put112(tag, referenceKind, addConstantFieldref(owner, name, descriptor).index); } else { constantPool.put112(tag, referenceKind, addConstantMethodref(owner, name, descriptor, isInterface).index); } return put(new Entry(constantPoolCount++, tag, owner, name, descriptor, referenceKind, hashCode)); } /** * Adds a new CONSTANT_MethodHandle_info to the constant pool of this symbol table. * * @param index the constant pool index of the new Symbol. * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link * Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. * @param owner the internal name of a class of interface. * @param name a field or method name. * @param descriptor a field or method descriptor. */ private void addConstantMethodHandle(final int index, final int referenceKind, final String owner, final String name, final String descriptor) { final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG; int hashCode = hash(tag, owner, name, descriptor, referenceKind); add(new Entry(index, tag, owner, name, descriptor, referenceKind, hashCode)); } /** * Adds a CONSTANT_MethodType_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param methodDescriptor a method descriptor. * @return a new or already existing Symbol with the given value. */ Symbol addConstantMethodType(final String methodDescriptor) { return addConstantUtf8Reference(Symbol.CONSTANT_METHOD_TYPE_TAG, methodDescriptor); } /** * Adds a CONSTANT_Dynamic_info to the constant pool of this symbol table. Also adds the related * bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the constant * pool already contains a similar item. * * @param name a method name. * @param descriptor a field descriptor. * @param bootstrapMethodHandle a bootstrap method handle. * @param bootstrapMethodArguments the bootstrap method arguments. * @return a new or already existing Symbol with the given value. */ Symbol addConstantDynamic(final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments); return addConstantDynamicOrInvokeDynamicReference(Symbol.CONSTANT_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index); } /** * Adds a CONSTANT_InvokeDynamic_info to the constant pool of this symbol table. Also adds the * related bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param name a method name. * @param descriptor a method descriptor. * @param bootstrapMethodHandle a bootstrap method handle. * @param bootstrapMethodArguments the bootstrap method arguments. * @return a new or already existing Symbol with the given value. */ Symbol addConstantInvokeDynamic(final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments); return addConstantDynamicOrInvokeDynamicReference(Symbol.CONSTANT_INVOKE_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index); } /** * Adds a CONSTANT_Dynamic or a CONSTANT_InvokeDynamic_info to the constant pool of this symbol * table. Does nothing if the constant pool already contains a similar item. * * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}. * @param name a method name. * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG) or a method descriptor for * CONSTANT_INVOKE_DYNAMIC_TAG. * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute. * @return a new or already existing Symbol with the given value. */ private Symbol addConstantDynamicOrInvokeDynamicReference(final int tag, final String name, final String descriptor, final int bootstrapMethodIndex) { int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.data == bootstrapMethodIndex && entry.name.equals(name) && entry.value.equals(descriptor)) { return entry; } entry = entry.next; } constantPool.put122(tag, bootstrapMethodIndex, addConstantNameAndType(name, descriptor)); return put(new Entry(constantPoolCount++, tag, null, name, descriptor, bootstrapMethodIndex, hashCode)); } /** * Adds a new CONSTANT_Dynamic_info or CONSTANT_InvokeDynamic_info to the constant pool of this * symbol table. * * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}. * @param index the constant pool index of the new Symbol. * @param name a method name. * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG or a method descriptor for * CONSTANT_INVOKE_DYNAMIC_TAG. * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute. */ private void addConstantDynamicOrInvokeDynamicReference(final int tag, final int index, final String name, final String descriptor, final int bootstrapMethodIndex) { int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex); add(new Entry(index, tag, null, name, descriptor, bootstrapMethodIndex, hashCode)); } /** * Adds a CONSTANT_Module_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param moduleName a fully qualified name (using dots) of a module. * @return a new or already existing Symbol with the given value. */ Symbol addConstantModule(final String moduleName) { return addConstantUtf8Reference(Symbol.CONSTANT_MODULE_TAG, moduleName); } /** * Adds a CONSTANT_Package_info to the constant pool of this symbol table. Does nothing if the * constant pool already contains a similar item. * * @param packageName the internal name of a package. * @return a new or already existing Symbol with the given value. */ Symbol addConstantPackage(final String packageName) { return addConstantUtf8Reference(Symbol.CONSTANT_PACKAGE_TAG, packageName); } /** * Adds a CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info, * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. Does * nothing if the constant pool already contains a similar item. * * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link * Symbol#CONSTANT_PACKAGE_TAG}. * @param value an internal class name, an arbitrary string, a method descriptor, a module or a * package name, depending on tag. * @return a new or already existing Symbol with the given value. */ private Symbol addConstantUtf8Reference(final int tag, final String value) { int hashCode = hash(tag, value); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.value.equals(value)) { return entry; } entry = entry.next; } constantPool.put12(tag, addConstantUtf8(value)); return put(new Entry(constantPoolCount++, tag, value, hashCode)); } /** * Adds a new CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info, * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. * * @param index the constant pool index of the new Symbol. * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link * Symbol#CONSTANT_PACKAGE_TAG}. * @param value an internal class name, an arbitrary string, a method descriptor, a module or a * package name, depending on tag. */ private void addConstantUtf8Reference(final int index, final int tag, final String value) { add(new Entry(index, tag, value, hash(tag, value))); } // ----------------------------------------------------------------------------------------------- // Bootstrap method entries management. // ----------------------------------------------------------------------------------------------- /** * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if * the BootstrapMethods already contains a similar bootstrap method. * * @param bootstrapMethodHandle a bootstrap method handle. * @param bootstrapMethodArguments the bootstrap method arguments. * @return a new or already existing Symbol with the given value. */ Symbol addBootstrapMethod(final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { ByteVector bootstrapMethodsAttribute = bootstrapMethods; if (bootstrapMethodsAttribute == null) { bootstrapMethodsAttribute = bootstrapMethods = new ByteVector(); } // The bootstrap method arguments can be Constant_Dynamic values, which reference other // bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool // and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified // while adding the given bootstrap method to it, in the rest of this method. for (Object bootstrapMethodArgument : bootstrapMethodArguments) { addConstant(bootstrapMethodArgument); } // Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to // compare it with existing ones, and will be reverted below if there is already a similar // bootstrap method. int bootstrapMethodOffset = bootstrapMethodsAttribute.length; bootstrapMethodsAttribute.putShort(addConstantMethodHandle(bootstrapMethodHandle.getTag(), bootstrapMethodHandle.getOwner(), bootstrapMethodHandle.getName(), bootstrapMethodHandle.getDesc(), bootstrapMethodHandle.isInterface()).index); int numBootstrapArguments = bootstrapMethodArguments.length; bootstrapMethodsAttribute.putShort(numBootstrapArguments); for (Object bootstrapMethodArgument : bootstrapMethodArguments) { bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index); } // Compute the length and the hash code of the bootstrap method. int bootstrapMethodlength = bootstrapMethodsAttribute.length - bootstrapMethodOffset; int hashCode = bootstrapMethodHandle.hashCode(); for (Object bootstrapMethodArgument : bootstrapMethodArguments) { hashCode ^= bootstrapMethodArgument.hashCode(); } hashCode &= 0x7FFFFFFF; // Add the bootstrap method to the symbol table or revert the above changes. return addBootstrapMethod(bootstrapMethodOffset, bootstrapMethodlength, hashCode); } /** * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if * the BootstrapMethods already contains a similar bootstrap method (more precisely, reverts the * content of {@link #bootstrapMethods} to remove the last, duplicate bootstrap method). * * @param offset the offset of the last bootstrap method in {@link #bootstrapMethods}, in bytes. * @param length the length of this bootstrap method in {@link #bootstrapMethods}, in bytes. * @param hashCode the hash code of this bootstrap method. * @return a new or already existing Symbol with the given value. */ private Symbol addBootstrapMethod(final int offset, final int length, final int hashCode) { final byte[] bootstrapMethodsData = bootstrapMethods.data; Entry entry = get(hashCode); while (entry != null) { if (entry.tag == Symbol.BOOTSTRAP_METHOD_TAG && entry.hashCode == hashCode) { int otherOffset = (int) entry.data; boolean isSameBootstrapMethod = true; for (int i = 0; i < length; ++i) { if (bootstrapMethodsData[offset + i] != bootstrapMethodsData[otherOffset + i]) { isSameBootstrapMethod = false; break; } } if (isSameBootstrapMethod) { bootstrapMethods.length = offset; // Revert to old position. return entry; } } entry = entry.next; } return put(new Entry(bootstrapMethodCount++, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode)); } // ----------------------------------------------------------------------------------------------- // Type table entries management. // ----------------------------------------------------------------------------------------------- /** * Returns the type table element whose index is given. * * @param typeIndex a type table index. * @return the type table element whose index is given. */ Symbol getType(final int typeIndex) { return typeTable[typeIndex]; } /** * Adds a type in the type table of this symbol table. Does nothing if the type table already * contains a similar type. * * @param value an internal class name. * @return the index of a new or already existing type Symbol with the given value. */ int addType(final String value) { int hashCode = hash(Symbol.TYPE_TAG, value); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == Symbol.TYPE_TAG && entry.hashCode == hashCode && entry.value.equals(value)) { return entry.index; } entry = entry.next; } return addTypeInternal(new Entry(typeCount, Symbol.TYPE_TAG, value, hashCode)); } /** * Adds an {@link Frame#ITEM_UNINITIALIZED} type in the type table of this symbol table. Does * nothing if the type table already contains a similar type. * * @param value an internal class name. * @param bytecodeOffset the bytecode offset of the NEW instruction that created this {@link * Frame#ITEM_UNINITIALIZED} type value. * @return the index of a new or already existing type Symbol with the given value. */ int addUninitializedType(final String value, final int bytecodeOffset) { int hashCode = hash(Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == Symbol.UNINITIALIZED_TYPE_TAG && entry.hashCode == hashCode && entry.data == bytecodeOffset && entry.value.equals(value)) { return entry.index; } entry = entry.next; } return addTypeInternal( new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode)); } /** * Adds a merged type in the type table of this symbol table. Does nothing if the type table * already contains a similar type. * * @param typeTableIndex1 a {@link Symbol#TYPE_TAG} type, specified by its index in the type * table. * @param typeTableIndex2 another {@link Symbol#TYPE_TAG} type, specified by its index in the type * table. * @return the index of a new or already existing {@link Symbol#TYPE_TAG} type Symbol, * corresponding to the common super class of the given types. */ int addMergedType(final int typeTableIndex1, final int typeTableIndex2) { long data = typeTableIndex1 < typeTableIndex2 ? typeTableIndex1 | (((long) typeTableIndex2) << 32) : typeTableIndex2 | (((long) typeTableIndex1) << 32); int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == Symbol.MERGED_TYPE_TAG && entry.hashCode == hashCode && entry.data == data) { return entry.info; } entry = entry.next; } String type1 = typeTable[typeTableIndex1].value; String type2 = typeTable[typeTableIndex2].value; int commonSuperTypeIndex = addType(classWriter.getCommonSuperClass(type1, type2)); put(new Entry(typeCount, Symbol.MERGED_TYPE_TAG, data, hashCode)).info = commonSuperTypeIndex; return commonSuperTypeIndex; } /** * Adds the given type Symbol to {@link #typeTable}. * * @param entry a {@link Symbol#TYPE_TAG} or {@link Symbol#UNINITIALIZED_TYPE_TAG} type symbol. * The index of this Symbol must be equal to the current value of {@link #typeCount}. * @return the index in {@link #typeTable} where the given type was added, which is also equal to * entry's index by hypothesis. */ private int addTypeInternal(final Entry entry) { if (typeTable == null) { typeTable = new Entry[16]; } if (typeCount == typeTable.length) { Entry[] newTypeTable = new Entry[2 * typeTable.length]; System.arraycopy(typeTable, 0, newTypeTable, 0, typeTable.length); typeTable = newTypeTable; } typeTable[typeCount++] = entry; return put(entry).index; } // ----------------------------------------------------------------------------------------------- // Static helper methods to compute hash codes. // ----------------------------------------------------------------------------------------------- private static int hash(final int tag, final int value) { return 0x7FFFFFFF & (tag + value); } private static int hash(final int tag, final long value) { return 0x7FFFFFFF & (tag + (int) value + (int) (value >>> 32)); } private static int hash(final int tag, final String value) { return 0x7FFFFFFF & (tag + value.hashCode()); } private static int hash(final int tag, final String value1, final int value2) { return 0x7FFFFFFF & (tag + value1.hashCode() + value2); } private static int hash(final int tag, final String value1, final String value2) { return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode()); } private static int hash(final int tag, final String value1, final String value2, final int value3) { return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * (value3 + 1)); } private static int hash(final int tag, final String value1, final String value2, final String value3) { return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode()); } private static int hash(final int tag, final String value1, final String value2, final String value3, final int value4) { return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode() * value4); } /** * An entry of a SymbolTable. This concrete and private subclass of {@link Symbol} adds two fields * which are only used inside SymbolTable, to implement hash sets of symbols (in order to avoid * duplicate symbols). See {@link #entries}. * * @author Eric Bruneton */ private static class Entry extends Symbol { /** The hash code of this entry. */ final int hashCode; /** * Another entry (and so on recursively) having the same hash code (modulo the size of {@link * #entries}) as this one. */ Entry next; Entry(final int index, final int tag, final String owner, final String name, final String value, final long data, final int hashCode) { super(index, tag, owner, name, value, data); this.hashCode = hashCode; } Entry(final int index, final int tag, final String value, final int hashCode) { super(index, tag, /* owner = */ null, /* name = */ null, value, /* data = */ 0); this.hashCode = hashCode; } Entry(final int index, final int tag, final String value, final long data, final int hashCode) { super(index, tag, /* owner = */ null, /* name = */ null, value, data); this.hashCode = hashCode; } Entry(final int index, final int tag, final String name, final String value, final int hashCode) { super(index, tag, /* owner = */ null, name, value, /* data = */ 0); this.hashCode = hashCode; } Entry(final int index, final int tag, final long data, final int hashCode) { super(index, tag, /* owner = */ null, /* name = */ null, /* value = */ null, data); this.hashCode = hashCode; } } }