Upgrade to ASM 7.1

Closes gh-22503
This commit is contained in:
Juergen Hoeller 2019-03-08 09:58:16 +01:00
parent a06ab6d0ad
commit f5248ff13f
16 changed files with 841 additions and 541 deletions

View File

@ -43,7 +43,10 @@ public abstract class AnnotationVisitor {
*/
protected final int api;
/** The annotation visitor to which this visitor must delegate method calls. May be null. */
/**
* The annotation visitor to which this visitor must delegate method calls. May be {@literal
* null}.
*/
protected AnnotationVisitor av;
/**
@ -62,11 +65,11 @@ public abstract class AnnotationVisitor {
* @param api the ASM API version implemented by this visitor. Must be one of {@link
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
* @param annotationVisitor the annotation visitor to which this visitor must delegate method
* calls. May be null.
* calls. May be {@literal null}.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
throw new IllegalArgumentException();
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
throw new IllegalArgumentException("Unsupported api " + api);
}
this.api = api;
this.av = annotationVisitor;

View File

@ -91,7 +91,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private AnnotationWriter nextAnnotation;
// -----------------------------------------------------------------------------------------------
// Constructors
// Constructors and factories
// -----------------------------------------------------------------------------------------------
/**
@ -104,8 +104,8 @@ final class AnnotationWriter extends AnnotationVisitor {
* the visited content must be stored. This ByteVector must already contain all the fields of
* the structure except the last one (the element_value_pairs array).
* @param previousAnnotation the previously visited annotation of the
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
* other cases (e.g. nested or array annotations).
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
* {@literal null} in other cases (e.g. nested or array annotations).
*/
AnnotationWriter(
final SymbolTable symbolTable,
@ -125,21 +125,61 @@ final class AnnotationWriter extends AnnotationVisitor {
}
/**
* Constructs a new {@link AnnotationWriter} using named values.
* Creates a new {@link AnnotationWriter} using named values.
*
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
* @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
* the visited content must be stored. This ByteVector must already contain all the fields of
* the structure except the last one (the element_value_pairs array).
* @param descriptor the class descriptor of the annotation class.
* @param previousAnnotation the previously visited annotation of the
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
* other cases (e.g. nested or array annotations).
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
* {@literal null} in other cases (e.g. nested or array annotations).
* @return a new {@link AnnotationWriter} for the given annotation descriptor.
*/
AnnotationWriter(
static AnnotationWriter create(
final SymbolTable symbolTable,
final ByteVector annotation,
final String descriptor,
final AnnotationWriter previousAnnotation) {
this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
return new AnnotationWriter(
symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
}
/**
* Creates a new {@link AnnotationWriter} using named values.
*
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
* TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
* {@link TypeReference}.
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
* 'typeRef' as a whole.
* @param descriptor the class descriptor of the annotation class.
* @param previousAnnotation the previously visited annotation of the
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
* {@literal null} in other cases (e.g. nested or array annotations).
* @return a new {@link AnnotationWriter} for the given type annotation reference and descriptor.
*/
static AnnotationWriter create(
final SymbolTable symbolTable,
final int typeRef,
final TypePath typePath,
final String descriptor,
final AnnotationWriter previousAnnotation) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.putTarget(typeRef, typeAnnotation);
TypePath.put(typePath, typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
return new AnnotationWriter(
symbolTable, /* useNamedValues = */ true, typeAnnotation, previousAnnotation);
}
// -----------------------------------------------------------------------------------------------
@ -244,7 +284,7 @@ final class AnnotationWriter extends AnnotationVisitor {
}
// Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
return new AnnotationWriter(symbolTable, annotation, null);
return new AnnotationWriter(symbolTable, /* useNamedValues = */ true, annotation, null);
}
@Override
@ -284,7 +324,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* and all its <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the attribute name
* to the constant pool of the class (if not null).
*
* @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null.
* @param attributeName one of "Runtime[In]Visible[Type]Annotations", or {@literal null}.
* @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this
* annotation and all its predecessors. This includes the size of the attribute_name_index and
* attribute_length fields.
@ -303,6 +343,56 @@ final class AnnotationWriter extends AnnotationVisitor {
return attributeSize;
}
/**
* Returns the size of the Runtime[In]Visible[Type]Annotations attributes containing the given
* annotations and all their <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the
* attribute names to the constant pool of the class (if not null).
*
* @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or
* class. The previous ones can be accessed with the {@link #previousAnnotation} field. May be
* {@literal null}.
* @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field,
* method or class. The previous ones can be accessed with the {@link #previousAnnotation}
* field. May be {@literal null}.
* @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a
* field, method or class. The previous ones can be accessed with the {@link
* #previousAnnotation} field. May be {@literal null}.
* @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a
* field, method or class field. The previous ones can be accessed with the {@link
* #previousAnnotation} field. May be {@literal null}.
* @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing the
* given annotations and all their predecessors. This includes the size of the
* attribute_name_index and attribute_length fields.
*/
static int computeAnnotationsSize(
final AnnotationWriter lastRuntimeVisibleAnnotation,
final AnnotationWriter lastRuntimeInvisibleAnnotation,
final AnnotationWriter lastRuntimeVisibleTypeAnnotation,
final AnnotationWriter lastRuntimeInvisibleTypeAnnotation) {
int size = 0;
if (lastRuntimeVisibleAnnotation != null) {
size +=
lastRuntimeVisibleAnnotation.computeAnnotationsSize(
Constants.RUNTIME_VISIBLE_ANNOTATIONS);
}
if (lastRuntimeInvisibleAnnotation != null) {
size +=
lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
size +=
lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
size +=
lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
}
return size;
}
/**
* Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its
* <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector. Annotations are
@ -335,6 +425,51 @@ final class AnnotationWriter extends AnnotationVisitor {
}
}
/**
* Puts the Runtime[In]Visible[Type]Annotations attributes containing the given annotations and
* all their <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector.
* Annotations are put in the same order they have been visited.
*
* @param symbolTable where the constants used in the AnnotationWriter instances are stored.
* @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or
* class. The previous ones can be accessed with the {@link #previousAnnotation} field. May be
* {@literal null}.
* @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field,
* method or class. The previous ones can be accessed with the {@link #previousAnnotation}
* field. May be {@literal null}.
* @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a
* field, method or class. The previous ones can be accessed with the {@link
* #previousAnnotation} field. May be {@literal null}.
* @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a
* field, method or class field. The previous ones can be accessed with the {@link
* #previousAnnotation} field. May be {@literal null}.
* @param output where the attributes must be put.
*/
static void putAnnotations(
final SymbolTable symbolTable,
final AnnotationWriter lastRuntimeVisibleAnnotation,
final AnnotationWriter lastRuntimeInvisibleAnnotation,
final AnnotationWriter lastRuntimeVisibleTypeAnnotation,
final AnnotationWriter lastRuntimeInvisibleTypeAnnotation,
final ByteVector output) {
if (lastRuntimeVisibleAnnotation != null) {
lastRuntimeVisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
}
if (lastRuntimeInvisibleAnnotation != null) {
lastRuntimeInvisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
lastRuntimeVisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
lastRuntimeInvisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
}
}
/**
* Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the
* annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the

View File

@ -28,7 +28,7 @@
package org.springframework.asm;
/**
* A non standard class, field, method or code attribute, as defined in the Java Virtual Machine
* A non standard class, field, method or Code attribute, as defined in the Java Virtual Machine
* Specification (JVMS).
*
* @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS
@ -52,7 +52,7 @@ public class Attribute {
/**
* The next attribute in this attribute list (Attribute instances can be linked via this field to
* store a list of class, field, method or code attributes). May be {@literal null}.
* store a list of class, field, method or Code attributes). May be {@literal null}.
*/
Attribute nextAttribute;
@ -80,9 +80,9 @@ public class Attribute {
}
/**
* Returns {@literal true} if this type of attribute is a code attribute.
* Returns {@literal true} if this type of attribute is a Code attribute.
*
* @return {@literal true} if this type of attribute is a code attribute.
* @return {@literal true} if this type of attribute is a Code attribute.
*/
public boolean isCodeAttribute() {
return false;
@ -92,7 +92,7 @@ public class Attribute {
* Returns the labels corresponding to this attribute.
*
* @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
* a code attribute that contains labels.
* a Code attribute that contains labels.
*/
protected Label[] getLabels() {
return new Label[0];
@ -104,18 +104,18 @@ public class Attribute {
* ClassReader.
*
* @param classReader the class that contains the attribute to be read.
* @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
* 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
* @param offset index of the first byte of the attribute's content in {@link ClassReader}. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param length the length of the attribute's content (excluding the 6 attribute header bytes).
* @param charBuffer the buffer to be used to call the ClassReader methods requiring a
* 'charBuffer' parameter.
* @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
* in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
* in {@link ClassReader}, or -1 if the attribute to be read is not a Code attribute. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
* is not a code attribute.
* is not a Code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
*/
protected Attribute read(
@ -127,7 +127,7 @@ public class Attribute {
final Label[] labels) {
Attribute attribute = new Attribute(type);
attribute.content = new byte[length];
System.arraycopy(classReader.b, offset, attribute.content, 0, length);
System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
return attribute;
}
@ -138,16 +138,16 @@ public class Attribute {
*
* @param classWriter the class to which this attribute must be added. This parameter can be used
* to add the items that corresponds to this attribute to the constant pool of this class.
* @param code the bytecode of the method corresponding to this code attribute, or {@literal null}
* if this attribute is not a code attribute. Corresponds to the 'code' field of the Code
* @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
* if this attribute is not a Code attribute. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to this code
* attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length'
* attribute, or 0 if this attribute is not a Code attribute. Corresponds to the 'code_length'
* field of the Code attribute.
* @param maxStack the maximum stack size of the method corresponding to this code attribute, or
* -1 if this attribute is not a code attribute.
* @param maxStack the maximum stack size of the method corresponding to this Code attribute, or
* -1 if this attribute is not a Code attribute.
* @param maxLocals the maximum number of local variables of the method corresponding to this code
* attribute, or -1 if this attribute is not a code attribute.
* attribute, or -1 if this attribute is not a Code attribute.
* @return the byte array form of this attribute.
*/
protected ByteVector write(
@ -197,16 +197,16 @@ public class Attribute {
* attribute_length) per attribute. Also adds the attribute type names to the constant pool.
*
* @param symbolTable where the constants used in the attributes must be stored.
* @param code the bytecode of the method corresponding to these code attributes, or {@literal
* null} if they are not code attributes. Corresponds to the 'code' field of the Code
* @param code the bytecode of the method corresponding to these Code attributes, or {@literal
* null} if they are not Code attributes. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to these code
* attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
* attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
* the Code attribute.
* @param maxStack the maximum stack size of the method corresponding to these code attributes, or
* -1 if they are not code attributes.
* @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
* -1 if they are not Code attributes.
* @param maxLocals the maximum number of local variables of the method corresponding to these
* code attributes, or -1 if they are not code attribute.
* Code attributes, or -1 if they are not Code attribute.
* @return the size of all the attributes in this attribute list. This size includes the size of
* the attribute headers.
*/
@ -227,6 +227,42 @@ public class Attribute {
return size;
}
/**
* Returns the total size in bytes of all the attributes that correspond to the given field,
* method or class access flags and signature. This size includes the 6 header bytes
* (attribute_name_index and attribute_length) per attribute. Also adds the attribute type names
* to the constant pool.
*
* @param symbolTable where the constants used in the attributes must be stored.
* @param accessFlags some field, method or class access flags.
* @param signatureIndex the constant pool index of a field, method of class signature.
* @return the size of all the attributes in bytes. This size includes the size of the attribute
* headers.
*/
static int computeAttributesSize(
final SymbolTable symbolTable, final int accessFlags, final int signatureIndex) {
int size = 0;
// Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
&& symbolTable.getMajorVersion() < Opcodes.V1_5) {
// Synthetic attributes always use 6 bytes.
symbolTable.addConstantUtf8(Constants.SYNTHETIC);
size += 6;
}
if (signatureIndex != 0) {
// Signature attributes always use 8 bytes.
symbolTable.addConstantUtf8(Constants.SIGNATURE);
size += 8;
}
// ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
// Deprecated attributes always use 6 bytes.
symbolTable.addConstantUtf8(Constants.DEPRECATED);
size += 6;
}
return size;
}
/**
* Puts all the attributes of the attribute list that begins with this attribute, in the given
* byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
@ -249,16 +285,16 @@ public class Attribute {
* attribute.
*
* @param symbolTable where the constants used in the attributes must be stored.
* @param code the bytecode of the method corresponding to these code attributes, or {@literal
* null} if they are not code attributes. Corresponds to the 'code' field of the Code
* @param code the bytecode of the method corresponding to these Code attributes, or {@literal
* null} if they are not Code attributes. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to these code
* attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
* attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
* the Code attribute.
* @param maxStack the maximum stack size of the method corresponding to these code attributes, or
* -1 if they are not code attributes.
* @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
* -1 if they are not Code attributes.
* @param maxLocals the maximum number of local variables of the method corresponding to these
* code attributes, or -1 if they are not code attribute.
* Code attributes, or -1 if they are not Code attribute.
* @param output where the attributes must be written.
*/
final void putAttributes(
@ -280,6 +316,37 @@ public class Attribute {
}
}
/**
* Puts all the attributes that correspond to the given field, method or class access flags and
* signature, in the given byte vector. This includes the 6 header bytes (attribute_name_index and
* attribute_length) per attribute.
*
* @param symbolTable where the constants used in the attributes must be stored.
* @param accessFlags some field, method or class access flags.
* @param signatureIndex the constant pool index of a field, method of class signature.
* @param output where the attributes must be written.
*/
static void putAttributes(
final SymbolTable symbolTable,
final int accessFlags,
final int signatureIndex,
final ByteVector output) {
// Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
&& symbolTable.getMajorVersion() < Opcodes.V1_5) {
output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
}
if (signatureIndex != 0) {
output
.putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
.putInt(2)
.putShort(signatureIndex);
}
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
}
}
/** A set of attribute prototypes (attributes with the same type are considered equal). */
static final class Set {

View File

@ -90,6 +90,16 @@ public class ClassReader {
/** The size of the temporary byte array used to read class input streams chunk by chunk. */
private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
/**
* A byte array containing the JVMS ClassFile structure to be parsed.
*
* @deprecated Use {@link #readByte(int)} and the other read methods instead. This field will
* eventually be deleted.
*/
@Deprecated
// DontCheck(MemberName): can't be renamed (for backward binary compatibility).
public final byte[] b;
/**
* A byte array containing the JVMS ClassFile structure to be parsed. <i>The content of this array
* must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
@ -99,13 +109,13 @@ public class ClassReader {
* necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
* ClassFile element offsets within this byte array.
*/
// DontCheck(MemberName): can't be renamed (for backward binary compatibility).
public final byte[] b;
final byte[] classFileBuffer;
/**
* The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool
* array, <i>plus one</i>. In other words, the offset of constant pool entry i is given by
* cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] - 1].
* The offset in bytes, in {@link #classFileBuffer}, of each cp_info entry of the ClassFile's
* constant_pool array, <i>plus one</i>. In other words, the offset of constant pool entry i is
* given by cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] -
* 1].
*/
private final int[] cpInfoOffsets;
@ -122,8 +132,8 @@ public class ClassReader {
private final ConstantDynamic[] constantDynamicValues;
/**
* The start offsets in {@link #b} of each element of the bootstrap_methods array (in the
* BootstrapMethods attribute).
* The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
* (in the BootstrapMethods attribute).
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
* 4.7.23</a>
@ -136,7 +146,7 @@ public class ClassReader {
*/
private final int maxStringLength;
/** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */
/** The offset in bytes of the ClassFile's access_flags field. */
public final int header;
// -----------------------------------------------------------------------------------------------
@ -176,10 +186,11 @@ public class ClassReader {
*/
ClassReader(
final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
b = classFileBuffer;
this.classFileBuffer = classFileBuffer;
this.b = classFileBuffer;
// Check the class' major_version. This field is after the magic and minor_version fields, which
// use 4 and 2 bytes respectively.
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) {
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V13) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
@ -195,8 +206,8 @@ public class ClassReader {
int currentCpInfoIndex = 1;
int currentCpInfoOffset = classFileOffset + 10;
int currentMaxStringLength = 0;
boolean hasBootstrapMethods = false;
boolean hasConstantDynamic = false;
boolean hasConstantInvokeDynamic = false;
// The offset of the other entries depend on the total size of all the previous entries.
while (currentCpInfoIndex < constantPoolCount) {
cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
@ -212,11 +223,12 @@ public class ClassReader {
break;
case Symbol.CONSTANT_DYNAMIC_TAG:
cpInfoSize = 5;
hasBootstrapMethods = true;
hasConstantDynamic = true;
break;
case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
cpInfoSize = 5;
hasConstantInvokeDynamic = true;
hasBootstrapMethods = true;
break;
case Symbol.CONSTANT_LONG_TAG:
case Symbol.CONSTANT_DOUBLE_TAG:
@ -256,9 +268,7 @@ public class ClassReader {
// Read the BootstrapMethods attribute, if any (only get the offset of each method).
bootstrapMethodOffsets =
(hasConstantDynamic | hasConstantInvokeDynamic)
? readBootstrapMethodsAttribute(currentMaxStringLength)
: null;
hasBootstrapMethods ? readBootstrapMethodsAttribute(currentMaxStringLength) : null;
}
/**
@ -299,8 +309,7 @@ public class ClassReader {
if (inputStream == null) {
throw new IOException("Class not found");
}
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
@ -696,7 +705,8 @@ public class ClassReader {
* attribute_name_index and attribute_length fields).
* @param modulePackagesOffset the offset of the ModulePackages attribute (excluding the
* 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 {@literal
* null}.
*/
private void readModuleAttributes(
final ClassVisitor classVisitor,
@ -1128,13 +1138,12 @@ public class ClassReader {
MethodWriter methodWriter = (MethodWriter) methodVisitor;
if (methodWriter.canCopyMethodAttributes(
this,
methodInfoOffset,
currentOffset - methodInfoOffset,
synthetic,
(context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0,
readUnsignedShort(methodInfoOffset + 4),
signatureIndex,
exceptionsOffset)) {
methodWriter.setMethodAttributesSource(methodInfoOffset, currentOffset - methodInfoOffset);
return currentOffset;
}
}
@ -1289,15 +1298,15 @@ public class ClassReader {
*
* @param methodVisitor the visitor that must visit the Code attribute.
* @param context information about the class being parsed.
* @param codeOffset the start offset in {@link #b} of the Code attribute, excluding its
* attribute_name_index and attribute_length fields.
* @param codeOffset the start offset in {@link #classFileBuffer} of the Code attribute, excluding
* its attribute_name_index and attribute_length fields.
*/
private void readCode(
final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
int currentOffset = codeOffset;
// Read the max_stack, max_locals and code_length fields.
final byte[] classFileBuffer = b;
final byte[] classBuffer = classFileBuffer;
final char[] charBuffer = context.charBuffer;
final int maxStack = readUnsignedShort(currentOffset);
final int maxLocals = readUnsignedShort(currentOffset + 2);
@ -1310,7 +1319,7 @@ public class ClassReader {
final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
while (currentOffset < bytecodeEndOffset) {
final int bytecodeOffset = currentOffset - bytecodeStartOffset;
final int opcode = classFileBuffer[currentOffset] & 0xFF;
final int opcode = classBuffer[currentOffset] & 0xFF;
switch (opcode) {
case Constants.NOP:
case Constants.ACONST_NULL:
@ -1510,7 +1519,7 @@ public class ClassReader {
currentOffset += 5;
break;
case Constants.WIDE:
switch (classFileBuffer[currentOffset + 1] & 0xFF) {
switch (classBuffer[currentOffset + 1] & 0xFF) {
case Constants.ILOAD:
case Constants.FLOAD:
case Constants.ALOAD:
@ -1759,11 +1768,11 @@ public class ClassReader {
// creating a label for each NEW instruction, and faster than fully decoding the whole stack
// map table.
for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
if (classBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
int potentialBytecodeOffset = readUnsignedShort(offset + 1);
if (potentialBytecodeOffset >= 0
&& potentialBytecodeOffset < codeLength
&& (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
&& (classBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
== Opcodes.NEW) {
createLabel(potentialBytecodeOffset, labels);
}
@ -1859,7 +1868,7 @@ public class ClassReader {
}
// Visit the instruction at this bytecode offset.
int opcode = classFileBuffer[currentOffset] & 0xFF;
int opcode = classBuffer[currentOffset] & 0xFF;
switch (opcode) {
case Constants.NOP:
case Constants.ACONST_NULL:
@ -2097,19 +2106,17 @@ public class ClassReader {
break;
}
case Constants.ASM_GOTO_W:
{
// Replace ASM_GOTO_W with GOTO_W.
methodVisitor.visitJumpInsn(
Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
// The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
// IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
// here.
insertFrame = true;
currentOffset += 5;
break;
}
// Replace ASM_GOTO_W with GOTO_W.
methodVisitor.visitJumpInsn(
Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
// The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
// IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
// here.
insertFrame = true;
currentOffset += 5;
break;
case Constants.WIDE:
opcode = classFileBuffer[currentOffset + 1] & 0xFF;
opcode = classBuffer[currentOffset + 1] & 0xFF;
if (opcode == Opcodes.IINC) {
methodVisitor.visitIincInsn(
readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
@ -2165,12 +2172,12 @@ public class ClassReader {
case Constants.DSTORE:
case Constants.ASTORE:
case Constants.RET:
methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
methodVisitor.visitVarInsn(opcode, classBuffer[currentOffset + 1] & 0xFF);
currentOffset += 2;
break;
case Constants.BIPUSH:
case Constants.NEWARRAY:
methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
methodVisitor.visitIntInsn(opcode, classBuffer[currentOffset + 1]);
currentOffset += 2;
break;
case Constants.SIPUSH:
@ -2178,8 +2185,7 @@ public class ClassReader {
currentOffset += 3;
break;
case Constants.LDC:
methodVisitor.visitLdcInsn(
readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
methodVisitor.visitLdcInsn(readConst(classBuffer[currentOffset + 1] & 0xFF, charBuffer));
currentOffset += 2;
break;
case Constants.LDC_W:
@ -2205,7 +2211,7 @@ public class ClassReader {
methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
} else {
boolean isInterface =
classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
classBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
if (opcode == Opcodes.INVOKEINTERFACE) {
@ -2246,12 +2252,12 @@ public class ClassReader {
break;
case Constants.IINC:
methodVisitor.visitIincInsn(
classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
classBuffer[currentOffset + 1] & 0xFF, classBuffer[currentOffset + 2]);
currentOffset += 3;
break;
case Constants.MULTIANEWARRAY:
methodVisitor.visitMultiANewArrayInsn(
readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
readClass(currentOffset + 1, charBuffer), classBuffer[currentOffset + 3] & 0xFF);
currentOffset += 4;
break;
default:
@ -2560,7 +2566,7 @@ public class ClassReader {
int pathLength = readByte(currentOffset);
if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
// Parse the target_path structure and create a corresponding TypePath.
TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset);
TypePath path = pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
currentOffset += 1 + 2 * pathLength;
// Parse the type_index field.
String annotationDescriptor = readUTF8(currentOffset, charBuffer);
@ -2593,7 +2599,7 @@ public class ClassReader {
* -1 if there is no such type_annotation of if it does not have a bytecode offset.
*
* @param typeAnnotationOffsets the offset of each 'type_annotation' entry in a
* Runtime[In]VisibleTypeAnnotations attribute, or null.
* Runtime[In]VisibleTypeAnnotations attribute, or {@literal null}.
* @param typeAnnotationIndex the index a 'type_annotation' entry in typeAnnotationOffsets.
* @return bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or -1
* if there is no such type_annotation of if it does not have a bytecode offset.
@ -2685,7 +2691,7 @@ public class ClassReader {
// Parse and store the target_path structure.
int pathLength = readByte(currentOffset);
context.currentTypeAnnotationTargetPath =
pathLength == 0 ? null : new TypePath(b, currentOffset);
pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
// Return the start offset of the rest of the type_annotation structure.
return currentOffset + 1 + 2 * pathLength;
}
@ -2707,7 +2713,7 @@ public class ClassReader {
final int runtimeParameterAnnotationsOffset,
final boolean visible) {
int currentOffset = runtimeParameterAnnotationsOffset;
int numParameters = b[currentOffset++] & 0xFF;
int numParameters = classFileBuffer[currentOffset++] & 0xFF;
methodVisitor.visitAnnotableParameterCount(numParameters, visible);
char[] charBuffer = context.charBuffer;
for (int i = 0; i < numParameters; ++i) {
@ -2775,8 +2781,8 @@ public class ClassReader {
* Reads a JVMS 'element_value' structure and makes the given visitor visit it.
*
* @param annotationVisitor the visitor that must visit the element_value structure.
* @param elementValueOffset the start offset in {@link #b} of the element_value structure to be
* read.
* @param elementValueOffset the start offset in {@link #classFileBuffer} of the element_value
* structure to be read.
* @param elementName the name of the element_value structure to be read, or {@literal null}.
* @param charBuffer the buffer used to read strings in the constant pool.
* @return the end offset of the JVMS 'element_value' structure.
@ -2788,7 +2794,7 @@ public class ClassReader {
final char[] charBuffer) {
int currentOffset = elementValueOffset;
if (annotationVisitor == null) {
switch (b[currentOffset] & 0xFF) {
switch (classFileBuffer[currentOffset] & 0xFF) {
case 'e': // enum_const_value
return currentOffset + 5;
case '@': // annotation_value
@ -2799,7 +2805,7 @@ public class ClassReader {
return currentOffset + 3;
}
}
switch (b[currentOffset++] & 0xFF) {
switch (classFileBuffer[currentOffset++] & 0xFF) {
case 'B': // const_value_index, CONSTANT_Integer
annotationVisitor.visit(
elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
@ -2865,7 +2871,7 @@ public class ClassReader {
/* named = */ false,
charBuffer);
}
switch (b[currentOffset] & 0xFF) {
switch (classFileBuffer[currentOffset] & 0xFF) {
case 'B':
byte[] byteValues = new byte[numValues];
for (int i = 0; i < numValues; i++) {
@ -3027,9 +3033,9 @@ public class ClassReader {
* object. This method can also be used to read a full_frame structure, excluding its frame_type
* field (this is used to parse the legacy StackMap attributes).
*
* @param stackMapFrameOffset the start offset in {@link #b} of the stack_map_frame_value
* structure to be read, or the start offset of a full_frame structure (excluding its
* frame_type field).
* @param stackMapFrameOffset the start offset in {@link #classFileBuffer} of the
* stack_map_frame_value structure to be read, or the start offset of a full_frame structure
* (excluding its frame_type field).
* @param compressed true to read a 'stack_map_frame' structure, false to read a 'full_frame'
* structure without its frame_type field.
* @param expand if the stack map frame must be expanded. See {@link #EXPAND_FRAMES}.
@ -3047,7 +3053,7 @@ public class ClassReader {
int frameType;
if (compressed) {
// Read the frame_type field.
frameType = b[currentOffset++] & 0xFF;
frameType = classFileBuffer[currentOffset++] & 0xFF;
} else {
frameType = Frame.FULL_FRAME;
context.currentFrameOffset = -1;
@ -3142,7 +3148,7 @@ public class ClassReader {
final char[] charBuffer,
final Label[] labels) {
int currentOffset = verificationTypeInfoOffset;
int tag = b[currentOffset++] & 0xFF;
int tag = classFileBuffer[currentOffset++] & 0xFF;
switch (tag) {
case Frame.ITEM_TOP:
frame[index] = Opcodes.TOP;
@ -3184,9 +3190,11 @@ public class ClassReader {
// ----------------------------------------------------------------------------------------------
/**
* Returns the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
* Returns the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
* field entry.
*
* @return the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
* @return the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
* field entry.
*/
final int getFirstAttributeOffset() {
// Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
@ -3233,7 +3241,7 @@ public class ClassReader {
*
* @param maxStringLength a conservative estimate of the maximum length of the strings contained
* in the constant pool of the class.
* @return the offsets of the bootstrap methods or null.
* @return the offsets of the bootstrap methods.
*/
private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
char[] charBuffer = new char[maxStringLength];
@ -3260,23 +3268,25 @@ public class ClassReader {
}
currentAttributeOffset += attributeLength;
}
return null;
throw new IllegalArgumentException();
}
/**
* Reads a non standard JVMS 'attribute' structure in {@link #b}.
* Reads a non standard JVMS 'attribute' structure in {@link #classFileBuffer}.
*
* @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
* the class. Any attribute whose type is not equal to the type of one the prototypes will not
* be parsed: its byte array value will be passed unchanged to the ClassWriter.
* @param type the type of the attribute.
* @param offset the start offset of the JVMS 'attribute' structure in {@link #b}. The 6 attribute
* header bytes (attribute_name_index and attribute_length) are not taken into account here.
* @param offset the start offset of the JVMS 'attribute' structure in {@link #classFileBuffer}.
* The 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param length the length of the attribute's content (excluding the 6 attribute header bytes).
* @param charBuffer the buffer to be used to read strings in the constant pool.
* @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link #b}, or
* -1 if the attribute to be read is not a code attribute. The 6 attribute header bytes
* (attribute_name_index and attribute_length) are not taken into account here.
* @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link
* #classFileBuffer}, or -1 if the attribute to be read is not a code attribute. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
* is not a code attribute.
* @return the attribute that has been read.
@ -3312,13 +3322,14 @@ public class ClassReader {
}
/**
* Returns the start offset in {@link #b} of a JVMS 'cp_info' structure (i.e. a constant pool
* entry), plus one. <i>This method is intended for {@link Attribute} sub classes, and is normally
* not needed by class generators or adapters.</i>
* Returns the start offset in this {@link ClassReader} of a JVMS 'cp_info' structure (i.e. a
* constant pool entry), plus one. <i>This method is intended for {@link Attribute} sub classes,
* and is normally not needed by class generators or adapters.</i>
*
* @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool
* table.
* @return the start offset in {@link #b} of the corresponding JVMS 'cp_info' structure, plus one.
* @return the start offset in this {@link ClassReader} of the corresponding JVMS 'cp_info'
* structure, plus one.
*/
public int getItem(final int constantPoolEntryIndex) {
return cpInfoOffsets[constantPoolEntryIndex];
@ -3336,60 +3347,60 @@ public class ClassReader {
}
/**
* Reads a byte value in {@link #b}. <i>This method is intended for {@link Attribute} sub classes,
* and is normally not needed by class generators or adapters.</i>
* Reads a byte value in this {@link ClassReader}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param offset the start offset of the value to be read in {@link #b}.
* @param offset the start offset of the value to be read in this {@link ClassReader}.
* @return the read value.
*/
public int readByte(final int offset) {
return b[offset] & 0xFF;
return classFileBuffer[offset] & 0xFF;
}
/**
* Reads an unsigned short value in {@link #b}. <i>This method is intended for {@link Attribute}
* sub classes, and is normally not needed by class generators or adapters.</i>
* Reads an unsigned short value in this {@link ClassReader}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param offset the start index of the value to be read in {@link #b}.
* @param offset the start index of the value to be read in this {@link ClassReader}.
* @return the read value.
*/
public int readUnsignedShort(final int offset) {
byte[] classFileBuffer = b;
return ((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF);
byte[] classBuffer = classFileBuffer;
return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);
}
/**
* Reads a signed short value in {@link #b}. <i>This method is intended for {@link Attribute} sub
* classes, and is normally not needed by class generators or adapters.</i>
* Reads a signed short value in this {@link ClassReader}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param offset the start offset of the value to be read in {@link #b}.
* @param offset the start offset of the value to be read in this {@link ClassReader}.
* @return the read value.
*/
public short readShort(final int offset) {
byte[] classFileBuffer = b;
return (short) (((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF));
byte[] classBuffer = classFileBuffer;
return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));
}
/**
* Reads a signed int value in {@link #b}. <i>This method is intended for {@link Attribute} sub
* classes, and is normally not needed by class generators or adapters.</i>
* Reads a signed int value in this {@link ClassReader}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param offset the start offset of the value to be read in {@link #b}.
* @param offset the start offset of the value to be read in this {@link ClassReader}.
* @return the read value.
*/
public int readInt(final int offset) {
byte[] classFileBuffer = b;
return ((classFileBuffer[offset] & 0xFF) << 24)
| ((classFileBuffer[offset + 1] & 0xFF) << 16)
| ((classFileBuffer[offset + 2] & 0xFF) << 8)
| (classFileBuffer[offset + 3] & 0xFF);
byte[] classBuffer = classFileBuffer;
return ((classBuffer[offset] & 0xFF) << 24)
| ((classBuffer[offset + 1] & 0xFF) << 16)
| ((classBuffer[offset + 2] & 0xFF) << 8)
| (classBuffer[offset + 3] & 0xFF);
}
/**
* Reads a signed long value in {@link #b}. <i>This method is intended for {@link Attribute} sub
* classes, and is normally not needed by class generators or adapters.</i>
* Reads a signed long value in this {@link ClassReader}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param offset the start offset of the value to be read in {@link #b}.
* @param offset the start offset of the value to be read in this {@link ClassReader}.
* @return the read value.
*/
public long readLong(final int offset) {
@ -3399,11 +3410,12 @@ public class ClassReader {
}
/**
* Reads a CONSTANT_Utf8 constant pool entry in {@link #b}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
* Reads a CONSTANT_Utf8 constant pool entry in this {@link ClassReader}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by class generators or
* adapters.</i>
*
* @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
* index of a CONSTANT_Utf8 entry in the class's constant pool table.
* @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
* value is the index of a CONSTANT_Utf8 entry in the class's constant pool table.
* @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the String corresponding to the specified CONSTANT_Utf8 entry.
@ -3418,7 +3430,7 @@ public class ClassReader {
}
/**
* Reads a CONSTANT_Utf8 constant pool entry in {@link #b}.
* Reads a CONSTANT_Utf8 constant pool entry in {@link #classFileBuffer}.
*
* @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool
* table.
@ -3437,7 +3449,7 @@ public class ClassReader {
}
/**
* Reads an UTF8 string in {@link #b}.
* Reads an UTF8 string in {@link #classFileBuffer}.
*
* @param utfOffset the start offset of the UTF8 string to be read.
* @param utfLength the length of the UTF8 string to be read.
@ -3449,20 +3461,20 @@ public class ClassReader {
int currentOffset = utfOffset;
int endOffset = currentOffset + utfLength;
int strLength = 0;
byte[] classFileBuffer = b;
byte[] classBuffer = classFileBuffer;
while (currentOffset < endOffset) {
int currentByte = classFileBuffer[currentOffset++];
int currentByte = classBuffer[currentOffset++];
if ((currentByte & 0x80) == 0) {
charBuffer[strLength++] = (char) (currentByte & 0x7F);
} else if ((currentByte & 0xE0) == 0xC0) {
charBuffer[strLength++] =
(char) (((currentByte & 0x1F) << 6) + (classFileBuffer[currentOffset++] & 0x3F));
(char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F));
} else {
charBuffer[strLength++] =
(char)
(((currentByte & 0xF) << 12)
+ ((classFileBuffer[currentOffset++] & 0x3F) << 6)
+ (classFileBuffer[currentOffset++] & 0x3F));
+ ((classBuffer[currentOffset++] & 0x3F) << 6)
+ (classBuffer[currentOffset++] & 0x3F));
}
}
return new String(charBuffer, 0, strLength);
@ -3470,12 +3482,13 @@ public class ClassReader {
/**
* Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
* CONSTANT_Package constant pool entry in {@link #b}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
* CONSTANT_Package constant pool entry in {@link #classFileBuffer}. <i>This method is intended
* for {@link Attribute} sub classes, and is normally not needed by class generators or
* adapters.</i>
*
* @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
* index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
* CONSTANT_Package entry in class's constant pool table.
* @param offset the start offset of an unsigned short value in {@link #classFileBuffer}, whose
* value is the index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
* CONSTANT_Module or CONSTANT_Package entry in class's constant pool table.
* @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the String corresponding to the specified constant pool entry.
@ -3487,11 +3500,12 @@ public class ClassReader {
}
/**
* Reads a CONSTANT_Class constant pool entry in {@link #b}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
* Reads a CONSTANT_Class constant pool entry in this {@link ClassReader}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by class generators or
* adapters.</i>
*
* @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
* index of a CONSTANT_Class entry in class's constant pool table.
* @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
* value is the index of a CONSTANT_Class entry in class's constant pool table.
* @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the String corresponding to the specified CONSTANT_Class entry.
@ -3501,11 +3515,12 @@ public class ClassReader {
}
/**
* Reads a CONSTANT_Module constant pool entry in {@link #b}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
* Reads a CONSTANT_Module constant pool entry in this {@link ClassReader}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by class generators or
* adapters.</i>
*
* @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
* index of a CONSTANT_Module entry in class's constant pool table.
* @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
* value is the index of a CONSTANT_Module entry in class's constant pool table.
* @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the String corresponding to the specified CONSTANT_Module entry.
@ -3515,11 +3530,12 @@ public class ClassReader {
}
/**
* Reads a CONSTANT_Package constant pool entry in {@link #b}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
* Reads a CONSTANT_Package constant pool entry in this {@link ClassReader}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by class generators or
* adapters.</i>
*
* @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
* index of a CONSTANT_Package entry in class's constant pool table.
* @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
* value is the index of a CONSTANT_Package entry in class's constant pool table.
* @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the String corresponding to the specified CONSTANT_Package entry.
@ -3529,7 +3545,7 @@ public class ClassReader {
}
/**
* Reads a CONSTANT_Dynamic constant pool entry in {@link #b}.
* Reads a CONSTANT_Dynamic constant pool entry in {@link #classFileBuffer}.
*
* @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
* pool table.
@ -3560,8 +3576,9 @@ public class ClassReader {
}
/**
* Reads a numeric or string constant pool entry in {@link #b}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
* Reads a numeric or string constant pool entry in this {@link ClassReader}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by class generators or
* adapters.</i>
*
* @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
* CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
@ -3574,7 +3591,7 @@ public class ClassReader {
*/
public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
switch (b[cpInfoOffset - 1]) {
switch (classFileBuffer[cpInfoOffset - 1]) {
case Symbol.CONSTANT_INTEGER_TAG:
return readInt(cpInfoOffset);
case Symbol.CONSTANT_FLOAT_TAG:
@ -3597,7 +3614,7 @@ public class ClassReader {
String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
boolean isInterface =
b[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
classFileBuffer[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
return new Handle(referenceKind, owner, name, descriptor, isInterface);
case Symbol.CONSTANT_DYNAMIC_TAG:
return readConstantDynamic(constantPoolEntryIndex, charBuffer);

View File

@ -44,7 +44,7 @@ public abstract class ClassVisitor {
*/
protected final int api;
/** The class visitor to which this visitor must delegate method calls. May be null. */
/** The class visitor to which this visitor must delegate method calls. May be {@literal null}. */
protected ClassVisitor cv;
/**
@ -66,8 +66,8 @@ public abstract class ClassVisitor {
* null.
*/
public ClassVisitor(final int api, final ClassVisitor classVisitor) {
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
throw new IllegalArgumentException();
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
throw new IllegalArgumentException("Unsupported api " + api);
}
this.api = api;
this.cv = classVisitor;

View File

@ -313,37 +313,26 @@ public class ClassWriter extends ClassVisitor {
@Override
public final AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastRuntimeVisibleAnnotation =
new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
} else {
return lastRuntimeInvisibleAnnotation =
new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
}
}
@Override
public final AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.putTarget(typeRef, typeAnnotation);
TypePath.put(typePath, typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastRuntimeVisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
} else {
return lastRuntimeInvisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
}
}
@ -438,7 +427,7 @@ public class ClassWriter extends ClassVisitor {
* @throws ClassTooLargeException if the constant pool of the class is too large.
* @throws MethodTooLargeException if the Code attribute of a method is too large.
*/
public byte[] toByteArray() throws ClassTooLargeException, MethodTooLargeException {
public byte[] toByteArray() {
// First step: compute the size in bytes of the ClassFile structure.
// The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version,
// constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count,
@ -617,22 +606,13 @@ public class ClassWriter extends ClassVisitor {
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
}
if (lastRuntimeVisibleAnnotation != null) {
lastRuntimeVisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), result);
}
if (lastRuntimeInvisibleAnnotation != null) {
lastRuntimeInvisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), result);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
lastRuntimeVisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), result);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
lastRuntimeInvisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), result);
}
AnnotationWriter.putAnnotations(
symbolTable,
lastRuntimeVisibleAnnotation,
lastRuntimeInvisibleAnnotation,
lastRuntimeVisibleTypeAnnotation,
lastRuntimeInvisibleTypeAnnotation,
result);
symbolTable.putBootstrapMethods(result);
if (moduleWriter != null) {
moduleWriter.putAttributes(result);
@ -980,7 +960,7 @@ public class ClassWriter extends ClassVisitor {
* @return ClassLoader
*/
protected ClassLoader getClassLoader() {
// SPRING PATCH: PREFER THREAD CONTEXT CLASSLOADER FOR APPLICATION CLASSES
// SPRING PATCH: prefer thread context ClassLoader for application classes
ClassLoader classLoader = null;
try {
classLoader = Thread.currentThread().getContextClassLoader();

View File

@ -42,7 +42,7 @@ public abstract class FieldVisitor {
*/
protected final int api;
/** The field visitor to which this visitor must delegate method calls. May be null. */
/** The field visitor to which this visitor must delegate method calls. May be {@literal null}. */
protected FieldVisitor fv;
/**
@ -64,8 +64,8 @@ public abstract class FieldVisitor {
* null.
*/
public FieldVisitor(final int api, final FieldVisitor fieldVisitor) {
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
throw new IllegalArgumentException();
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
throw new IllegalArgumentException("Unsupported api " + api);
}
this.api = api;
this.fv = fieldVisitor;

View File

@ -143,37 +143,26 @@ final class FieldWriter extends FieldVisitor {
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastRuntimeVisibleAnnotation =
new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
} else {
return lastRuntimeInvisibleAnnotation =
new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
}
}
@Override
public AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.putTarget(typeRef, typeAnnotation);
TypePath.put(typePath, typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastRuntimeVisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
} else {
return lastRuntimeInvisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
}
}
@ -208,44 +197,13 @@ final class FieldWriter extends FieldVisitor {
symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE);
size += 8;
}
// Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
&& symbolTable.getMajorVersion() < Opcodes.V1_5) {
// Synthetic attributes always use 6 bytes.
symbolTable.addConstantUtf8(Constants.SYNTHETIC);
size += 6;
}
if (signatureIndex != 0) {
// Signature attributes always use 8 bytes.
symbolTable.addConstantUtf8(Constants.SIGNATURE);
size += 8;
}
// ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
// Deprecated attributes always use 6 bytes.
symbolTable.addConstantUtf8(Constants.DEPRECATED);
size += 6;
}
if (lastRuntimeVisibleAnnotation != null) {
size +=
lastRuntimeVisibleAnnotation.computeAnnotationsSize(
Constants.RUNTIME_VISIBLE_ANNOTATIONS);
}
if (lastRuntimeInvisibleAnnotation != null) {
size +=
lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
size +=
lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
size +=
lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
}
size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex);
size +=
AnnotationWriter.computeAnnotationsSize(
lastRuntimeVisibleAnnotation,
lastRuntimeInvisibleAnnotation,
lastRuntimeVisibleTypeAnnotation,
lastRuntimeInvisibleTypeAnnotation);
if (firstAttribute != null) {
size += firstAttribute.computeAttributesSize(symbolTable);
}
@ -302,34 +260,14 @@ final class FieldWriter extends FieldVisitor {
.putInt(2)
.putShort(constantValueIndex);
}
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
}
if (signatureIndex != 0) {
output
.putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
.putInt(2)
.putShort(signatureIndex);
}
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
}
if (lastRuntimeVisibleAnnotation != null) {
lastRuntimeVisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
}
if (lastRuntimeInvisibleAnnotation != null) {
lastRuntimeInvisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
lastRuntimeVisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
lastRuntimeInvisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
}
Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output);
AnnotationWriter.putAnnotations(
symbolTable,
lastRuntimeVisibleAnnotation,
lastRuntimeInvisibleAnnotation,
lastRuntimeVisibleTypeAnnotation,
lastRuntimeInvisibleTypeAnnotation,
output);
if (firstAttribute != null) {
firstAttribute.putAttributes(symbolTable, output);
}

View File

@ -54,19 +54,19 @@ package org.springframework.asm;
*
* <pre>
* =====================================
* |.DIM|KIND|FLAG|...............VALUE|
* |...DIM|KIND|.F|...............VALUE|
* =====================================
* </pre>
*
* <ul>
* <li>the DIM field, stored in the 4 most significant bits, is a signed number of array
* dimensions (from -8 to 7, included). It can be retrieved with {@link #DIM_MASK} and a right
* shift of {@link #DIM_SHIFT}.
* <li>the DIM field, stored in the 6 most significant bits, is a signed number of array
* dimensions (from -32 to 31, included). It can be retrieved with {@link #DIM_MASK} and a
* right shift of {@link #DIM_SHIFT}.
* <li>the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be
* retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link
* #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND}
* or {@link #STACK_KIND}.
* <li>the FLAGS field, stored in 4 bits, contains up to 4 boolean flags. Currently only one flag
* <li>the FLAGS field, stored in 2 bits, contains up to 2 boolean flags. Currently only one flag
* is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}.
* <li>the VALUE field, stored in the remaining 20 bits, contains either
* <ul>
@ -89,9 +89,9 @@ package org.springframework.asm;
* <p>Output frames can contain abstract types of any kind and with a positive or negative array
* dimension (and even unassigned types, represented by 0 - which does not correspond to any valid
* abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or
* UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type
* table contains only internal type names (array type descriptors are forbidden - array dimensions
* must be represented through the DIM field).
* UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases
* the type table contains only internal type names (array type descriptors are forbidden - array
* dimensions must be represented through the DIM field).
*
* <p>The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE +
* TOP), for local variables as well as in the operand stack. This is necessary to be able to
@ -129,18 +129,25 @@ class Frame {
private static final int ITEM_ASM_CHAR = 11;
private static final int ITEM_ASM_SHORT = 12;
// The size and offset in bits of each field of an abstract type.
private static final int DIM_SIZE = 6;
private static final int KIND_SIZE = 4;
private static final int FLAGS_SIZE = 2;
private static final int VALUE_SIZE = 32 - DIM_SIZE - KIND_SIZE - FLAGS_SIZE;
private static final int DIM_SHIFT = KIND_SIZE + FLAGS_SIZE + VALUE_SIZE;
private static final int KIND_SHIFT = FLAGS_SIZE + VALUE_SIZE;
private static final int FLAGS_SHIFT = VALUE_SIZE;
// Bitmasks to get each field of an abstract type.
private static final int DIM_MASK = 0xF0000000;
private static final int KIND_MASK = 0x0F000000;
private static final int FLAGS_MASK = 0x00F00000;
private static final int VALUE_MASK = 0x000FFFFF;
private static final int DIM_MASK = ((1 << DIM_SIZE) - 1) << DIM_SHIFT;
private static final int KIND_MASK = ((1 << KIND_SIZE) - 1) << KIND_SHIFT;
private static final int VALUE_MASK = (1 << VALUE_SIZE) - 1;
// Constants to manipulate the DIM field of an abstract type.
/** The number of right shift bits to use to get the array dimensions of an abstract type. */
private static final int DIM_SHIFT = 28;
/** The constant to be added to an abstract type to get one with one more array dimension. */
private static final int ARRAY_OF = +1 << DIM_SHIFT;
@ -149,11 +156,11 @@ class Frame {
// Possible values for the KIND field of an abstract type.
private static final int CONSTANT_KIND = 0x01000000;
private static final int REFERENCE_KIND = 0x02000000;
private static final int UNINITIALIZED_KIND = 0x03000000;
private static final int LOCAL_KIND = 0x04000000;
private static final int STACK_KIND = 0x05000000;
private static final int CONSTANT_KIND = 1 << KIND_SHIFT;
private static final int REFERENCE_KIND = 2 << KIND_SHIFT;
private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT;
private static final int LOCAL_KIND = 4 << KIND_SHIFT;
private static final int STACK_KIND = 5 << KIND_SHIFT;
// Possible flags for the FLAGS field of an abstract type.
@ -162,7 +169,7 @@ class Frame {
* concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been
* partially overridden with an xSTORE instruction).
*/
private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK;
private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 1 << FLAGS_SHIFT;
// Useful predefined abstract types (all the possible CONSTANT_KIND types).
@ -540,7 +547,8 @@ class Frame {
* @param descriptor a type or method descriptor (in which case its return type is pushed).
*/
private void push(final SymbolTable symbolTable, final String descriptor) {
int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0;
int typeDescriptorOffset =
descriptor.charAt(0) == '(' ? Type.getReturnTypeOffset(descriptor) : 0;
int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset);
if (abstractType != 0) {
push(abstractType);
@ -1103,6 +1111,42 @@ class Frame {
// Frame merging methods, used in the second step of the stack map frame computation algorithm
// -----------------------------------------------------------------------------------------------
/**
* Computes the concrete output type corresponding to a given abstract output type.
*
* @param abstractOutputType an abstract output type.
* @param numStack the size of the input stack, used to resolve abstract output types of
* STACK_KIND kind.
* @return the concrete output type corresponding to 'abstractOutputType'.
*/
private int getConcreteOutputType(final int abstractOutputType, final int numStack) {
int dim = abstractOutputType & DIM_MASK;
int kind = abstractOutputType & KIND_MASK;
if (kind == LOCAL_KIND) {
// By definition, a LOCAL_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
// this method is called, but was not when the abstract type was computed).
int concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
concreteOutputType = TOP;
}
return concreteOutputType;
} else if (kind == STACK_KIND) {
// 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
// this method is called, but was not when the abstract type was computed).
int concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
concreteOutputType = TOP;
}
return concreteOutputType;
} else {
return abstractOutputType;
}
}
/**
* Merges the input frame of the given {@link Frame} with the input and output frames of this
* {@link Frame}. Returns {@literal true} if the given frame has been changed by this operation
@ -1137,29 +1181,7 @@ class Frame {
// value at the beginning of the block.
concreteOutputType = inputLocals[i];
} else {
int dim = abstractOutputType & DIM_MASK;
int kind = abstractOutputType & KIND_MASK;
if (kind == LOCAL_KIND) {
// By definition, a LOCAL_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
// this method is called, but was not when the abstract type was computed).
concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
concreteOutputType = TOP;
}
} else if (kind == STACK_KIND) {
// 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
// this method is called, but was not when the abstract type was computed).
concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
concreteOutputType = TOP;
}
} else {
concreteOutputType = abstractOutputType;
}
concreteOutputType = getConcreteOutputType(abstractOutputType, numStack);
}
} else {
// If the local variable has never been assigned in this basic block, it is equal to its
@ -1213,25 +1235,8 @@ class Frame {
// Then, do this for the stack operands that have pushed in the basic block (this code is the
// same as the one above for local variables).
for (int i = 0; i < outputStackTop; ++i) {
int concreteOutputType;
int abstractOutputType = outputStack[i];
int dim = abstractOutputType & DIM_MASK;
int kind = abstractOutputType & KIND_MASK;
if (kind == LOCAL_KIND) {
concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
concreteOutputType = TOP;
}
} else if (kind == STACK_KIND) {
concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
&& (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
concreteOutputType = TOP;
}
} else {
concreteOutputType = abstractOutputType;
}
int concreteOutputType = getConcreteOutputType(abstractOutputType, numStack);
if (initializations != null) {
concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
}
@ -1248,10 +1253,10 @@ class Frame {
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
* @param sourceType the abstract type with which the abstract type array element must be merged.
* This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link
* #UNINITIALIZED_KIND} kind, with positive or null array dimensions.
* #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions.
* @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND},
* {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array
* dimensions.
* {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal
* null} array dimensions.
* @param dstIndex the index of the type that must be merged in dstTypes.
* @return {@literal true} if the type array has been modified by this operation.
*/

View File

@ -227,7 +227,8 @@ public class Label {
/**
* The maximum height reached by the output stack, relatively to the top of the input stack, in
* the basic block corresponding to this label. This maximum is always positive or null.
* the basic block corresponding to this label. This maximum is always positive or {@literal
* null}.
*/
short outputStackMax;
@ -264,12 +265,12 @@ public class Label {
Edge outgoingEdges;
/**
* The next element in the list of labels to which this label belongs, or null if it does not
* belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in
* order to ensure that this field is null if and only if this label does not belong to a list of
* labels. Note that there can be several lists of labels at the same time, but that a label can
* belong to at most one list at a time (unless some lists share a common tail, but this is not
* used in practice).
* The next element in the list of labels to which this label belongs, or {@literal null} if it
* does not belong to any list. All lists of labels must end with the {@link #EMPTY_LIST}
* sentinel, in order to ensure that this field is null if and only if this label does not belong
* to a list of labels. Note that there can be several lists of labels at the same time, but that
* a label can belong to at most one list at a time (unless some lists share a common tail, but
* this is not used in practice).
*
* <p>List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
* MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,

View File

@ -56,7 +56,9 @@ public abstract class MethodVisitor {
*/
protected final int api;
/** The method visitor to which this visitor must delegate method calls. May be null. */
/**
* The method visitor to which this visitor must delegate method calls. May be {@literal null}.
*/
protected MethodVisitor mv;
/**
@ -78,8 +80,8 @@ public abstract class MethodVisitor {
* be null.
*/
public MethodVisitor(final int api, final MethodVisitor methodVisitor) {
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
throw new IllegalArgumentException();
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
throw new IllegalArgumentException("Unsupported api " + api);
}
this.api = api;
this.mv = methodVisitor;
@ -92,7 +94,7 @@ public abstract class MethodVisitor {
/**
* Visits a parameter of this method.
*
* @param name parameter name or null if none is provided.
* @param name parameter name or {@literal null} if none is provided.
* @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC}
* or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}).
*/
@ -395,14 +397,8 @@ public abstract class MethodVisitor {
@Deprecated
public void visitMethodInsn(
final int opcode, final String owner, final String name, final String descriptor) {
if (api >= Opcodes.ASM5) {
boolean isInterface = opcode == Opcodes.INVOKEINTERFACE;
visitMethodInsn(opcode, owner, name, descriptor, isInterface);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, descriptor);
}
int opcodeAndSource = opcode | (api < Opcodes.ASM5 ? Opcodes.SOURCE_DEPRECATED : 0);
visitMethodInsn(opcodeAndSource, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
}
/**
@ -422,15 +418,15 @@ public abstract class MethodVisitor {
final String name,
final String descriptor,
final boolean isInterface) {
if (api < Opcodes.ASM5) {
if (api < Opcodes.ASM5 && (opcode & Opcodes.SOURCE_DEPRECATED) == 0) {
if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
throw new UnsupportedOperationException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
}
visitMethodInsn(opcode, owner, name, descriptor);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
mv.visitMethodInsn(opcode & ~Opcodes.SOURCE_MASK, owner, name, descriptor, isInterface);
}
}

View File

@ -654,37 +654,26 @@ final class MethodWriter extends MethodVisitor {
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastRuntimeVisibleAnnotation =
new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
} else {
return lastRuntimeInvisibleAnnotation =
new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
}
}
@Override
public AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.putTarget(typeRef, typeAnnotation);
TypePath.put(typePath, typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastRuntimeVisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
} else {
return lastRuntimeInvisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
}
}
@ -700,27 +689,24 @@ final class MethodWriter extends MethodVisitor {
@Override
public AnnotationVisitor visitParameterAnnotation(
final int parameter, final String annotationDescriptor, final boolean visible) {
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.putShort(symbolTable.addConstantUtf8(annotationDescriptor)).putShort(0);
if (visible) {
if (lastRuntimeVisibleParameterAnnotations == null) {
lastRuntimeVisibleParameterAnnotations =
new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
return lastRuntimeVisibleParameterAnnotations[parameter] =
new AnnotationWriter(
symbolTable, annotation, lastRuntimeVisibleParameterAnnotations[parameter]);
AnnotationWriter.create(
symbolTable, annotationDescriptor, lastRuntimeVisibleParameterAnnotations[parameter]);
} else {
if (lastRuntimeInvisibleParameterAnnotations == null) {
lastRuntimeInvisibleParameterAnnotations =
new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
return lastRuntimeInvisibleParameterAnnotations[parameter] =
new AnnotationWriter(
symbolTable, annotation, lastRuntimeInvisibleParameterAnnotations[parameter]);
AnnotationWriter.create(
symbolTable,
annotationDescriptor,
lastRuntimeInvisibleParameterAnnotations[parameter]);
}
}
@ -1415,20 +1401,22 @@ final class MethodWriter extends MethodVisitor {
@Override
public AnnotationVisitor visitInsnAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.putTarget((typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), typeAnnotation);
TypePath.put(typePath, typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastCodeRuntimeVisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable,
(typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8),
typePath,
descriptor,
lastCodeRuntimeVisibleTypeAnnotation);
} else {
return lastCodeRuntimeInvisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable,
(typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8),
typePath,
descriptor,
lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@ -1449,20 +1437,14 @@ final class MethodWriter extends MethodVisitor {
@Override
public AnnotationVisitor visitTryCatchAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.putTarget(typeRef, typeAnnotation);
TypePath.put(typePath, typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastCodeRuntimeVisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeVisibleTypeAnnotation);
} else {
return lastCodeRuntimeInvisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
AnnotationWriter.create(
symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@ -1530,10 +1512,18 @@ final class MethodWriter extends MethodVisitor {
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastCodeRuntimeVisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
new AnnotationWriter(
symbolTable,
/* useNamedValues = */ true,
typeAnnotation,
lastCodeRuntimeVisibleTypeAnnotation);
} else {
return lastCodeRuntimeInvisibleTypeAnnotation =
new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
new AnnotationWriter(
symbolTable,
/* useNamedValues = */ true,
typeAnnotation,
lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@ -2004,10 +1994,6 @@ final class MethodWriter extends MethodVisitor {
* attribute) are the same as the corresponding attributes in the given method.
*
* @param source the source ClassReader from which the attributes of this method might be copied.
* @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which
* the attributes of this method might be copied.
* @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which
* the attributes of this method might be copied.
* @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes
* of this method might be copied contains a Synthetic attribute.
* @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes
@ -2024,8 +2010,6 @@ final class MethodWriter extends MethodVisitor {
*/
boolean canCopyMethodAttributes(
final ClassReader source,
final int methodInfoOffset,
final int methodInfoLength,
final boolean hasSyntheticAttribute,
final boolean hasDeprecatedAttribute,
final int descriptorIndex,
@ -2060,12 +2044,23 @@ final class MethodWriter extends MethodVisitor {
currentExceptionOffset += 2;
}
}
return true;
}
/**
* Sets the source from which the attributes of this method will be copied.
*
* @param methodInfoOffset the offset in 'symbolTable.getSource()' of the method_info JVMS
* structure from which the attributes of this method will be copied.
* @param methodInfoLength the length in 'symbolTable.getSource()' of the method_info JVMS
* structure from which the attributes of this method will be copied.
*/
void setMethodAttributesSource(final int methodInfoOffset, final int methodInfoLength) {
// Don't copy the attributes yet, instead store their location in the source class reader so
// they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes
// of the method_info JVMS structure.
this.sourceOffset = methodInfoOffset + 6;
this.sourceLength = methodInfoLength - 6;
return true;
}
/**
@ -2133,29 +2128,13 @@ final class MethodWriter extends MethodVisitor {
symbolTable.addConstantUtf8(Constants.EXCEPTIONS);
size += 8 + 2 * numberOfExceptions;
}
boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5;
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
symbolTable.addConstantUtf8(Constants.SYNTHETIC);
size += 6;
}
if (signatureIndex != 0) {
symbolTable.addConstantUtf8(Constants.SIGNATURE);
size += 8;
}
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
symbolTable.addConstantUtf8(Constants.DEPRECATED);
size += 6;
}
if (lastRuntimeVisibleAnnotation != null) {
size +=
lastRuntimeVisibleAnnotation.computeAnnotationsSize(
Constants.RUNTIME_VISIBLE_ANNOTATIONS);
}
if (lastRuntimeInvisibleAnnotation != null) {
size +=
lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
}
size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex);
size +=
AnnotationWriter.computeAnnotationsSize(
lastRuntimeVisibleAnnotation,
lastRuntimeInvisibleAnnotation,
lastRuntimeVisibleTypeAnnotation,
lastRuntimeInvisibleTypeAnnotation);
if (lastRuntimeVisibleParameterAnnotations != null) {
size +=
AnnotationWriter.computeParameterAnnotationsSize(
@ -2174,16 +2153,6 @@ final class MethodWriter extends MethodVisitor {
? lastRuntimeInvisibleParameterAnnotations.length
: invisibleAnnotableParameterCount);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
size +=
lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
size +=
lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
}
if (defaultValue != null) {
symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT);
size += 6 + defaultValue.length;
@ -2211,7 +2180,7 @@ final class MethodWriter extends MethodVisitor {
output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex);
// If this method_info must be copied from an existing one, copy it now and return early.
if (sourceOffset != 0) {
output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength);
output.putByteArray(symbolTable.getSource().classFileBuffer, sourceOffset, sourceLength);
return;
}
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
@ -2365,26 +2334,14 @@ final class MethodWriter extends MethodVisitor {
output.putShort(exceptionIndex);
}
}
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
}
if (signatureIndex != 0) {
output
.putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
.putInt(2)
.putShort(signatureIndex);
}
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
}
if (lastRuntimeVisibleAnnotation != null) {
lastRuntimeVisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
}
if (lastRuntimeInvisibleAnnotation != null) {
lastRuntimeInvisibleAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
}
Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output);
AnnotationWriter.putAnnotations(
symbolTable,
lastRuntimeVisibleAnnotation,
lastRuntimeInvisibleAnnotation,
lastRuntimeVisibleTypeAnnotation,
lastRuntimeInvisibleTypeAnnotation,
output);
if (lastRuntimeVisibleParameterAnnotations != null) {
AnnotationWriter.putParameterAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS),
@ -2403,14 +2360,6 @@ final class MethodWriter extends MethodVisitor {
: invisibleAnnotableParameterCount,
output);
}
if (lastRuntimeVisibleTypeAnnotation != null) {
lastRuntimeVisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
}
if (lastRuntimeInvisibleTypeAnnotation != null) {
lastRuntimeInvisibleTypeAnnotation.putAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
}
if (defaultValue != null) {
output
.putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT))

View File

@ -42,7 +42,9 @@ public abstract class ModuleVisitor {
*/
protected final int api;
/** The module visitor to which this visitor must delegate method calls. May be null. */
/**
* The module visitor to which this visitor must delegate method calls. May be {@literal null}.
*/
protected ModuleVisitor mv;
/**
@ -64,8 +66,8 @@ public abstract class ModuleVisitor {
* be null.
*/
public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) {
if (api != Opcodes.ASM6 && api != Opcodes.ASM7) {
throw new IllegalArgumentException();
if (api != Opcodes.ASM7 && api != Opcodes.ASM6) {
throw new IllegalArgumentException("Unsupported api " + api);
}
this.api = api;
this.mv = moduleVisitor;

View File

@ -48,8 +48,212 @@ public interface Opcodes {
int ASM6 = 6 << 16 | 0 << 8;
int ASM7 = 7 << 16 | 0 << 8;
// Java ClassFile versions (the minor version is stored in the 16 most
// significant bits, and the
/*
* Internal flags used to redirect calls to deprecated methods. For instance, if a visitOldStuff
* method in API_OLD is deprecated and replaced with visitNewStuff in API_NEW, then the
* redirection should be done as follows:
*
* <pre>
* public class StuffVisitor {
* ...
*
* &#64;Deprecated public void visitOldStuff(int arg, ...) {
* // SOURCE_DEPRECATED means "a call from a deprecated method using the old 'api' value".
* visitNewStuf(arg | (api &#60; API_NEW ? SOURCE_DEPRECATED : 0), ...);
* }
*
* public void visitNewStuff(int argAndSource, ...) {
* if (api &#60; API_NEW &#38;&#38; (argAndSource &#38; SOURCE_DEPRECATED) == 0) {
* visitOldStuff(argAndSource, ...);
* } else {
* int arg = argAndSource &#38; ~SOURCE_MASK;
* [ do stuff ]
* }
* }
* }
* </pre>
*
* <p>If 'api' is equal to API_NEW, there are two cases:
*
* <ul>
* <li>call visitNewStuff: the redirection test is skipped and 'do stuff' is executed directly.
* <li>call visitOldSuff: the source is not set to SOURCE_DEPRECATED before calling
* visitNewStuff, but the redirection test is skipped anyway in visitNewStuff, which
* directly executes 'do stuff'.
* </ul>
*
* <p>If 'api' is equal to API_OLD, there are two cases:
*
* <ul>
* <li>call visitOldSuff: the source is set to SOURCE_DEPRECATED before calling visitNewStuff.
* Because of this visitNewStuff does not redirect back to visitOldStuff, and instead
* executes 'do stuff'.
* <li>call visitNewStuff: the call is redirected to visitOldStuff because the source is 0.
* visitOldStuff now sets the source to SOURCE_DEPRECATED and calls visitNewStuff back. This
* time visitNewStuff does not redirect the call, and instead executes 'do stuff'.
* </ul>
*
* <h1>User subclasses</h1>
*
* <p>If a user subclass overrides one of these methods, there are only two cases: either 'api' is
* API_OLD and visitOldStuff is overridden (and visitNewStuff is not), or 'api' is API_NEW or
* more, and visitNewStuff is overridden (and visitOldStuff is not). Any other case is a user
* programming error.
*
* <p>If 'api' is equal to API_NEW, the class hierarchy is equivalent to
*
* <pre>
* public class StuffVisitor {
* &#64;Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); }
* public void visitNewStuff(int arg, ...) { [ do stuff ] }
* }
* class UserStuffVisitor extends StuffVisitor {
* &#64;Override public void visitNewStuff(int arg, ...) {
* super.visitNewStuff(int arg, ...); // optional
* [ do user stuff ]
* }
* }
* </pre>
*
* <p>It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff' and 'do
* user stuff' will be executed, in this order.
*
* <p>If 'api' is equal to API_OLD, the class hierarchy is equivalent to
*
* <pre>
* public class StuffVisitor {
* &#64;Deprecated public void visitOldStuff(int arg, ...) {
* visitNewStuf(arg | SOURCE_DEPRECATED, ...);
* }
* public void visitNewStuff(int argAndSource...) {
* if ((argAndSource & SOURCE_DEPRECATED) == 0) {
* visitOldStuff(argAndSource, ...);
* } else {
* int arg = argAndSource &#38; ~SOURCE_MASK;
* [ do stuff ]
* }
* }
* }
* class UserStuffVisitor extends StuffVisitor {
* &#64;Override public void visitOldStuff(int arg, ...) {
* super.visitOldStuff(int arg, ...); // optional
* [ do user stuff ]
* }
* }
* </pre>
*
* <p>and there are two cases:
*
* <ul>
* <li>call visitOldSuff: in the call to super.visitOldStuff, the source is set to
* SOURCE_DEPRECATED and visitNewStuff is called. Here 'do stuff' is run because the source
* was previously set to SOURCE_DEPRECATED, and execution eventually returns to
* UserStuffVisitor.visitOldStuff, where 'do user stuff' is run.
* <li>call visitNewStuff: the call is redirected to UserStuffVisitor.visitOldStuff because the
* source is 0. Execution continues as in the previous case, resulting in 'do stuff' and 'do
* user stuff' being executed, in this order.
* </ul>
*
* <h1>ASM subclasses</h1>
*
* <p>In ASM packages, subclasses of StuffVisitor can typically be sub classed again by the user,
* and can be used with API_OLD or API_NEW. Because of this, if such a subclass must override
* visitNewStuff, it must do so in the following way (and must not override visitOldStuff):
*
* <pre>
* public class AsmStuffVisitor extends StuffVisitor {
* &#64;Override public void visitNewStuff(int argAndSource, ...) {
* if (api &#60; API_NEW &#38;&#38; (argAndSource &#38; SOURCE_DEPRECATED) == 0) {
* super.visitNewStuff(argAndSource, ...);
* return;
* }
* super.visitNewStuff(argAndSource, ...); // optional
* int arg = argAndSource &#38; ~SOURCE_MASK;
* [ do other stuff ]
* }
* }
* </pre>
*
* <p>If a user class extends this with 'api' equal to API_NEW, the class hierarchy is equivalent
* to
*
* <pre>
* public class StuffVisitor {
* &#64;Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); }
* public void visitNewStuff(int arg, ...) { [ do stuff ] }
* }
* public class AsmStuffVisitor extends StuffVisitor {
* &#64;Override public void visitNewStuff(int arg, ...) {
* super.visitNewStuff(arg, ...);
* [ do other stuff ]
* }
* }
* class UserStuffVisitor extends StuffVisitor {
* &#64;Override public void visitNewStuff(int arg, ...) {
* super.visitNewStuff(int arg, ...);
* [ do user stuff ]
* }
* }
* </pre>
*
* <p>It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff', 'do
* other stuff' and 'do user stuff' will be executed, in this order. If, on the other hand, a user
* class extends AsmStuffVisitor with 'api' equal to API_OLD, the class hierarchy is equivalent to
*
* <pre>
* public class StuffVisitor {
* &#64;Deprecated public void visitOldStuff(int arg, ...) {
* visitNewStuf(arg | SOURCE_DEPRECATED, ...);
* }
* public void visitNewStuff(int argAndSource, ...) {
* if ((argAndSource & SOURCE_DEPRECATED) == 0) {
* visitOldStuff(argAndSource, ...);
* } else {
* int arg = argAndSource &#38; ~SOURCE_MASK;
* [ do stuff ]
* }
* }
* }
* public class AsmStuffVisitor extends StuffVisitor {
* &#64;Override public void visitNewStuff(int argAndSource, ...) {
* if ((argAndSource &#38; SOURCE_DEPRECATED) == 0) {
* super.visitNewStuff(argAndSource, ...);
* return;
* }
* super.visitNewStuff(argAndSource, ...); // optional
* int arg = argAndSource &#38; ~SOURCE_MASK;
* [ do other stuff ]
* }
* }
* class UserStuffVisitor extends StuffVisitor {
* &#64;Override public void visitOldStuff(int arg, ...) {
* super.visitOldStuff(arg, ...);
* [ do user stuff ]
* }
* }
* </pre>
*
* <p>and, here again, whether visitNewStuff or visitOldStuff is called, 'do stuff', 'do other
* stuff' and 'do user stuff' will be executed, in this order (exercise left to the reader).
*
* <h1>Notes</h1>
*
* <ul>
* <li>the SOURCE_DEPRECATED flag is set only if 'api' is API_OLD, just before calling
* visitNewStuff. By hypothesis, this method is not overridden by the user. Therefore, user
* classes can never see this flag. Only ASM subclasses must take care of extracting the
* actual argument value by clearing the source flags.
* <li>because the SOURCE_DEPRECATED flag is immediately cleared in the caller, the caller can
* call visitOldStuff or visitNewStuff (in 'do stuff' and 'do user stuff') on a delegate
* visitor without any risks (breaking the redirection logic, "leaking" the flag, etc).
* <li>all the scenarios discussed above are unit tested in MethodVisitorTest.
* </ul>
*/
int SOURCE_DEPRECATED = 0x100;
int SOURCE_MASK = SOURCE_DEPRECATED;
// Java ClassFile versions (the minor version is stored in the 16 most significant bits, and the
// major version in the 16 least significant bits).
int V1_1 = 3 << 16 | 45;
@ -64,6 +268,7 @@ public interface Opcodes {
int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
int V12 = 0 << 16 | 56;
int V13 = 0 << 16 | 57;
/**
* Version flag indicating that the class is using 'preview' features.

View File

@ -139,7 +139,7 @@ final class SymbolTable {
this.sourceClassReader = classReader;
// Copy the constant pool binary content.
byte[] inputBytes = classReader.b;
byte[] inputBytes = classReader.classFileBuffer;
int constantPoolOffset = classReader.getItem(1) - 1;
int constantPoolLength = classReader.header - constantPoolOffset;
constantPoolCount = classReader.getItemCount();
@ -242,7 +242,7 @@ final class SymbolTable {
*/
private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) {
// Find attributOffset of the 'bootstrap_methods' array.
byte[] inputBytes = classReader.b;
byte[] inputBytes = classReader.classFileBuffer;
int currentAttributeOffset = classReader.getFirstAttributeOffset();
for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer);
@ -1183,8 +1183,10 @@ final class SymbolTable {
* corresponding to the common super class of the given types.
*/
int addMergedType(final int typeTableIndex1, final int typeTableIndex2) {
// TODO sort the arguments? The merge result should be independent of their order.
long data = typeTableIndex1 | (((long) typeTableIndex2) << 32);
long data =
typeTableIndex1 < typeTableIndex2
? typeTableIndex1 | (((long) typeTableIndex2) << 32)
: typeTableIndex2 | (((long) typeTableIndex1) << 32);
int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2);
Entry entry = get(hashCode);
while (entry != null) {

View File

@ -363,6 +363,27 @@ public final class Type {
* @return the {@link Type} corresponding to the return type of the given method descriptor.
*/
public static Type getReturnType(final String methodDescriptor) {
return getTypeInternal(
methodDescriptor, getReturnTypeOffset(methodDescriptor), methodDescriptor.length());
}
/**
* Returns the {@link Type} corresponding to the return type of the given method.
*
* @param method a method.
* @return the {@link Type} corresponding to the return type of the given method.
*/
public static Type getReturnType(final Method method) {
return getType(method.getReturnType());
}
/**
* Returns the start index of the return type of the given method descriptor.
*
* @param methodDescriptor a method descriptor.
* @return the start index of the return type of the given method descriptor.
*/
static int getReturnTypeOffset(final String methodDescriptor) {
// Skip the first character, which is always a '('.
int currentOffset = 1;
// Skip the argument types, one at a each loop iteration.
@ -375,17 +396,7 @@ public final class Type {
currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
}
}
return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length());
}
/**
* Returns the {@link Type} corresponding to the return type of the given method.
*
* @param method a method.
* @return the {@link Type} corresponding to the return type of the given method.
*/
public static Type getReturnType(final Method method) {
return getType(method.getReturnType());
return currentOffset + 1;
}
/**
@ -505,11 +516,7 @@ public final class Type {
if (sort == OBJECT) {
return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
} else if (sort == INTERNAL) {
return new StringBuilder()
.append('L')
.append(valueBuffer, valueBegin, valueEnd)
.append(';')
.toString();
return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
} else {
return valueBuffer.substring(valueBegin, valueEnd);
}
@ -631,14 +638,7 @@ public final class Type {
}
stringBuilder.append(descriptor);
} else {
stringBuilder.append('L');
String name = currentClass.getName();
int nameLength = name.length();
for (int i = 0; i < nameLength; ++i) {
char car = name.charAt(i);
stringBuilder.append(car == '.' ? '/' : car);
}
stringBuilder.append(';');
stringBuilder.append('L').append(getInternalName(currentClass)).append(';');
}
}