parent
c385a1de83
commit
69e8bcdf40
|
@ -239,6 +239,7 @@ public class ByteVector {
|
||||||
* @param stringValue a String whose UTF8 encoded length must be less than 65536.
|
* @param stringValue a String whose UTF8 encoded length must be less than 65536.
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
|
// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||||||
public ByteVector putUTF8(final String stringValue) {
|
public ByteVector putUTF8(final String stringValue) {
|
||||||
int charLength = stringValue.length();
|
int charLength = stringValue.length();
|
||||||
if (charLength > 65535) {
|
if (charLength > 65535) {
|
||||||
|
@ -261,7 +262,7 @@ public class ByteVector {
|
||||||
currentData[currentLength++] = (byte) charValue;
|
currentData[currentLength++] = (byte) charValue;
|
||||||
} else {
|
} else {
|
||||||
length = currentLength;
|
length = currentLength;
|
||||||
return encodeUTF8(stringValue, i, 65535);
|
return encodeUtf8(stringValue, i, 65535);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
length = currentLength;
|
length = currentLength;
|
||||||
|
@ -280,14 +281,14 @@ public class ByteVector {
|
||||||
* encoded characters.
|
* encoded characters.
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
final ByteVector encodeUTF8(final String stringValue, final int offset, final int maxByteLength) {
|
final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength) {
|
||||||
int charLength = stringValue.length();
|
int charLength = stringValue.length();
|
||||||
int byteLength = offset;
|
int byteLength = offset;
|
||||||
for (int i = offset; i < charLength; ++i) {
|
for (int i = offset; i < charLength; ++i) {
|
||||||
char charValue = stringValue.charAt(i);
|
char charValue = stringValue.charAt(i);
|
||||||
if (charValue >= '\u0001' && charValue <= '\u007F') {
|
if (charValue >= 0x0001 && charValue <= 0x007F) {
|
||||||
byteLength++;
|
byteLength++;
|
||||||
} else if (charValue <= '\u07FF') {
|
} else if (charValue <= 0x07FF) {
|
||||||
byteLength += 2;
|
byteLength += 2;
|
||||||
} else {
|
} else {
|
||||||
byteLength += 3;
|
byteLength += 3;
|
||||||
|
@ -308,9 +309,9 @@ public class ByteVector {
|
||||||
int currentLength = length;
|
int currentLength = length;
|
||||||
for (int i = offset; i < charLength; ++i) {
|
for (int i = offset; i < charLength; ++i) {
|
||||||
char charValue = stringValue.charAt(i);
|
char charValue = stringValue.charAt(i);
|
||||||
if (charValue >= '\u0001' && charValue <= '\u007F') {
|
if (charValue >= 0x0001 && charValue <= 0x007F) {
|
||||||
data[currentLength++] = (byte) charValue;
|
data[currentLength++] = (byte) charValue;
|
||||||
} else if (charValue <= '\u07FF') {
|
} else if (charValue <= 0x07FF) {
|
||||||
data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F);
|
data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F);
|
||||||
data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
|
data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -99,6 +99,7 @@ public class ClassReader {
|
||||||
* necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
|
* necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
|
||||||
* ClassFile element offsets within this byte array.
|
* ClassFile element offsets within this byte array.
|
||||||
*/
|
*/
|
||||||
|
// DontCheck(MemberName): can't be renamed (for backward binary compatibility).
|
||||||
public final byte[] b;
|
public final byte[] b;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -488,7 +489,7 @@ public class ClassReader {
|
||||||
accessFlags |= Opcodes.ACC_SYNTHETIC;
|
accessFlags |= Opcodes.ACC_SYNTHETIC;
|
||||||
} else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
|
} else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
|
||||||
sourceDebugExtension =
|
sourceDebugExtension =
|
||||||
readUTF(currentAttributeOffset, attributeLength, new char[attributeLength]);
|
readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
|
||||||
} else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
|
} else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||||||
runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
|
runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
|
||||||
} else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
} else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||||||
|
@ -530,7 +531,8 @@ public class ClassReader {
|
||||||
|
|
||||||
// Visit the Module, ModulePackages and ModuleMainClass attributes.
|
// Visit the Module, ModulePackages and ModuleMainClass attributes.
|
||||||
if (moduleOffset != 0) {
|
if (moduleOffset != 0) {
|
||||||
readModule(classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
|
readModuleAttributes(
|
||||||
|
classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit the NestHost attribute.
|
// Visit the NestHost attribute.
|
||||||
|
@ -685,7 +687,7 @@ public class ClassReader {
|
||||||
// ----------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the module attribute and visit it.
|
* Reads the Module, ModulePackages and ModuleMainClass attributes and visit them.
|
||||||
*
|
*
|
||||||
* @param classVisitor the current class visitor
|
* @param classVisitor the current class visitor
|
||||||
* @param context information about the class being parsed.
|
* @param context information about the class being parsed.
|
||||||
|
@ -695,7 +697,7 @@ public class ClassReader {
|
||||||
* attribute_info's attribute_name_index and attribute_length fields), or 0.
|
* attribute_info's attribute_name_index and attribute_length fields), or 0.
|
||||||
* @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
|
* @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
|
||||||
*/
|
*/
|
||||||
private void readModule(
|
private void readModuleAttributes(
|
||||||
final ClassVisitor classVisitor,
|
final ClassVisitor classVisitor,
|
||||||
final Context context,
|
final Context context,
|
||||||
final int moduleOffset,
|
final int moduleOffset,
|
||||||
|
@ -1111,7 +1113,7 @@ public class ClassReader {
|
||||||
context.currentMethodAccessFlags,
|
context.currentMethodAccessFlags,
|
||||||
context.currentMethodName,
|
context.currentMethodName,
|
||||||
context.currentMethodDescriptor,
|
context.currentMethodDescriptor,
|
||||||
signatureIndex == 0 ? null : readUTF(signatureIndex, charBuffer),
|
signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer),
|
||||||
exceptions);
|
exceptions);
|
||||||
if (methodVisitor == null) {
|
if (methodVisitor == null) {
|
||||||
return currentOffset;
|
return currentOffset;
|
||||||
|
@ -1601,18 +1603,15 @@ public class ClassReader {
|
||||||
|
|
||||||
// Read the 'exception_table_length' and 'exception_table' field to create a label for each
|
// Read the 'exception_table_length' and 'exception_table' field to create a label for each
|
||||||
// referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
|
// referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
|
||||||
{
|
int exceptionTableLength = readUnsignedShort(currentOffset);
|
||||||
int exceptionTableLength = readUnsignedShort(currentOffset);
|
currentOffset += 2;
|
||||||
currentOffset += 2;
|
while (exceptionTableLength-- > 0) {
|
||||||
while (exceptionTableLength-- > 0) {
|
Label start = createLabel(readUnsignedShort(currentOffset), labels);
|
||||||
Label start = createLabel(readUnsignedShort(currentOffset), labels);
|
Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
|
||||||
Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
|
Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
|
||||||
Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
|
String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
|
||||||
String catchType =
|
currentOffset += 8;
|
||||||
readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
|
methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
|
||||||
currentOffset += 8;
|
|
||||||
methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the Code attributes to create a label for each referenced instruction (the variables
|
// Read the Code attributes to create a label for each referenced instruction (the variables
|
||||||
|
@ -2142,11 +2141,11 @@ public class ClassReader {
|
||||||
currentOffset += 4 - (currentBytecodeOffset & 3);
|
currentOffset += 4 - (currentBytecodeOffset & 3);
|
||||||
// Read the instruction.
|
// Read the instruction.
|
||||||
Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
|
Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
|
||||||
int nPairs = readInt(currentOffset + 4);
|
int numPairs = readInt(currentOffset + 4);
|
||||||
currentOffset += 8;
|
currentOffset += 8;
|
||||||
int[] keys = new int[nPairs];
|
int[] keys = new int[numPairs];
|
||||||
Label[] values = new Label[nPairs];
|
Label[] values = new Label[numPairs];
|
||||||
for (int i = 0; i < nPairs; ++i) {
|
for (int i = 0; i < numPairs; ++i) {
|
||||||
keys[i] = readInt(currentOffset);
|
keys[i] = readInt(currentOffset);
|
||||||
values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
|
values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
|
||||||
currentOffset += 8;
|
currentOffset += 8;
|
||||||
|
@ -2958,12 +2957,12 @@ public class ClassReader {
|
||||||
private void computeImplicitFrame(final Context context) {
|
private void computeImplicitFrame(final Context context) {
|
||||||
String methodDescriptor = context.currentMethodDescriptor;
|
String methodDescriptor = context.currentMethodDescriptor;
|
||||||
Object[] locals = context.currentFrameLocalTypes;
|
Object[] locals = context.currentFrameLocalTypes;
|
||||||
int nLocal = 0;
|
int numLocal = 0;
|
||||||
if ((context.currentMethodAccessFlags & Opcodes.ACC_STATIC) == 0) {
|
if ((context.currentMethodAccessFlags & Opcodes.ACC_STATIC) == 0) {
|
||||||
if ("<init>".equals(context.currentMethodName)) {
|
if ("<init>".equals(context.currentMethodName)) {
|
||||||
locals[nLocal++] = Opcodes.UNINITIALIZED_THIS;
|
locals[numLocal++] = Opcodes.UNINITIALIZED_THIS;
|
||||||
} else {
|
} else {
|
||||||
locals[nLocal++] = readClass(header + 2, context.charBuffer);
|
locals[numLocal++] = readClass(header + 2, context.charBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Parse the method descriptor, one argument type descriptor at each iteration. Start by
|
// Parse the method descriptor, one argument type descriptor at each iteration. Start by
|
||||||
|
@ -2977,16 +2976,16 @@ public class ClassReader {
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'S':
|
case 'S':
|
||||||
case 'I':
|
case 'I':
|
||||||
locals[nLocal++] = Opcodes.INTEGER;
|
locals[numLocal++] = Opcodes.INTEGER;
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
locals[nLocal++] = Opcodes.FLOAT;
|
locals[numLocal++] = Opcodes.FLOAT;
|
||||||
break;
|
break;
|
||||||
case 'J':
|
case 'J':
|
||||||
locals[nLocal++] = Opcodes.LONG;
|
locals[numLocal++] = Opcodes.LONG;
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
locals[nLocal++] = Opcodes.DOUBLE;
|
locals[numLocal++] = Opcodes.DOUBLE;
|
||||||
break;
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
while (methodDescriptor.charAt(currentMethodDescritorOffset) == '[') {
|
while (methodDescriptor.charAt(currentMethodDescritorOffset) == '[') {
|
||||||
|
@ -2998,7 +2997,7 @@ public class ClassReader {
|
||||||
++currentMethodDescritorOffset;
|
++currentMethodDescritorOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
locals[nLocal++] =
|
locals[numLocal++] =
|
||||||
methodDescriptor.substring(
|
methodDescriptor.substring(
|
||||||
currentArgumentDescriptorStartOffset, ++currentMethodDescritorOffset);
|
currentArgumentDescriptorStartOffset, ++currentMethodDescritorOffset);
|
||||||
break;
|
break;
|
||||||
|
@ -3006,12 +3005,12 @@ public class ClassReader {
|
||||||
while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
|
while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
|
||||||
++currentMethodDescritorOffset;
|
++currentMethodDescritorOffset;
|
||||||
}
|
}
|
||||||
locals[nLocal++] =
|
locals[numLocal++] =
|
||||||
methodDescriptor.substring(
|
methodDescriptor.substring(
|
||||||
currentArgumentDescriptorStartOffset + 1, currentMethodDescritorOffset++);
|
currentArgumentDescriptorStartOffset + 1, currentMethodDescritorOffset++);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
context.currentFrameLocalCount = nLocal;
|
context.currentFrameLocalCount = numLocal;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3178,7 +3177,11 @@ public class ClassReader {
|
||||||
// Methods to parse attributes
|
// Methods to parse attributes
|
||||||
// ----------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** @return the offset in {@link #b} of the first ClassFile's 'attributes' array field entry. */
|
/**
|
||||||
|
* Returns the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
|
||||||
|
*
|
||||||
|
* @return the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
|
||||||
|
*/
|
||||||
final int getFirstAttributeOffset() {
|
final int getFirstAttributeOffset() {
|
||||||
// Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
|
// Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
|
||||||
// each), as well as the interfaces array field (2 bytes per interface).
|
// each), as well as the interfaces array field (2 bytes per interface).
|
||||||
|
@ -3399,12 +3402,13 @@ public class ClassReader {
|
||||||
* large. It is not automatically resized.
|
* large. It is not automatically resized.
|
||||||
* @return the String corresponding to the specified CONSTANT_Utf8 entry.
|
* @return the String corresponding to the specified CONSTANT_Utf8 entry.
|
||||||
*/
|
*/
|
||||||
|
// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||||||
public String readUTF8(final int offset, final char[] charBuffer) {
|
public String readUTF8(final int offset, final char[] charBuffer) {
|
||||||
int constantPoolEntryIndex = readUnsignedShort(offset);
|
int constantPoolEntryIndex = readUnsignedShort(offset);
|
||||||
if (offset == 0 || constantPoolEntryIndex == 0) {
|
if (offset == 0 || constantPoolEntryIndex == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return readUTF(constantPoolEntryIndex, charBuffer);
|
return readUtf(constantPoolEntryIndex, charBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3416,14 +3420,14 @@ public class ClassReader {
|
||||||
* large. It is not automatically resized.
|
* large. It is not automatically resized.
|
||||||
* @return the String corresponding to the specified CONSTANT_Utf8 entry.
|
* @return the String corresponding to the specified CONSTANT_Utf8 entry.
|
||||||
*/
|
*/
|
||||||
final String readUTF(final int constantPoolEntryIndex, final char[] charBuffer) {
|
final String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) {
|
||||||
String value = constantUtf8Values[constantPoolEntryIndex];
|
String value = constantUtf8Values[constantPoolEntryIndex];
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
|
int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
|
||||||
return constantUtf8Values[constantPoolEntryIndex] =
|
return constantUtf8Values[constantPoolEntryIndex] =
|
||||||
readUTF(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
|
readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3435,7 +3439,7 @@ public class ClassReader {
|
||||||
* large. It is not automatically resized.
|
* large. It is not automatically resized.
|
||||||
* @return the String corresponding to the specified UTF8 string.
|
* @return the String corresponding to the specified UTF8 string.
|
||||||
*/
|
*/
|
||||||
private String readUTF(final int utfOffset, final int utfLength, final char[] charBuffer) {
|
private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) {
|
||||||
int currentOffset = utfOffset;
|
int currentOffset = utfOffset;
|
||||||
int endOffset = currentOffset + utfLength;
|
int endOffset = currentOffset + utfLength;
|
||||||
int strLength = 0;
|
int strLength = 0;
|
||||||
|
|
|
@ -51,12 +51,20 @@ public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||||||
this.constantPoolCount = constantPoolCount;
|
this.constantPoolCount = constantPoolCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the internal name of the class. */
|
/**
|
||||||
|
* Returns the internal name of the class.
|
||||||
|
*
|
||||||
|
* @return the internal name of the class.
|
||||||
|
*/
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the number of constant pool items of the class. */
|
/**
|
||||||
|
* Returns the number of constant pool items of the class.
|
||||||
|
*
|
||||||
|
* @return the number of constant pool items of the class.
|
||||||
|
*/
|
||||||
public int getConstantPoolCount() {
|
public int getConstantPoolCount() {
|
||||||
return constantPoolCount;
|
return constantPoolCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,7 +282,7 @@ public class ClassWriter extends ClassVisitor {
|
||||||
sourceFileIndex = symbolTable.addConstantUtf8(file);
|
sourceFileIndex = symbolTable.addConstantUtf8(file);
|
||||||
}
|
}
|
||||||
if (debug != null) {
|
if (debug != null) {
|
||||||
debugExtension = new ByteVector().encodeUTF8(debug, 0, Integer.MAX_VALUE);
|
debugExtension = new ByteVector().encodeUtf8(debug, 0, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +674,7 @@ public class ClassWriter extends ClassVisitor {
|
||||||
* ones.
|
* ones.
|
||||||
*/
|
*/
|
||||||
private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) {
|
private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) {
|
||||||
Attribute[] attributes = getAttributePrototypes();
|
final Attribute[] attributes = getAttributePrototypes();
|
||||||
firstField = null;
|
firstField = null;
|
||||||
lastField = null;
|
lastField = null;
|
||||||
firstMethod = null;
|
firstMethod = null;
|
||||||
|
@ -743,6 +743,7 @@ public class ClassWriter extends ClassVisitor {
|
||||||
* @param value the String value.
|
* @param value the String value.
|
||||||
* @return the index of a new or already existing UTF8 item.
|
* @return the index of a new or already existing UTF8 item.
|
||||||
*/
|
*/
|
||||||
|
// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||||||
public int newUTF8(final String value) {
|
public int newUTF8(final String value) {
|
||||||
return symbolTable.addConstantUtf8(value);
|
return symbolTable.addConstantUtf8(value);
|
||||||
}
|
}
|
||||||
|
@ -947,13 +948,13 @@ public class ClassWriter extends ClassVisitor {
|
||||||
Class<?> class1;
|
Class<?> class1;
|
||||||
try {
|
try {
|
||||||
class1 = Class.forName(type1.replace('/', '.'), false, classLoader);
|
class1 = Class.forName(type1.replace('/', '.'), false, classLoader);
|
||||||
} catch (Exception e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new TypeNotPresentException(type1, e);
|
throw new TypeNotPresentException(type1, e);
|
||||||
}
|
}
|
||||||
Class<?> class2;
|
Class<?> class2;
|
||||||
try {
|
try {
|
||||||
class2 = Class.forName(type2.replace('/', '.'), false, classLoader);
|
class2 = Class.forName(type2.replace('/', '.'), false, classLoader);
|
||||||
} catch (Exception e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new TypeNotPresentException(type2, e);
|
throw new TypeNotPresentException(type2, e);
|
||||||
}
|
}
|
||||||
if (class1.isAssignableFrom(class2)) {
|
if (class1.isAssignableFrom(class2)) {
|
||||||
|
|
|
@ -36,8 +36,6 @@ package org.springframework.asm;
|
||||||
*/
|
*/
|
||||||
final class Constants implements Opcodes {
|
final class Constants implements Opcodes {
|
||||||
|
|
||||||
private Constants() {}
|
|
||||||
|
|
||||||
// The ClassFile attribute names, in the order they are defined in
|
// The ClassFile attribute names, in the order they are defined in
|
||||||
// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300.
|
// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300.
|
||||||
|
|
||||||
|
@ -173,4 +171,6 @@ final class Constants implements Opcodes {
|
||||||
static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA;
|
static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA;
|
||||||
static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
|
static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
|
||||||
static final int ASM_GOTO_W = 220;
|
static final int ASM_GOTO_W = 220;
|
||||||
|
|
||||||
|
private Constants() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,38 @@ class Frame {
|
||||||
*/
|
*/
|
||||||
private int[] initializations;
|
private int[] initializations;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Frame.
|
||||||
|
*
|
||||||
|
* @param owner the basic block to which these input and output stack map frames correspond.
|
||||||
|
*/
|
||||||
|
Frame(final Label owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this frame to the value of the given frame.
|
||||||
|
*
|
||||||
|
* <p>WARNING: after this method is called the two frames share the same data structures. It is
|
||||||
|
* recommended to discard the given frame to avoid unexpected side effects.
|
||||||
|
*
|
||||||
|
* @param frame The new frame value.
|
||||||
|
*/
|
||||||
|
final void copyFrom(final Frame frame) {
|
||||||
|
inputLocals = frame.inputLocals;
|
||||||
|
inputStack = frame.inputStack;
|
||||||
|
outputStackStart = 0;
|
||||||
|
outputLocals = frame.outputLocals;
|
||||||
|
outputStack = frame.outputStack;
|
||||||
|
outputStackTop = frame.outputStackTop;
|
||||||
|
initializationCount = frame.initializationCount;
|
||||||
|
initializations = frame.initializations;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Static methods to get abstract types from other type formats
|
// Static methods to get abstract types from other type formats
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
@ -336,38 +368,6 @@ class Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
// Constructor
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new Frame.
|
|
||||||
*
|
|
||||||
* @param owner the basic block to which these input and output stack map frames correspond.
|
|
||||||
*/
|
|
||||||
Frame(final Label owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this frame to the value of the given frame.
|
|
||||||
*
|
|
||||||
* <p>WARNING: after this method is called the two frames share the same data structures. It is
|
|
||||||
* recommended to discard the given frame to avoid unexpected side effects.
|
|
||||||
*
|
|
||||||
* @param frame The new frame value.
|
|
||||||
*/
|
|
||||||
final void copyFrom(final Frame frame) {
|
|
||||||
inputLocals = frame.inputLocals;
|
|
||||||
inputStack = frame.inputStack;
|
|
||||||
outputStackStart = 0;
|
|
||||||
outputLocals = frame.outputLocals;
|
|
||||||
outputStack = frame.outputStack;
|
|
||||||
outputStackTop = frame.outputStackTop;
|
|
||||||
initializationCount = frame.initializationCount;
|
|
||||||
initializations = frame.initializations;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Methods related to the input frame
|
// Methods related to the input frame
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
@ -415,21 +415,21 @@ class Frame {
|
||||||
* Sets the input frame from the given public API frame description.
|
* Sets the input frame from the given public API frame description.
|
||||||
*
|
*
|
||||||
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
||||||
* @param nLocal the number of local variables.
|
* @param numLocal the number of local variables.
|
||||||
* @param local the local variable types, described using the same format as in {@link
|
* @param local the local variable types, described using the same format as in {@link
|
||||||
* MethodVisitor#visitFrame}.
|
* MethodVisitor#visitFrame}.
|
||||||
* @param nStack the number of operand stack elements.
|
* @param numStack the number of operand stack elements.
|
||||||
* @param stack the operand stack types, described using the same format as in {@link
|
* @param stack the operand stack types, described using the same format as in {@link
|
||||||
* MethodVisitor#visitFrame}.
|
* MethodVisitor#visitFrame}.
|
||||||
*/
|
*/
|
||||||
final void setInputFrameFromApiFormat(
|
final void setInputFrameFromApiFormat(
|
||||||
final SymbolTable symbolTable,
|
final SymbolTable symbolTable,
|
||||||
final int nLocal,
|
final int numLocal,
|
||||||
final Object[] local,
|
final Object[] local,
|
||||||
final int nStack,
|
final int numStack,
|
||||||
final Object[] stack) {
|
final Object[] stack) {
|
||||||
int inputLocalIndex = 0;
|
int inputLocalIndex = 0;
|
||||||
for (int i = 0; i < nLocal; ++i) {
|
for (int i = 0; i < numLocal; ++i) {
|
||||||
inputLocals[inputLocalIndex++] = getAbstractTypeFromApiFormat(symbolTable, local[i]);
|
inputLocals[inputLocalIndex++] = getAbstractTypeFromApiFormat(symbolTable, local[i]);
|
||||||
if (local[i] == Opcodes.LONG || local[i] == Opcodes.DOUBLE) {
|
if (local[i] == Opcodes.LONG || local[i] == Opcodes.DOUBLE) {
|
||||||
inputLocals[inputLocalIndex++] = TOP;
|
inputLocals[inputLocalIndex++] = TOP;
|
||||||
|
@ -438,15 +438,15 @@ class Frame {
|
||||||
while (inputLocalIndex < inputLocals.length) {
|
while (inputLocalIndex < inputLocals.length) {
|
||||||
inputLocals[inputLocalIndex++] = TOP;
|
inputLocals[inputLocalIndex++] = TOP;
|
||||||
}
|
}
|
||||||
int nStackTop = 0;
|
int numStackTop = 0;
|
||||||
for (int i = 0; i < nStack; ++i) {
|
for (int i = 0; i < numStack; ++i) {
|
||||||
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
||||||
++nStackTop;
|
++numStackTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputStack = new int[nStack + nStackTop];
|
inputStack = new int[numStack + numStackTop];
|
||||||
int inputStackIndex = 0;
|
int inputStackIndex = 0;
|
||||||
for (int i = 0; i < nStack; ++i) {
|
for (int i = 0; i < numStack; ++i) {
|
||||||
inputStack[inputStackIndex++] = getAbstractTypeFromApiFormat(symbolTable, stack[i]);
|
inputStack[inputStackIndex++] = getAbstractTypeFromApiFormat(symbolTable, stack[i]);
|
||||||
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
||||||
inputStack[inputStackIndex++] = TOP;
|
inputStack[inputStackIndex++] = TOP;
|
||||||
|
@ -1122,13 +1122,13 @@ class Frame {
|
||||||
// Compute the concrete types of the local variables at the end of the basic block corresponding
|
// Compute the concrete types of the local variables at the end of the basic block corresponding
|
||||||
// to this frame, by resolving its abstract output types, and merge these concrete types with
|
// to this frame, by resolving its abstract output types, and merge these concrete types with
|
||||||
// those of the local variables in the input frame of dstFrame.
|
// those of the local variables in the input frame of dstFrame.
|
||||||
int nLocal = inputLocals.length;
|
int numLocal = inputLocals.length;
|
||||||
int nStack = inputStack.length;
|
int numStack = inputStack.length;
|
||||||
if (dstFrame.inputLocals == null) {
|
if (dstFrame.inputLocals == null) {
|
||||||
dstFrame.inputLocals = new int[nLocal];
|
dstFrame.inputLocals = new int[numLocal];
|
||||||
frameChanged = true;
|
frameChanged = true;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nLocal; ++i) {
|
for (int i = 0; i < numLocal; ++i) {
|
||||||
int concreteOutputType;
|
int concreteOutputType;
|
||||||
if (outputLocals != null && i < outputLocals.length) {
|
if (outputLocals != null && i < outputLocals.length) {
|
||||||
int abstractOutputType = outputLocals[i];
|
int abstractOutputType = outputLocals[i];
|
||||||
|
@ -1152,7 +1152,7 @@ class Frame {
|
||||||
// By definition, a STACK_KIND type designates the concrete type of a local variable at
|
// By definition, a STACK_KIND type designates the concrete type of a local variable at
|
||||||
// the beginning of the basic block corresponding to this frame (which is known when
|
// the beginning of the basic block corresponding to this frame (which is known when
|
||||||
// this method is called, but was not when the abstract type was computed).
|
// this method is called, but was not when the abstract type was computed).
|
||||||
concreteOutputType = dim + inputStack[nStack - (abstractOutputType & VALUE_MASK)];
|
concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
|
||||||
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
||||||
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
||||||
concreteOutputType = TOP;
|
concreteOutputType = TOP;
|
||||||
|
@ -1181,7 +1181,7 @@ class Frame {
|
||||||
// frame (and the input stack of dstFrame should be compatible, i.e. merged, with a one
|
// frame (and the input stack of dstFrame should be compatible, i.e. merged, with a one
|
||||||
// element stack containing the caught exception type).
|
// element stack containing the caught exception type).
|
||||||
if (catchTypeIndex > 0) {
|
if (catchTypeIndex > 0) {
|
||||||
for (int i = 0; i < nLocal; ++i) {
|
for (int i = 0; i < numLocal; ++i) {
|
||||||
frameChanged |= merge(symbolTable, inputLocals[i], dstFrame.inputLocals, i);
|
frameChanged |= merge(symbolTable, inputLocals[i], dstFrame.inputLocals, i);
|
||||||
}
|
}
|
||||||
if (dstFrame.inputStack == null) {
|
if (dstFrame.inputStack == null) {
|
||||||
|
@ -1195,15 +1195,15 @@ class Frame {
|
||||||
// Compute the concrete types of the stack operands at the end of the basic block corresponding
|
// Compute the concrete types of the stack operands at the end of the basic block corresponding
|
||||||
// to this frame, by resolving its abstract output types, and merge these concrete types with
|
// to this frame, by resolving its abstract output types, and merge these concrete types with
|
||||||
// those of the stack operands in the input frame of dstFrame.
|
// those of the stack operands in the input frame of dstFrame.
|
||||||
int nInputStack = inputStack.length + outputStackStart;
|
int numInputStack = inputStack.length + outputStackStart;
|
||||||
if (dstFrame.inputStack == null) {
|
if (dstFrame.inputStack == null) {
|
||||||
dstFrame.inputStack = new int[nInputStack + outputStackTop];
|
dstFrame.inputStack = new int[numInputStack + outputStackTop];
|
||||||
frameChanged = true;
|
frameChanged = true;
|
||||||
}
|
}
|
||||||
// First, do this for the stack operands that have not been popped in the basic block
|
// First, do this for the stack operands that have not been popped in the basic block
|
||||||
// corresponding to this frame, and which are therefore equal to their value in the input
|
// corresponding to this frame, and which are therefore equal to their value in the input
|
||||||
// frame (except for uninitialized types, which may have been initialized).
|
// frame (except for uninitialized types, which may have been initialized).
|
||||||
for (int i = 0; i < nInputStack; ++i) {
|
for (int i = 0; i < numInputStack; ++i) {
|
||||||
int concreteOutputType = inputStack[i];
|
int concreteOutputType = inputStack[i];
|
||||||
if (initializations != null) {
|
if (initializations != null) {
|
||||||
concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
||||||
|
@ -1224,7 +1224,7 @@ class Frame {
|
||||||
concreteOutputType = TOP;
|
concreteOutputType = TOP;
|
||||||
}
|
}
|
||||||
} else if (kind == STACK_KIND) {
|
} else if (kind == STACK_KIND) {
|
||||||
concreteOutputType = dim + inputStack[nStack - (abstractOutputType & VALUE_MASK)];
|
concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
|
||||||
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
||||||
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
||||||
concreteOutputType = TOP;
|
concreteOutputType = TOP;
|
||||||
|
@ -1235,7 +1235,8 @@ class Frame {
|
||||||
if (initializations != null) {
|
if (initializations != null) {
|
||||||
concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
||||||
}
|
}
|
||||||
frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputStack, nInputStack + i);
|
frameChanged |=
|
||||||
|
merge(symbolTable, concreteOutputType, dstFrame.inputStack, numInputStack + i);
|
||||||
}
|
}
|
||||||
return frameChanged;
|
return frameChanged;
|
||||||
}
|
}
|
||||||
|
@ -1348,38 +1349,38 @@ class Frame {
|
||||||
// Compute the number of locals, ignoring TOP types that are just after a LONG or a DOUBLE, and
|
// Compute the number of locals, ignoring TOP types that are just after a LONG or a DOUBLE, and
|
||||||
// all trailing TOP types.
|
// all trailing TOP types.
|
||||||
int[] localTypes = inputLocals;
|
int[] localTypes = inputLocals;
|
||||||
int nLocal = 0;
|
int numLocal = 0;
|
||||||
int nTrailingTop = 0;
|
int numTrailingTop = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < localTypes.length) {
|
while (i < localTypes.length) {
|
||||||
int localType = localTypes[i];
|
int localType = localTypes[i];
|
||||||
i += (localType == LONG || localType == DOUBLE) ? 2 : 1;
|
i += (localType == LONG || localType == DOUBLE) ? 2 : 1;
|
||||||
if (localType == TOP) {
|
if (localType == TOP) {
|
||||||
nTrailingTop++;
|
numTrailingTop++;
|
||||||
} else {
|
} else {
|
||||||
nLocal += nTrailingTop + 1;
|
numLocal += numTrailingTop + 1;
|
||||||
nTrailingTop = 0;
|
numTrailingTop = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Compute the stack size, ignoring TOP types that are just after a LONG or a DOUBLE.
|
// Compute the stack size, ignoring TOP types that are just after a LONG or a DOUBLE.
|
||||||
int[] stackTypes = inputStack;
|
int[] stackTypes = inputStack;
|
||||||
int nStack = 0;
|
int numStack = 0;
|
||||||
i = 0;
|
i = 0;
|
||||||
while (i < stackTypes.length) {
|
while (i < stackTypes.length) {
|
||||||
int stackType = stackTypes[i];
|
int stackType = stackTypes[i];
|
||||||
i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1;
|
i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1;
|
||||||
nStack++;
|
numStack++;
|
||||||
}
|
}
|
||||||
// Visit the frame and its content.
|
// Visit the frame and its content.
|
||||||
int frameIndex = methodWriter.visitFrameStart(owner.bytecodeOffset, nLocal, nStack);
|
int frameIndex = methodWriter.visitFrameStart(owner.bytecodeOffset, numLocal, numStack);
|
||||||
i = 0;
|
i = 0;
|
||||||
while (nLocal-- > 0) {
|
while (numLocal-- > 0) {
|
||||||
int localType = localTypes[i];
|
int localType = localTypes[i];
|
||||||
i += (localType == LONG || localType == DOUBLE) ? 2 : 1;
|
i += (localType == LONG || localType == DOUBLE) ? 2 : 1;
|
||||||
methodWriter.visitAbstractType(frameIndex++, localType);
|
methodWriter.visitAbstractType(frameIndex++, localType);
|
||||||
}
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
while (nStack-- > 0) {
|
while (numStack-- > 0) {
|
||||||
int stackType = stackTypes[i];
|
int stackType = stackTypes[i];
|
||||||
i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1;
|
i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1;
|
||||||
methodWriter.visitAbstractType(frameIndex++, stackType);
|
methodWriter.visitAbstractType(frameIndex++, stackType);
|
||||||
|
|
|
@ -178,7 +178,7 @@ public class Label {
|
||||||
* and {@link #FORWARD_REFERENCE_HANDLE_MASK}.
|
* and {@link #FORWARD_REFERENCE_HANDLE_MASK}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is
|
* <p>For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is
|
||||||
* equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1
|
* equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1
|
||||||
* (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after
|
* (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after
|
||||||
* the start of the instruction itself). For the default case of a lookupswitch instruction at
|
* the start of the instruction itself). For the default case of a lookupswitch instruction at
|
||||||
|
|
|
@ -61,22 +61,38 @@ public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
||||||
this.codeSize = codeSize;
|
this.codeSize = codeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the internal name of the owner class. */
|
/**
|
||||||
|
* Returns the internal name of the owner class.
|
||||||
|
*
|
||||||
|
* @return the internal name of the owner class.
|
||||||
|
*/
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the name of the method. */
|
/**
|
||||||
|
* Returns the name of the method.
|
||||||
|
*
|
||||||
|
* @return the name of the method.
|
||||||
|
*/
|
||||||
public String getMethodName() {
|
public String getMethodName() {
|
||||||
return methodName;
|
return methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the descriptor of the method. */
|
/**
|
||||||
|
* Returns the descriptor of the method.
|
||||||
|
*
|
||||||
|
* @return the descriptor of the method.
|
||||||
|
*/
|
||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the size of the method's Code attribute, in bytes. */
|
/**
|
||||||
|
* Returns the size of the method's Code attribute, in bytes.
|
||||||
|
*
|
||||||
|
* @return the size of the method's Code attribute, in bytes.
|
||||||
|
*/
|
||||||
public int getCodeSize() {
|
public int getCodeSize() {
|
||||||
return codeSize;
|
return codeSize;
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,15 +243,15 @@ public abstract class MethodVisitor {
|
||||||
* <li>{@link Opcodes#F_SAME} representing frame with exactly the same locals as the
|
* <li>{@link Opcodes#F_SAME} representing frame with exactly the same locals as the
|
||||||
* previous frame and with the empty stack.
|
* previous frame and with the empty stack.
|
||||||
* <li>{@link Opcodes#F_SAME1} representing frame with exactly the same locals as the
|
* <li>{@link Opcodes#F_SAME1} representing frame with exactly the same locals as the
|
||||||
* previous frame and with single value on the stack ( <code>nStack</code> is 1 and
|
* previous frame and with single value on the stack ( <code>numStack</code> is 1 and
|
||||||
* <code>stack[0]</code> contains value for the type of the stack item).
|
* <code>stack[0]</code> contains value for the type of the stack item).
|
||||||
* <li>{@link Opcodes#F_APPEND} representing frame with current locals are the same as the
|
* <li>{@link Opcodes#F_APPEND} representing frame with current locals are the same as the
|
||||||
* locals in the previous frame, except that additional locals are defined (<code>
|
* locals in the previous frame, except that additional locals are defined (<code>
|
||||||
* nLocal</code> is 1, 2 or 3 and <code>local</code> elements contains values
|
* numLocal</code> is 1, 2 or 3 and <code>local</code> elements contains values
|
||||||
* representing added types).
|
* representing added types).
|
||||||
* <li>{@link Opcodes#F_CHOP} representing frame with current locals are the same as the
|
* <li>{@link Opcodes#F_CHOP} representing frame with current locals are the same as the
|
||||||
* locals in the previous frame, except that the last 1-3 locals are absent and with
|
* locals in the previous frame, except that the last 1-3 locals are absent and with
|
||||||
* the empty stack (<code>nLocals</code> is 1, 2 or 3).
|
* the empty stack (<code>numLocal</code> is 1, 2 or 3).
|
||||||
* <li>{@link Opcodes#F_FULL} representing complete frame data.
|
* <li>{@link Opcodes#F_FULL} representing complete frame data.
|
||||||
* </ul>
|
* </ul>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
@ -264,7 +264,7 @@ public abstract class MethodVisitor {
|
||||||
* @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded
|
* @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded
|
||||||
* frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link
|
* frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link
|
||||||
* Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames.
|
* Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames.
|
||||||
* @param nLocal the number of local variables in the visited frame.
|
* @param numLocal the number of local variables in the visited frame.
|
||||||
* @param local the local variable types in this frame. This array must not be modified. Primitive
|
* @param local the local variable types in this frame. This array must not be modified. Primitive
|
||||||
* types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
* types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
||||||
* Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or
|
* Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or
|
||||||
|
@ -272,7 +272,7 @@ public abstract class MethodVisitor {
|
||||||
* Reference types are represented by String objects (representing internal names), and
|
* Reference types are represented by String objects (representing internal names), and
|
||||||
* uninitialized types by Label objects (this label designates the NEW instruction that
|
* uninitialized types by Label objects (this label designates the NEW instruction that
|
||||||
* created this uninitialized value).
|
* created this uninitialized value).
|
||||||
* @param nStack the number of operand stack elements in the visited frame.
|
* @param numStack the number of operand stack elements in the visited frame.
|
||||||
* @param stack the operand stack types in this frame. This array must not be modified. Its
|
* @param stack the operand stack types in this frame. This array must not be modified. Its
|
||||||
* content has the same format as the "local" array.
|
* content has the same format as the "local" array.
|
||||||
* @throws IllegalStateException if a frame is visited just after another one, without any
|
* @throws IllegalStateException if a frame is visited just after another one, without any
|
||||||
|
@ -281,12 +281,12 @@ public abstract class MethodVisitor {
|
||||||
*/
|
*/
|
||||||
public void visitFrame(
|
public void visitFrame(
|
||||||
final int type,
|
final int type,
|
||||||
final int nLocal,
|
final int numLocal,
|
||||||
final Object[] local,
|
final Object[] local,
|
||||||
final int nStack,
|
final int numStack,
|
||||||
final Object[] stack) {
|
final Object[] stack) {
|
||||||
if (mv != null) {
|
if (mv != null) {
|
||||||
mv.visitFrame(type, nLocal, local, nStack, stack);
|
mv.visitFrame(type, numLocal, local, numStack, stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +390,7 @@ public abstract class MethodVisitor {
|
||||||
* Type#getInternalName()}).
|
* Type#getInternalName()}).
|
||||||
* @param name the method's name.
|
* @param name the method's name.
|
||||||
* @param descriptor the method's descriptor (see {@link Type}).
|
* @param descriptor the method's descriptor (see {@link Type}).
|
||||||
* @deprecated
|
* @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void visitMethodInsn(
|
public void visitMethodInsn(
|
||||||
|
|
|
@ -531,11 +531,10 @@ final class MethodWriter extends MethodVisitor {
|
||||||
* The current stack map frame. The first element contains the bytecode offset of the instruction
|
* The current stack map frame. The first element contains the bytecode offset of the instruction
|
||||||
* to which the frame corresponds, the second element is the number of locals and the third one is
|
* to which the frame corresponds, the second element is the number of locals and the third one is
|
||||||
* the number of stack elements. The local variables start at index 3 and are followed by the
|
* the number of stack elements. The local variables start at index 3 and are followed by the
|
||||||
* operand stack elements. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = nStack,
|
* operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack.
|
||||||
* frame[3] = nLocal. Local variables and operand stack entries contain abstract types, as defined
|
* Local variables and operand stack entries contain abstract types, as defined in {@link Frame},
|
||||||
* in {@link Frame}, but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND}
|
* but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link
|
||||||
* or {@link Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array
|
* Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry.
|
||||||
* entry.
|
|
||||||
*/
|
*/
|
||||||
private int[] currentFrame;
|
private int[] currentFrame;
|
||||||
|
|
||||||
|
@ -745,9 +744,9 @@ final class MethodWriter extends MethodVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visitFrame(
|
public void visitFrame(
|
||||||
final int type,
|
final int type,
|
||||||
final int nLocal,
|
final int numLocal,
|
||||||
final Object[] local,
|
final Object[] local,
|
||||||
final int nStack,
|
final int numStack,
|
||||||
final Object[] stack) {
|
final Object[] stack) {
|
||||||
if (compute == COMPUTE_ALL_FRAMES) {
|
if (compute == COMPUTE_ALL_FRAMES) {
|
||||||
return;
|
return;
|
||||||
|
@ -760,12 +759,12 @@ final class MethodWriter extends MethodVisitor {
|
||||||
// can't be set if EXPAND_ASM_INSNS is not used).
|
// can't be set if EXPAND_ASM_INSNS is not used).
|
||||||
currentBasicBlock.frame = new CurrentFrame(currentBasicBlock);
|
currentBasicBlock.frame = new CurrentFrame(currentBasicBlock);
|
||||||
currentBasicBlock.frame.setInputFrameFromDescriptor(
|
currentBasicBlock.frame.setInputFrameFromDescriptor(
|
||||||
symbolTable, accessFlags, descriptor, nLocal);
|
symbolTable, accessFlags, descriptor, numLocal);
|
||||||
currentBasicBlock.frame.accept(this);
|
currentBasicBlock.frame.accept(this);
|
||||||
} else {
|
} else {
|
||||||
if (type == Opcodes.F_NEW) {
|
if (type == Opcodes.F_NEW) {
|
||||||
currentBasicBlock.frame.setInputFrameFromApiFormat(
|
currentBasicBlock.frame.setInputFrameFromApiFormat(
|
||||||
symbolTable, nLocal, local, nStack, stack);
|
symbolTable, numLocal, local, numStack, stack);
|
||||||
} else {
|
} else {
|
||||||
// In this case type is equal to F_INSERT by hypothesis, and currentBlock.frame contains
|
// In this case type is equal to F_INSERT by hypothesis, and currentBlock.frame contains
|
||||||
// the stack map frame at the current instruction, computed from the last F_NEW frame
|
// the stack map frame at the current instruction, computed from the last F_NEW frame
|
||||||
|
@ -781,12 +780,12 @@ final class MethodWriter extends MethodVisitor {
|
||||||
symbolTable, accessFlags, descriptor, argumentsSize);
|
symbolTable, accessFlags, descriptor, argumentsSize);
|
||||||
implicitFirstFrame.accept(this);
|
implicitFirstFrame.accept(this);
|
||||||
}
|
}
|
||||||
currentLocals = nLocal;
|
currentLocals = numLocal;
|
||||||
int frameIndex = visitFrameStart(code.length, nLocal, nStack);
|
int frameIndex = visitFrameStart(code.length, numLocal, numStack);
|
||||||
for (int i = 0; i < nLocal; ++i) {
|
for (int i = 0; i < numLocal; ++i) {
|
||||||
currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, local[i]);
|
currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, local[i]);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nStack; ++i) {
|
for (int i = 0; i < numStack; ++i) {
|
||||||
currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, stack[i]);
|
currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, stack[i]);
|
||||||
}
|
}
|
||||||
visitFrameEnd();
|
visitFrameEnd();
|
||||||
|
@ -808,26 +807,26 @@ final class MethodWriter extends MethodVisitor {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Opcodes.F_FULL:
|
case Opcodes.F_FULL:
|
||||||
currentLocals = nLocal;
|
currentLocals = numLocal;
|
||||||
stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(nLocal);
|
stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal);
|
||||||
for (int i = 0; i < nLocal; ++i) {
|
for (int i = 0; i < numLocal; ++i) {
|
||||||
putFrameType(local[i]);
|
putFrameType(local[i]);
|
||||||
}
|
}
|
||||||
stackMapTableEntries.putShort(nStack);
|
stackMapTableEntries.putShort(numStack);
|
||||||
for (int i = 0; i < nStack; ++i) {
|
for (int i = 0; i < numStack; ++i) {
|
||||||
putFrameType(stack[i]);
|
putFrameType(stack[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Opcodes.F_APPEND:
|
case Opcodes.F_APPEND:
|
||||||
currentLocals += nLocal;
|
currentLocals += numLocal;
|
||||||
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + nLocal).putShort(offsetDelta);
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + numLocal).putShort(offsetDelta);
|
||||||
for (int i = 0; i < nLocal; ++i) {
|
for (int i = 0; i < numLocal; ++i) {
|
||||||
putFrameType(local[i]);
|
putFrameType(local[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Opcodes.F_CHOP:
|
case Opcodes.F_CHOP:
|
||||||
currentLocals -= nLocal;
|
currentLocals -= numLocal;
|
||||||
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED - nLocal).putShort(offsetDelta);
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED - numLocal).putShort(offsetDelta);
|
||||||
break;
|
break;
|
||||||
case Opcodes.F_SAME:
|
case Opcodes.F_SAME:
|
||||||
if (offsetDelta < 64) {
|
if (offsetDelta < 64) {
|
||||||
|
@ -855,8 +854,8 @@ final class MethodWriter extends MethodVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
|
if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
|
||||||
relativeStackSize = nStack;
|
relativeStackSize = numStack;
|
||||||
for (int i = 0; i < nStack; ++i) {
|
for (int i = 0; i < numStack; ++i) {
|
||||||
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
||||||
relativeStackSize++;
|
relativeStackSize++;
|
||||||
}
|
}
|
||||||
|
@ -866,7 +865,7 @@ final class MethodWriter extends MethodVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maxStack = Math.max(maxStack, nStack);
|
maxStack = Math.max(maxStack, numStack);
|
||||||
maxLocals = Math.max(maxLocals, currentLocals);
|
maxLocals = Math.max(maxLocals, currentLocals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,7 +1648,7 @@ final class MethodWriter extends MethodVisitor {
|
||||||
code.data[endOffset] = (byte) Opcodes.ATHROW;
|
code.data[endOffset] = (byte) Opcodes.ATHROW;
|
||||||
// Emit a frame for this unreachable block, with no local and a Throwable on the stack
|
// Emit a frame for this unreachable block, with no local and a Throwable on the stack
|
||||||
// (so that the ATHROW could consume this Throwable if it were reachable).
|
// (so that the ATHROW could consume this Throwable if it were reachable).
|
||||||
int frameIndex = visitFrameStart(startOffset, /* nLocal = */ 0, /* nStack = */ 1);
|
int frameIndex = visitFrameStart(startOffset, /* numLocal = */ 0, /* numStack = */ 1);
|
||||||
currentFrame[frameIndex] =
|
currentFrame[frameIndex] =
|
||||||
Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable");
|
Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable");
|
||||||
visitFrameEnd();
|
visitFrameEnd();
|
||||||
|
@ -1821,18 +1820,18 @@ final class MethodWriter extends MethodVisitor {
|
||||||
* Starts the visit of a new stack map frame, stored in {@link #currentFrame}.
|
* Starts the visit of a new stack map frame, stored in {@link #currentFrame}.
|
||||||
*
|
*
|
||||||
* @param offset the bytecode offset of the instruction to which the frame corresponds.
|
* @param offset the bytecode offset of the instruction to which the frame corresponds.
|
||||||
* @param nLocal the number of local variables in the frame.
|
* @param numLocal the number of local variables in the frame.
|
||||||
* @param nStack the number of stack elements in the frame.
|
* @param numStack the number of stack elements in the frame.
|
||||||
* @return the index of the next element to be written in this frame.
|
* @return the index of the next element to be written in this frame.
|
||||||
*/
|
*/
|
||||||
int visitFrameStart(final int offset, final int nLocal, final int nStack) {
|
int visitFrameStart(final int offset, final int numLocal, final int numStack) {
|
||||||
int frameLength = 3 + nLocal + nStack;
|
int frameLength = 3 + numLocal + numStack;
|
||||||
if (currentFrame == null || currentFrame.length < frameLength) {
|
if (currentFrame == null || currentFrame.length < frameLength) {
|
||||||
currentFrame = new int[frameLength];
|
currentFrame = new int[frameLength];
|
||||||
}
|
}
|
||||||
currentFrame[0] = offset;
|
currentFrame[0] = offset;
|
||||||
currentFrame[1] = nLocal;
|
currentFrame[1] = numLocal;
|
||||||
currentFrame[2] = nStack;
|
currentFrame[2] = numStack;
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1865,25 +1864,25 @@ final class MethodWriter extends MethodVisitor {
|
||||||
|
|
||||||
/** Compresses and writes {@link #currentFrame} in a new StackMapTable entry. */
|
/** Compresses and writes {@link #currentFrame} in a new StackMapTable entry. */
|
||||||
private void putFrame() {
|
private void putFrame() {
|
||||||
final int nLocal = currentFrame[1];
|
final int numLocal = currentFrame[1];
|
||||||
final int nStack = currentFrame[2];
|
final int numStack = currentFrame[2];
|
||||||
if (symbolTable.getMajorVersion() < Opcodes.V1_6) {
|
if (symbolTable.getMajorVersion() < Opcodes.V1_6) {
|
||||||
// Generate a StackMap attribute entry, which are always uncompressed.
|
// Generate a StackMap attribute entry, which are always uncompressed.
|
||||||
stackMapTableEntries.putShort(currentFrame[0]).putShort(nLocal);
|
stackMapTableEntries.putShort(currentFrame[0]).putShort(numLocal);
|
||||||
putAbstractTypes(3, 3 + nLocal);
|
putAbstractTypes(3, 3 + numLocal);
|
||||||
stackMapTableEntries.putShort(nStack);
|
stackMapTableEntries.putShort(numStack);
|
||||||
putAbstractTypes(3 + nLocal, 3 + nLocal + nStack);
|
putAbstractTypes(3 + numLocal, 3 + numLocal + numStack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int offsetDelta =
|
final int offsetDelta =
|
||||||
stackMapTableNumberOfEntries == 0
|
stackMapTableNumberOfEntries == 0
|
||||||
? currentFrame[0]
|
? currentFrame[0]
|
||||||
: currentFrame[0] - previousFrame[0] - 1;
|
: currentFrame[0] - previousFrame[0] - 1;
|
||||||
final int previousNlocal = previousFrame[1];
|
final int previousNumlocal = previousFrame[1];
|
||||||
final int nLocalDelta = nLocal - previousNlocal;
|
final int numLocalDelta = numLocal - previousNumlocal;
|
||||||
int type = Frame.FULL_FRAME;
|
int type = Frame.FULL_FRAME;
|
||||||
if (nStack == 0) {
|
if (numStack == 0) {
|
||||||
switch (nLocalDelta) {
|
switch (numLocalDelta) {
|
||||||
case -3:
|
case -3:
|
||||||
case -2:
|
case -2:
|
||||||
case -1:
|
case -1:
|
||||||
|
@ -1901,7 +1900,7 @@ final class MethodWriter extends MethodVisitor {
|
||||||
// Keep the FULL_FRAME type.
|
// Keep the FULL_FRAME type.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (nLocalDelta == 0 && nStack == 1) {
|
} else if (numLocalDelta == 0 && numStack == 1) {
|
||||||
type =
|
type =
|
||||||
offsetDelta < 63
|
offsetDelta < 63
|
||||||
? Frame.SAME_LOCALS_1_STACK_ITEM_FRAME
|
? Frame.SAME_LOCALS_1_STACK_ITEM_FRAME
|
||||||
|
@ -1910,7 +1909,7 @@ final class MethodWriter extends MethodVisitor {
|
||||||
if (type != Frame.FULL_FRAME) {
|
if (type != Frame.FULL_FRAME) {
|
||||||
// Verify if locals are the same as in the previous frame.
|
// Verify if locals are the same as in the previous frame.
|
||||||
int frameIndex = 3;
|
int frameIndex = 3;
|
||||||
for (int i = 0; i < previousNlocal && i < nLocal; i++) {
|
for (int i = 0; i < previousNumlocal && i < numLocal; i++) {
|
||||||
if (currentFrame[frameIndex] != previousFrame[frameIndex]) {
|
if (currentFrame[frameIndex] != previousFrame[frameIndex]) {
|
||||||
type = Frame.FULL_FRAME;
|
type = Frame.FULL_FRAME;
|
||||||
break;
|
break;
|
||||||
|
@ -1924,30 +1923,34 @@ final class MethodWriter extends MethodVisitor {
|
||||||
break;
|
break;
|
||||||
case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME:
|
case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME:
|
||||||
stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta);
|
stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta);
|
||||||
putAbstractTypes(3 + nLocal, 4 + nLocal);
|
putAbstractTypes(3 + numLocal, 4 + numLocal);
|
||||||
break;
|
break;
|
||||||
case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
|
case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
|
||||||
stackMapTableEntries
|
stackMapTableEntries
|
||||||
.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
|
.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
|
||||||
.putShort(offsetDelta);
|
.putShort(offsetDelta);
|
||||||
putAbstractTypes(3 + nLocal, 4 + nLocal);
|
putAbstractTypes(3 + numLocal, 4 + numLocal);
|
||||||
break;
|
break;
|
||||||
case Frame.SAME_FRAME_EXTENDED:
|
case Frame.SAME_FRAME_EXTENDED:
|
||||||
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta);
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta);
|
||||||
break;
|
break;
|
||||||
case Frame.CHOP_FRAME:
|
case Frame.CHOP_FRAME:
|
||||||
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + nLocalDelta).putShort(offsetDelta);
|
stackMapTableEntries
|
||||||
|
.putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta)
|
||||||
|
.putShort(offsetDelta);
|
||||||
break;
|
break;
|
||||||
case Frame.APPEND_FRAME:
|
case Frame.APPEND_FRAME:
|
||||||
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + nLocalDelta).putShort(offsetDelta);
|
stackMapTableEntries
|
||||||
putAbstractTypes(3 + previousNlocal, 3 + nLocal);
|
.putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta)
|
||||||
|
.putShort(offsetDelta);
|
||||||
|
putAbstractTypes(3 + previousNumlocal, 3 + numLocal);
|
||||||
break;
|
break;
|
||||||
case Frame.FULL_FRAME:
|
case Frame.FULL_FRAME:
|
||||||
default:
|
default:
|
||||||
stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(nLocal);
|
stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal);
|
||||||
putAbstractTypes(3, 3 + nLocal);
|
putAbstractTypes(3, 3 + numLocal);
|
||||||
stackMapTableEntries.putShort(nStack);
|
stackMapTableEntries.putShort(numStack);
|
||||||
putAbstractTypes(3 + nLocal, 3 + nLocal + nStack);
|
putAbstractTypes(3 + numLocal, 3 + numLocal + numStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ package org.springframework.asm;
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
* @author Eugene Kuleshov
|
* @author Eugene Kuleshov
|
||||||
*/
|
*/
|
||||||
|
// DontCheck(InterfaceIsType): can't be fixed (for backward binary compatibility).
|
||||||
public interface Opcodes {
|
public interface Opcodes {
|
||||||
|
|
||||||
// ASM API versions.
|
// ASM API versions.
|
||||||
|
|
|
@ -227,6 +227,8 @@ abstract class Symbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the result {@link Type#getArgumentsAndReturnSizes} on {@link #value}.
|
||||||
|
*
|
||||||
* @return the result {@link Type#getArgumentsAndReturnSizes} on {@link #value} (memoized in
|
* @return the result {@link Type#getArgumentsAndReturnSizes} on {@link #value} (memoized in
|
||||||
* {@link #info} for efficiency). This should only be used for {@link
|
* {@link #info} for efficiency). This should only be used for {@link
|
||||||
* #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link
|
* #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link
|
||||||
|
|
|
@ -39,58 +39,6 @@ package org.springframework.asm;
|
||||||
*/
|
*/
|
||||||
final class SymbolTable {
|
final class SymbolTable {
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ClassWriter to which this SymbolTable belongs. This is only used to get access to {@link
|
* 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
|
* ClassWriter#getCommonSuperClass} and to serialize custom attributes with {@link
|
||||||
|
@ -224,7 +172,7 @@ final class SymbolTable {
|
||||||
break;
|
break;
|
||||||
case Symbol.CONSTANT_INTEGER_TAG:
|
case Symbol.CONSTANT_INTEGER_TAG:
|
||||||
case Symbol.CONSTANT_FLOAT_TAG:
|
case Symbol.CONSTANT_FLOAT_TAG:
|
||||||
addConstantInteger(itemIndex, itemTag, classReader.readInt(itemOffset));
|
addConstantIntegerOrFloat(itemIndex, itemTag, classReader.readInt(itemOffset));
|
||||||
break;
|
break;
|
||||||
case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
|
case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
|
||||||
addConstantNameAndType(
|
addConstantNameAndType(
|
||||||
|
@ -234,10 +182,10 @@ final class SymbolTable {
|
||||||
break;
|
break;
|
||||||
case Symbol.CONSTANT_LONG_TAG:
|
case Symbol.CONSTANT_LONG_TAG:
|
||||||
case Symbol.CONSTANT_DOUBLE_TAG:
|
case Symbol.CONSTANT_DOUBLE_TAG:
|
||||||
addConstantLong(itemIndex, itemTag, classReader.readLong(itemOffset));
|
addConstantLongOrDouble(itemIndex, itemTag, classReader.readLong(itemOffset));
|
||||||
break;
|
break;
|
||||||
case Symbol.CONSTANT_UTF8_TAG:
|
case Symbol.CONSTANT_UTF8_TAG:
|
||||||
addConstantUtf8(itemIndex, classReader.readUTF(itemIndex, charBuffer));
|
addConstantUtf8(itemIndex, classReader.readUtf(itemIndex, charBuffer));
|
||||||
break;
|
break;
|
||||||
case Symbol.CONSTANT_METHOD_HANDLE_TAG:
|
case Symbol.CONSTANT_METHOD_HANDLE_TAG:
|
||||||
int memberRefItemOffset =
|
int memberRefItemOffset =
|
||||||
|
@ -331,6 +279,8 @@ final class SymbolTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the ClassReader from which this SymbolTable was constructed.
|
||||||
|
*
|
||||||
* @return the ClassReader from which this SymbolTable was constructed, or {@literal null} if it
|
* @return the ClassReader from which this SymbolTable was constructed, or {@literal null} if it
|
||||||
* was constructed from scratch.
|
* was constructed from scratch.
|
||||||
*/
|
*/
|
||||||
|
@ -338,12 +288,20 @@ final class SymbolTable {
|
||||||
return sourceClassReader;
|
return sourceClassReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the major version of the class to which this symbol table belongs. */
|
/**
|
||||||
|
* 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() {
|
int getMajorVersion() {
|
||||||
return majorVersion;
|
return majorVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the internal name of the class to which this symbol table belongs. */
|
/**
|
||||||
|
* 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() {
|
String getClassName() {
|
||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
|
@ -362,12 +320,20 @@ final class SymbolTable {
|
||||||
return addConstantClass(className).index;
|
return addConstantClass(className).index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the number of items in this symbol table's constant_pool array (plus 1). */
|
/**
|
||||||
|
* 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() {
|
int getConstantPoolCount() {
|
||||||
return constantPoolCount;
|
return constantPoolCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the length in bytes of this symbol table's constant_pool array. */
|
/**
|
||||||
|
* 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() {
|
int getConstantPoolLength() {
|
||||||
return constantPool.length;
|
return constantPool.length;
|
||||||
}
|
}
|
||||||
|
@ -418,6 +384,8 @@ final class SymbolTable {
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the list of entries which can potentially have the given hash code.
|
||||||
|
*
|
||||||
* @param hashCode a {@link Entry#hashCode} value.
|
* @param hashCode a {@link Entry#hashCode} value.
|
||||||
* @return the list of entries which can potentially have the given hash code. The list is stored
|
* @return the list of entries which can potentially have the given hash code. The list is stored
|
||||||
* via the {@link Entry#next} field.
|
* via the {@link Entry#next} field.
|
||||||
|
@ -644,7 +612,7 @@ final class SymbolTable {
|
||||||
* @return a new or already existing Symbol with the given value.
|
* @return a new or already existing Symbol with the given value.
|
||||||
*/
|
*/
|
||||||
Symbol addConstantInteger(final int value) {
|
Symbol addConstantInteger(final int value) {
|
||||||
return addConstantInteger(Symbol.CONSTANT_INTEGER_TAG, value);
|
return addConstantIntegerOrFloat(Symbol.CONSTANT_INTEGER_TAG, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -655,7 +623,7 @@ final class SymbolTable {
|
||||||
* @return a new or already existing Symbol with the given value.
|
* @return a new or already existing Symbol with the given value.
|
||||||
*/
|
*/
|
||||||
Symbol addConstantFloat(final float value) {
|
Symbol addConstantFloat(final float value) {
|
||||||
return addConstantInteger(Symbol.CONSTANT_FLOAT_TAG, Float.floatToRawIntBits(value));
|
return addConstantIntegerOrFloat(Symbol.CONSTANT_FLOAT_TAG, Float.floatToRawIntBits(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -666,7 +634,7 @@ final class SymbolTable {
|
||||||
* @param value an int or float.
|
* @param value an int or float.
|
||||||
* @return a constant pool constant with the given tag and primitive values.
|
* @return a constant pool constant with the given tag and primitive values.
|
||||||
*/
|
*/
|
||||||
private Symbol addConstantInteger(final int tag, final int value) {
|
private Symbol addConstantIntegerOrFloat(final int tag, final int value) {
|
||||||
int hashCode = hash(tag, value);
|
int hashCode = hash(tag, value);
|
||||||
Entry entry = get(hashCode);
|
Entry entry = get(hashCode);
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
|
@ -687,7 +655,7 @@ final class SymbolTable {
|
||||||
* @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}.
|
* @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}.
|
||||||
* @param value an int or float.
|
* @param value an int or float.
|
||||||
*/
|
*/
|
||||||
private void addConstantInteger(final int index, final int tag, final int value) {
|
private void addConstantIntegerOrFloat(final int index, final int tag, final int value) {
|
||||||
add(new Entry(index, tag, value, hash(tag, value)));
|
add(new Entry(index, tag, value, hash(tag, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,7 +667,7 @@ final class SymbolTable {
|
||||||
* @return a new or already existing Symbol with the given value.
|
* @return a new or already existing Symbol with the given value.
|
||||||
*/
|
*/
|
||||||
Symbol addConstantLong(final long value) {
|
Symbol addConstantLong(final long value) {
|
||||||
return addConstantLong(Symbol.CONSTANT_LONG_TAG, value);
|
return addConstantLongOrDouble(Symbol.CONSTANT_LONG_TAG, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -710,7 +678,7 @@ final class SymbolTable {
|
||||||
* @return a new or already existing Symbol with the given value.
|
* @return a new or already existing Symbol with the given value.
|
||||||
*/
|
*/
|
||||||
Symbol addConstantDouble(final double value) {
|
Symbol addConstantDouble(final double value) {
|
||||||
return addConstantLong(Symbol.CONSTANT_DOUBLE_TAG, Double.doubleToRawLongBits(value));
|
return addConstantLongOrDouble(Symbol.CONSTANT_DOUBLE_TAG, Double.doubleToRawLongBits(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -721,7 +689,7 @@ final class SymbolTable {
|
||||||
* @param value a long or double.
|
* @param value a long or double.
|
||||||
* @return a constant pool constant with the given tag and primitive values.
|
* @return a constant pool constant with the given tag and primitive values.
|
||||||
*/
|
*/
|
||||||
private Symbol addConstantLong(final int tag, final long value) {
|
private Symbol addConstantLongOrDouble(final int tag, final long value) {
|
||||||
int hashCode = hash(tag, value);
|
int hashCode = hash(tag, value);
|
||||||
Entry entry = get(hashCode);
|
Entry entry = get(hashCode);
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
|
@ -737,13 +705,14 @@ final class SymbolTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new CONSTANT_Double_info to the constant pool of this symbol table.
|
* 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 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 tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}.
|
||||||
* @param value a long or double.
|
* @param value a long or double.
|
||||||
*/
|
*/
|
||||||
private void addConstantLong(final int index, final int tag, final long value) {
|
private void addConstantLongOrDouble(final int index, final int tag, final long value) {
|
||||||
add(new Entry(index, tag, value, hash(tag, value)));
|
add(new Entry(index, tag, value, hash(tag, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,6 +1118,8 @@ final class SymbolTable {
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the type table element whose index is given.
|
||||||
|
*
|
||||||
* @param typeIndex a type table index.
|
* @param typeIndex a type table index.
|
||||||
* @return the type table element whose index is given.
|
* @return the type table element whose index is given.
|
||||||
*/
|
*/
|
||||||
|
@ -1172,7 +1143,7 @@ final class SymbolTable {
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
}
|
}
|
||||||
return addType(new Entry(typeCount, Symbol.TYPE_TAG, value, hashCode));
|
return addTypeInternal(new Entry(typeCount, Symbol.TYPE_TAG, value, hashCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1196,7 +1167,7 @@ final class SymbolTable {
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
}
|
}
|
||||||
return addType(
|
return addTypeInternal(
|
||||||
new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode));
|
new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,7 +1208,7 @@ final class SymbolTable {
|
||||||
* @return the index in {@link #typeTable} where the given type was added, which is also equal to
|
* @return the index in {@link #typeTable} where the given type was added, which is also equal to
|
||||||
* entry's index by hypothesis.
|
* entry's index by hypothesis.
|
||||||
*/
|
*/
|
||||||
private int addType(final Entry entry) {
|
private int addTypeInternal(final Entry entry) {
|
||||||
if (typeTable == null) {
|
if (typeTable == null) {
|
||||||
typeTable = new Entry[16];
|
typeTable = new Entry[16];
|
||||||
}
|
}
|
||||||
|
@ -1292,4 +1263,56 @@ final class SymbolTable {
|
||||||
final int value4) {
|
final int value4) {
|
||||||
return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode() * 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,10 +146,6 @@ public class Type {
|
||||||
*/
|
*/
|
||||||
private final int valueEnd;
|
private final int valueEnd;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
// Constructors
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a reference type.
|
* Constructs a reference type.
|
||||||
*
|
*
|
||||||
|
@ -167,6 +163,10 @@ public class Type {
|
||||||
this.valueEnd = valueEnd;
|
this.valueEnd = valueEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
// Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc.
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Type} corresponding to the given type descriptor.
|
* Returns the {@link Type} corresponding to the given type descriptor.
|
||||||
*
|
*
|
||||||
|
@ -174,40 +174,7 @@ public class Type {
|
||||||
* @return the {@link Type} corresponding to the given type descriptor.
|
* @return the {@link Type} corresponding to the given type descriptor.
|
||||||
*/
|
*/
|
||||||
public static Type getType(final String typeDescriptor) {
|
public static Type getType(final String typeDescriptor) {
|
||||||
return getType(typeDescriptor, 0, typeDescriptor.length());
|
return getTypeInternal(typeDescriptor, 0, typeDescriptor.length());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link Type} corresponding to the given internal name.
|
|
||||||
*
|
|
||||||
* @param internalName an internal name.
|
|
||||||
* @return the {@link Type} corresponding to the given internal name.
|
|
||||||
*/
|
|
||||||
public static Type getObjectType(final String internalName) {
|
|
||||||
return new Type(
|
|
||||||
internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link Type} corresponding to the given method descriptor. Equivalent to <code>
|
|
||||||
* Type.getType(methodDescriptor)</code>.
|
|
||||||
*
|
|
||||||
* @param methodDescriptor a method descriptor.
|
|
||||||
* @return the {@link Type} corresponding to the given method descriptor.
|
|
||||||
*/
|
|
||||||
public static Type getMethodType(final String methodDescriptor) {
|
|
||||||
return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the method {@link Type} corresponding to the given argument and return types.
|
|
||||||
*
|
|
||||||
* @param returnType the return type of the method.
|
|
||||||
* @param argumentTypes the argument types of the method.
|
|
||||||
* @return the method {@link Type} corresponding to the given argument and return types.
|
|
||||||
*/
|
|
||||||
public static Type getMethodType(final Type returnType, final Type... argumentTypes) {
|
|
||||||
return getType(getMethodDescriptor(returnType, argumentTypes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -264,6 +231,60 @@ public class Type {
|
||||||
return getType(getMethodDescriptor(method));
|
return getType(getMethodDescriptor(method));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of the elements of this array type. This method should only be used for an
|
||||||
|
* array type.
|
||||||
|
*
|
||||||
|
* @return Returns the type of the elements of this array type.
|
||||||
|
*/
|
||||||
|
public Type getElementType() {
|
||||||
|
final int numDimensions = getDimensions();
|
||||||
|
return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Type} corresponding to the given internal name.
|
||||||
|
*
|
||||||
|
* @param internalName an internal name.
|
||||||
|
* @return the {@link Type} corresponding to the given internal name.
|
||||||
|
*/
|
||||||
|
public static Type getObjectType(final String internalName) {
|
||||||
|
return new Type(
|
||||||
|
internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Type} corresponding to the given method descriptor. Equivalent to <code>
|
||||||
|
* Type.getType(methodDescriptor)</code>.
|
||||||
|
*
|
||||||
|
* @param methodDescriptor a method descriptor.
|
||||||
|
* @return the {@link Type} corresponding to the given method descriptor.
|
||||||
|
*/
|
||||||
|
public static Type getMethodType(final String methodDescriptor) {
|
||||||
|
return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the method {@link Type} corresponding to the given argument and return types.
|
||||||
|
*
|
||||||
|
* @param returnType the return type of the method.
|
||||||
|
* @param argumentTypes the argument types of the method.
|
||||||
|
* @return the method {@link Type} corresponding to the given argument and return types.
|
||||||
|
*/
|
||||||
|
public static Type getMethodType(final Type returnType, final Type... argumentTypes) {
|
||||||
|
return getType(getMethodDescriptor(returnType, argumentTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the argument types of methods of this type. This method should only be used for method
|
||||||
|
* types.
|
||||||
|
*
|
||||||
|
* @return the argument types of methods of this type.
|
||||||
|
*/
|
||||||
|
public Type[] getArgumentTypes() {
|
||||||
|
return getArgumentTypes(getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Type} values corresponding to the argument types of the given method
|
* Returns the {@link Type} values corresponding to the argument types of the given method
|
||||||
* descriptor.
|
* descriptor.
|
||||||
|
@ -307,7 +328,7 @@ public class Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argumentTypes[currentArgumentTypeIndex++] =
|
argumentTypes[currentArgumentTypeIndex++] =
|
||||||
getType(methodDescriptor, currentArgumentTypeOffset, currentOffset);
|
getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
|
||||||
}
|
}
|
||||||
return argumentTypes;
|
return argumentTypes;
|
||||||
}
|
}
|
||||||
|
@ -327,6 +348,16 @@ public class Type {
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the return type of methods of this type. This method should only be used for method
|
||||||
|
* types.
|
||||||
|
*
|
||||||
|
* @return the return type of methods of this type.
|
||||||
|
*/
|
||||||
|
public Type getReturnType() {
|
||||||
|
return getReturnType(getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Type} corresponding to the return type of the given method descriptor.
|
* Returns the {@link Type} corresponding to the return type of the given method descriptor.
|
||||||
*
|
*
|
||||||
|
@ -347,7 +378,7 @@ public class Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getType(methodDescriptor, currentOffset + 1, methodDescriptor.length());
|
return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -360,47 +391,6 @@ public class Type {
|
||||||
return getType(method.getReturnType());
|
return getType(method.getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the size of the arguments and of the return value of a method.
|
|
||||||
*
|
|
||||||
* @param methodDescriptor a method descriptor.
|
|
||||||
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
|
||||||
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
|
||||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
|
||||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
|
||||||
*/
|
|
||||||
public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
|
|
||||||
int argumentsSize = 1;
|
|
||||||
// Skip the first character, which is always a '('.
|
|
||||||
int currentOffset = 1;
|
|
||||||
int currentChar = methodDescriptor.charAt(currentOffset);
|
|
||||||
// Parse the argument types and compute their size, one at a each loop iteration.
|
|
||||||
while (currentChar != ')') {
|
|
||||||
if (currentChar == 'J' || currentChar == 'D') {
|
|
||||||
currentOffset++;
|
|
||||||
argumentsSize += 2;
|
|
||||||
} else {
|
|
||||||
while (methodDescriptor.charAt(currentOffset) == '[') {
|
|
||||||
currentOffset++;
|
|
||||||
}
|
|
||||||
if (methodDescriptor.charAt(currentOffset++) == 'L') {
|
|
||||||
while (methodDescriptor.charAt(currentOffset++) != ';') {
|
|
||||||
// Skip the argument descriptor content.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argumentsSize += 1;
|
|
||||||
}
|
|
||||||
currentChar = methodDescriptor.charAt(currentOffset);
|
|
||||||
}
|
|
||||||
currentChar = methodDescriptor.charAt(currentOffset + 1);
|
|
||||||
if (currentChar == 'V') {
|
|
||||||
return argumentsSize << 2;
|
|
||||||
} else {
|
|
||||||
int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1;
|
|
||||||
return argumentsSize << 2 | returnSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Type} corresponding to the given field or method descriptor.
|
* Returns the {@link Type} corresponding to the given field or method descriptor.
|
||||||
*
|
*
|
||||||
|
@ -411,7 +401,7 @@ public class Type {
|
||||||
* descriptorBuffer.
|
* descriptorBuffer.
|
||||||
* @return the {@link Type} corresponding to the given type descriptor.
|
* @return the {@link Type} corresponding to the given type descriptor.
|
||||||
*/
|
*/
|
||||||
private static Type getType(
|
private static Type getTypeInternal(
|
||||||
final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
|
final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
|
||||||
switch (descriptorBuffer.charAt(descriptorBegin)) {
|
switch (descriptorBuffer.charAt(descriptorBegin)) {
|
||||||
case 'V':
|
case 'V':
|
||||||
|
@ -444,45 +434,9 @@ public class Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Accessors
|
// Methods to get class names, internal names or descriptors.
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the sort of this type.
|
|
||||||
*
|
|
||||||
* @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link
|
|
||||||
* #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or
|
|
||||||
* {@link #METHOD}.
|
|
||||||
*/
|
|
||||||
public int getSort() {
|
|
||||||
return sort == INTERNAL ? OBJECT : sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of dimensions of this array type. This method should only be used for an
|
|
||||||
* array type.
|
|
||||||
*
|
|
||||||
* @return the number of dimensions of this array type.
|
|
||||||
*/
|
|
||||||
public int getDimensions() {
|
|
||||||
int numDimensions = 1;
|
|
||||||
while (valueBuffer.charAt(valueBegin + numDimensions) == '[') {
|
|
||||||
numDimensions++;
|
|
||||||
}
|
|
||||||
return numDimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the type of the elements of this array type. This method should only be used for an
|
|
||||||
* array type.
|
|
||||||
*
|
|
||||||
* @return Returns the type of the elements of this array type.
|
|
||||||
*/
|
|
||||||
public Type getElementType() {
|
|
||||||
final int numDimensions = getDimensions();
|
|
||||||
return getType(valueBuffer, valueBegin + numDimensions, valueEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the binary name of the class corresponding to this type. This method must not be used
|
* Returns the binary name of the class corresponding to this type. This method must not be used
|
||||||
* on method types.
|
* on method types.
|
||||||
|
@ -535,42 +489,16 @@ public class Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the argument types of methods of this type. This method should only be used for method
|
* Returns the internal name of the given class. The internal name of a class is its fully
|
||||||
* types.
|
* qualified name, as returned by Class.getName(), where '.' are replaced by '/'.
|
||||||
*
|
*
|
||||||
* @return the argument types of methods of this type.
|
* @param clazz an object or array class.
|
||||||
|
* @return the internal name of the given class.
|
||||||
*/
|
*/
|
||||||
public Type[] getArgumentTypes() {
|
public static String getInternalName(final Class<?> clazz) {
|
||||||
return getArgumentTypes(getDescriptor());
|
return clazz.getName().replace('.', '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the return type of methods of this type. This method should only be used for method
|
|
||||||
* types.
|
|
||||||
*
|
|
||||||
* @return the return type of methods of this type.
|
|
||||||
*/
|
|
||||||
public Type getReturnType() {
|
|
||||||
return getReturnType(getDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the arguments and of the return value of methods of this type. This method
|
|
||||||
* should only be used for method types.
|
|
||||||
*
|
|
||||||
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
|
||||||
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
|
||||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
|
||||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
|
||||||
*/
|
|
||||||
public int getArgumentsAndReturnSizes() {
|
|
||||||
return getArgumentsAndReturnSizes(getDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
// Conversion to type descriptors
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the descriptor corresponding to this type.
|
* Returns the descriptor corresponding to this type.
|
||||||
*
|
*
|
||||||
|
@ -590,6 +518,34 @@ public class Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the descriptor corresponding to the given class.
|
||||||
|
*
|
||||||
|
* @param clazz an object class, a primitive class or an array class.
|
||||||
|
* @return the descriptor corresponding to the given class.
|
||||||
|
*/
|
||||||
|
public static String getDescriptor(final Class<?> clazz) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
appendDescriptor(clazz, stringBuilder);
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the descriptor corresponding to the given constructor.
|
||||||
|
*
|
||||||
|
* @param constructor a {@link Constructor} object.
|
||||||
|
* @return the descriptor of the given constructor.
|
||||||
|
*/
|
||||||
|
public static String getConstructorDescriptor(final Constructor<?> constructor) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append('(');
|
||||||
|
Class<?>[] parameters = constructor.getParameterTypes();
|
||||||
|
for (int i = 0; i < parameters.length; ++i) {
|
||||||
|
appendDescriptor(parameters[i], stringBuilder);
|
||||||
|
}
|
||||||
|
return stringBuilder.append(")V").toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the descriptor corresponding to the given argument and return types.
|
* Returns the descriptor corresponding to the given argument and return types.
|
||||||
*
|
*
|
||||||
|
@ -608,6 +564,24 @@ public class Type {
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the descriptor corresponding to the given method.
|
||||||
|
*
|
||||||
|
* @param method a {@link Method} object.
|
||||||
|
* @return the descriptor of the given method.
|
||||||
|
*/
|
||||||
|
public static String getMethodDescriptor(final Method method) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append('(');
|
||||||
|
Class<?>[] parameters = method.getParameterTypes();
|
||||||
|
for (int i = 0; i < parameters.length; ++i) {
|
||||||
|
appendDescriptor(parameters[i], stringBuilder);
|
||||||
|
}
|
||||||
|
stringBuilder.append(')');
|
||||||
|
appendDescriptor(method.getReturnType(), stringBuilder);
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the descriptor corresponding to this type to the given string buffer.
|
* Appends the descriptor corresponding to this type to the given string buffer.
|
||||||
*
|
*
|
||||||
|
@ -625,75 +599,13 @@ public class Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
// Direct conversion from classes to type descriptors,
|
|
||||||
// without intermediate Type objects
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the internal name of the given class. The internal name of a class is its fully
|
|
||||||
* qualified name, as returned by Class.getName(), where '.' are replaced by '/'.
|
|
||||||
*
|
|
||||||
* @param clazz an object or array class.
|
|
||||||
* @return the internal name of the given class.
|
|
||||||
*/
|
|
||||||
public static String getInternalName(final Class<?> clazz) {
|
|
||||||
return clazz.getName().replace('.', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the descriptor corresponding to the given class.
|
|
||||||
*
|
|
||||||
* @param clazz an object class, a primitive class or an array class.
|
|
||||||
* @return the descriptor corresponding to the given class.
|
|
||||||
*/
|
|
||||||
public static String getDescriptor(final Class<?> clazz) {
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
appendDescriptor(stringBuilder, clazz);
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the descriptor corresponding to the given constructor.
|
|
||||||
*
|
|
||||||
* @param constructor a {@link Constructor} object.
|
|
||||||
* @return the descriptor of the given constructor.
|
|
||||||
*/
|
|
||||||
public static String getConstructorDescriptor(final Constructor<?> constructor) {
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
stringBuilder.append('(');
|
|
||||||
Class<?>[] parameters = constructor.getParameterTypes();
|
|
||||||
for (int i = 0; i < parameters.length; ++i) {
|
|
||||||
appendDescriptor(stringBuilder, parameters[i]);
|
|
||||||
}
|
|
||||||
return stringBuilder.append(")V").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the descriptor corresponding to the given method.
|
|
||||||
*
|
|
||||||
* @param method a {@link Method} object.
|
|
||||||
* @return the descriptor of the given method.
|
|
||||||
*/
|
|
||||||
public static String getMethodDescriptor(final Method method) {
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
stringBuilder.append('(');
|
|
||||||
Class<?>[] parameters = method.getParameterTypes();
|
|
||||||
for (int i = 0; i < parameters.length; ++i) {
|
|
||||||
appendDescriptor(stringBuilder, parameters[i]);
|
|
||||||
}
|
|
||||||
stringBuilder.append(')');
|
|
||||||
appendDescriptor(stringBuilder, method.getReturnType());
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the descriptor of the given class to the given string builder.
|
* Appends the descriptor of the given class to the given string builder.
|
||||||
*
|
*
|
||||||
* @param stringBuilder the string builder to which the descriptor must be appended.
|
|
||||||
* @param clazz the class whose descriptor must be computed.
|
* @param clazz the class whose descriptor must be computed.
|
||||||
|
* @param stringBuilder the string builder to which the descriptor must be appended.
|
||||||
*/
|
*/
|
||||||
private static void appendDescriptor(final StringBuilder stringBuilder, final Class<?> clazz) {
|
private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) {
|
||||||
Class<?> currentClass = clazz;
|
Class<?> currentClass = clazz;
|
||||||
while (currentClass.isArray()) {
|
while (currentClass.isArray()) {
|
||||||
stringBuilder.append('[');
|
stringBuilder.append('[');
|
||||||
|
@ -736,9 +648,34 @@ public class Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Corresponding size and opcodes
|
// Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor.
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sort of this type.
|
||||||
|
*
|
||||||
|
* @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link
|
||||||
|
* #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or
|
||||||
|
* {@link #METHOD}.
|
||||||
|
*/
|
||||||
|
public int getSort() {
|
||||||
|
return sort == INTERNAL ? OBJECT : sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of dimensions of this array type. This method should only be used for an
|
||||||
|
* array type.
|
||||||
|
*
|
||||||
|
* @return the number of dimensions of this array type.
|
||||||
|
*/
|
||||||
|
public int getDimensions() {
|
||||||
|
int numDimensions = 1;
|
||||||
|
while (valueBuffer.charAt(valueBegin + numDimensions) == '[') {
|
||||||
|
numDimensions++;
|
||||||
|
}
|
||||||
|
return numDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of values of this type. This method must not be used for method types.
|
* Returns the size of values of this type. This method must not be used for method types.
|
||||||
*
|
*
|
||||||
|
@ -767,6 +704,60 @@ public class Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the arguments and of the return value of methods of this type. This method
|
||||||
|
* should only be used for method types.
|
||||||
|
*
|
||||||
|
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
||||||
|
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
||||||
|
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||||
|
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||||
|
*/
|
||||||
|
public int getArgumentsAndReturnSizes() {
|
||||||
|
return getArgumentsAndReturnSizes(getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the size of the arguments and of the return value of a method.
|
||||||
|
*
|
||||||
|
* @param methodDescriptor a method descriptor.
|
||||||
|
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
||||||
|
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
||||||
|
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||||
|
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||||
|
*/
|
||||||
|
public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
|
||||||
|
int argumentsSize = 1;
|
||||||
|
// Skip the first character, which is always a '('.
|
||||||
|
int currentOffset = 1;
|
||||||
|
int currentChar = methodDescriptor.charAt(currentOffset);
|
||||||
|
// Parse the argument types and compute their size, one at a each loop iteration.
|
||||||
|
while (currentChar != ')') {
|
||||||
|
if (currentChar == 'J' || currentChar == 'D') {
|
||||||
|
currentOffset++;
|
||||||
|
argumentsSize += 2;
|
||||||
|
} else {
|
||||||
|
while (methodDescriptor.charAt(currentOffset) == '[') {
|
||||||
|
currentOffset++;
|
||||||
|
}
|
||||||
|
if (methodDescriptor.charAt(currentOffset++) == 'L') {
|
||||||
|
while (methodDescriptor.charAt(currentOffset++) != ';') {
|
||||||
|
// Skip the argument descriptor content.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argumentsSize += 1;
|
||||||
|
}
|
||||||
|
currentChar = methodDescriptor.charAt(currentOffset);
|
||||||
|
}
|
||||||
|
currentChar = methodDescriptor.charAt(currentOffset + 1);
|
||||||
|
if (currentChar == 'V') {
|
||||||
|
return argumentsSize << 2;
|
||||||
|
} else {
|
||||||
|
int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1;
|
||||||
|
return argumentsSize << 2 | returnSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for
|
* Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for
|
||||||
* method types.
|
* method types.
|
||||||
|
@ -841,7 +832,7 @@ public class Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Equals, hashCode and toString
|
// Equals, hashCode and toString.
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue