parent
a06ab6d0ad
commit
f5248ff13f
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
* ...
|
||||
*
|
||||
* @Deprecated public void visitOldStuff(int arg, ...) {
|
||||
* // SOURCE_DEPRECATED means "a call from a deprecated method using the old 'api' value".
|
||||
* visitNewStuf(arg | (api < API_NEW ? SOURCE_DEPRECATED : 0), ...);
|
||||
* }
|
||||
*
|
||||
* public void visitNewStuff(int argAndSource, ...) {
|
||||
* if (api < API_NEW && (argAndSource & SOURCE_DEPRECATED) == 0) {
|
||||
* visitOldStuff(argAndSource, ...);
|
||||
* } else {
|
||||
* int arg = argAndSource & ~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 {
|
||||
* @Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); }
|
||||
* public void visitNewStuff(int arg, ...) { [ do stuff ] }
|
||||
* }
|
||||
* class UserStuffVisitor extends StuffVisitor {
|
||||
* @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 {
|
||||
* @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 & ~SOURCE_MASK;
|
||||
* [ do stuff ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* class UserStuffVisitor extends StuffVisitor {
|
||||
* @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 {
|
||||
* @Override public void visitNewStuff(int argAndSource, ...) {
|
||||
* if (api < API_NEW && (argAndSource & SOURCE_DEPRECATED) == 0) {
|
||||
* super.visitNewStuff(argAndSource, ...);
|
||||
* return;
|
||||
* }
|
||||
* super.visitNewStuff(argAndSource, ...); // optional
|
||||
* int arg = argAndSource & ~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 {
|
||||
* @Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); }
|
||||
* public void visitNewStuff(int arg, ...) { [ do stuff ] }
|
||||
* }
|
||||
* public class AsmStuffVisitor extends StuffVisitor {
|
||||
* @Override public void visitNewStuff(int arg, ...) {
|
||||
* super.visitNewStuff(arg, ...);
|
||||
* [ do other stuff ]
|
||||
* }
|
||||
* }
|
||||
* class UserStuffVisitor extends StuffVisitor {
|
||||
* @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 {
|
||||
* @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 & ~SOURCE_MASK;
|
||||
* [ do stuff ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* public class AsmStuffVisitor extends StuffVisitor {
|
||||
* @Override public void visitNewStuff(int argAndSource, ...) {
|
||||
* if ((argAndSource & SOURCE_DEPRECATED) == 0) {
|
||||
* super.visitNewStuff(argAndSource, ...);
|
||||
* return;
|
||||
* }
|
||||
* super.visitNewStuff(argAndSource, ...); // optional
|
||||
* int arg = argAndSource & ~SOURCE_MASK;
|
||||
* [ do other stuff ]
|
||||
* }
|
||||
* }
|
||||
* class UserStuffVisitor extends StuffVisitor {
|
||||
* @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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(';');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue