/* |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
/* |
|
* This file is available under and governed by the GNU General Public |
|
* License version 2 only, as published by the Free Software Foundation. |
|
* However, the following notice accompanied the original version of this |
|
* file: |
|
* |
|
* 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 jdk.internal.org.objectweb.asm; |
|
/** |
|
* The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type |
|
* table entries of a class. |
|
* |
|
* @author Eric Bruneton |
|
* @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> |
|
*/ |
|
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. |
|
int numBootstrapArguments = bootstrapMethodArguments.length; |
|
int[] bootstrapMethodArgumentIndexes = new int[numBootstrapArguments]; |
|
for (int i = 0; i < numBootstrapArguments; i++) { |
|
bootstrapMethodArgumentIndexes[i] = addConstant(bootstrapMethodArguments[i]).index; |
|
} |
|
// 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); |
|
bootstrapMethodsAttribute.putShort(numBootstrapArguments); |
|
for (int i = 0; i < numBootstrapArguments; i++) { |
|
bootstrapMethodsAttribute.putShort(bootstrapMethodArgumentIndexes[i]); |
|
} |
|
// 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; |
|
} |
|
} |
|
} |