diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/AnnotationVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/AnnotationVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/AnnotationVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/AnnotationVisitor.java 2018-10-27 13:28:52.000000000 +0000
@@ -43,10 +43,7 @@
*/
protected final int api;
- /**
- * The annotation visitor to which this visitor must delegate method calls. May be {@literal
- * null}.
- */
+ /** The annotation visitor to which this visitor must delegate method calls. May be null. */
protected AnnotationVisitor av;
/**
@@ -65,20 +62,11 @@
* @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 {@literal null}.
+ * calls. May be null.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
- }
- if (api == Opcodes.ASM10_EXPERIMENTAL) {
- Constants.checkAsmExperimental(this);
+ if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
+ throw new IllegalArgumentException();
}
this.api = api;
this.av = annotationVisitor;
@@ -131,9 +119,9 @@
}
/**
- * Visits an array value of the annotation. Note that arrays of primitive values (such as byte,
+ * Visits an array value of the annotation. Note that arrays of primitive types (such as byte,
* boolean, short, char, int, long, float or double) can be passed as value to {@link #visit
- * visit}. This is what {@link ClassReader} does for non empty arrays of primitive values.
+ * visit}. This is what {@link ClassReader} does.
*
* @param name the value name.
* @return a visitor to visit the actual array value elements, or {@literal null} if this visitor
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/AnnotationWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/AnnotationWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/AnnotationWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/AnnotationWriter.java 2018-10-27 13:28:52.000000000 +0000
@@ -91,7 +91,7 @@
private AnnotationWriter nextAnnotation;
// -----------------------------------------------------------------------------------------------
- // Constructors and factories
+ // Constructors
// -----------------------------------------------------------------------------------------------
/**
@@ -104,15 +104,15 @@
* 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
- * {@literal null} in other cases (e.g. nested or array annotations).
+ * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
+ * other cases (e.g. nested or array annotations).
*/
AnnotationWriter(
final SymbolTable symbolTable,
final boolean useNamedValues,
final ByteVector annotation,
final AnnotationWriter previousAnnotation) {
- super(/* latest api = */ Opcodes.ASM9);
+ super(Opcodes.ASM7);
this.symbolTable = symbolTable;
this.useNamedValues = useNamedValues;
this.annotation = annotation;
@@ -125,61 +125,21 @@
}
/**
- * Creates a new {@link AnnotationWriter} using named values.
+ * Constructs a new {@link AnnotationWriter} using named values.
*
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
- * @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 annotation descriptor.
- */
- static AnnotationWriter create(
- final SymbolTable symbolTable,
- final String descriptor,
- final AnnotationWriter 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 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 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.
+ * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
+ * other cases (e.g. nested or array annotations).
*/
- static AnnotationWriter create(
+ AnnotationWriter(
final SymbolTable symbolTable,
- final int typeRef,
- final TypePath typePath,
- final String descriptor,
+ final ByteVector annotation,
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);
+ this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
}
// -----------------------------------------------------------------------------------------------
@@ -284,7 +244,7 @@
}
// 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, /* useNamedValues = */ true, annotation, null);
+ return new AnnotationWriter(symbolTable, annotation, null);
}
@Override
@@ -324,7 +284,7 @@
* and all its predecessors (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 {@literal null}.
+ * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or 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.
@@ -344,56 +304,6 @@
}
/**
- * Returns the size of the Runtime[In]Visible[Type]Annotations attributes containing the given
- * annotations and all their predecessors (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
* predecessors (see {@link #previousAnnotation} in the given ByteVector. Annotations are
* put in the same order they have been visited.
@@ -425,51 +335,6 @@
}
}
- /**
- * Puts the Runtime[In]Visible[Type]Annotations attributes containing the given annotations and
- * all their predecessors (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
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/Attribute.java asm-7.0/asm/src/main/java/org/objectweb/asm/Attribute.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/Attribute.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/Attribute.java 2018-10-27 13:28:52.000000000 +0000
@@ -28,7 +28,7 @@
package org.objectweb.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 JVMS
@@ -52,7 +52,7 @@
/**
* 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 @@
}
/**
- * 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 @@
* 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 @@
* 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}. 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#b}. 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}, or -1 if the attribute to be read is not a Code attribute. The 6
+ * in {@link ClassReader#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 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 new {@link Attribute} object corresponding to the specified bytes.
*/
protected Attribute read(
@@ -127,7 +127,7 @@
final Label[] labels) {
Attribute attribute = new Attribute(type);
attribute.content = new byte[length];
- System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
+ System.arraycopy(classReader.b, offset, attribute.content, 0, length);
return attribute;
}
@@ -138,16 +138,16 @@
*
* @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 @@
* 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.
*/
@@ -228,42 +228,6 @@
}
/**
- * 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
* attribute.
@@ -285,16 +249,16 @@
* 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(
@@ -316,37 +280,6 @@
}
}
- /**
- * 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 {
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/ClassReader.java asm-7.0/asm/src/main/java/org/objectweb/asm/ClassReader.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/ClassReader.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/ClassReader.java 2018-10-27 13:28:52.000000000 +0000
@@ -50,11 +50,10 @@
public static final int SKIP_CODE = 1;
/**
- * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable,
- * LocalVariableTypeTable, LineNumberTable and MethodParameters attributes. If this flag is set
- * these attributes are neither parsed nor visited (i.e. {@link ClassVisitor#visitSource}, {@link
- * MethodVisitor#visitLocalVariable}, {@link MethodVisitor#visitLineNumber} and {@link
- * MethodVisitor#visitParameter} are not called).
+ * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable
+ * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor
+ * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and
+ * {@link MethodVisitor#visitLineNumber} are not called).
*/
public static final int SKIP_DEBUG = 2;
@@ -92,19 +91,6 @@
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;
-
- /** The offset in bytes of the ClassFile's access_flags field. */
- public final int header;
-
- /**
* A byte array containing the JVMS ClassFile structure to be parsed. The content of this array
* must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
* not needed by class visitors.
@@ -113,13 +99,13 @@
* necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
* ClassFile element offsets within this byte array.
*/
- final byte[] classFileBuffer;
+ // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
+ public final byte[] b;
/**
- * The offset in bytes, in {@link #classFileBuffer}, of each cp_info entry of the ClassFile's
- * constant_pool array, plus one. 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 #b}, of each cp_info entry of the ClassFile's constant_pool
+ * array, plus one. 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;
@@ -136,8 +122,8 @@
private final ConstantDynamic[] constantDynamicValues;
/**
- * The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
- * (in the BootstrapMethods attribute).
+ * The start offsets in {@link #b} of each element of the bootstrap_methods array (in the
+ * BootstrapMethods attribute).
*
* @see JVMS
* 4.7.23
@@ -150,6 +136,9 @@
*/
private final int maxStringLength;
+ /** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */
+ public final int header;
+
// -----------------------------------------------------------------------------------------------
// Constructors
// -----------------------------------------------------------------------------------------------
@@ -187,11 +176,10 @@
*/
ClassReader(
final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
- this.classFileBuffer = classFileBuffer;
- this.b = classFileBuffer;
+ 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.V17) {
+ if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
@@ -207,8 +195,8 @@
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;
@@ -224,12 +212,11 @@
break;
case Symbol.CONSTANT_DYNAMIC_TAG:
cpInfoSize = 5;
- hasBootstrapMethods = true;
hasConstantDynamic = true;
break;
case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
cpInfoSize = 5;
- hasBootstrapMethods = true;
+ hasConstantInvokeDynamic = true;
break;
case Symbol.CONSTANT_LONG_TAG:
case Symbol.CONSTANT_DOUBLE_TAG:
@@ -269,7 +256,9 @@
// Read the BootstrapMethods attribute, if any (only get the offset of each method).
bootstrapMethodOffsets =
- hasBootstrapMethods ? readBootstrapMethodsAttribute(currentMaxStringLength) : null;
+ (hasConstantDynamic | hasConstantInvokeDynamic)
+ ? readBootstrapMethodsAttribute(currentMaxStringLength)
+ : null;
}
/**
@@ -310,7 +299,8 @@
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) {
@@ -467,10 +457,6 @@
String nestHostClass = null;
// - The offset of the NestMembers attribute, or 0.
int nestMembersOffset = 0;
- // - The offset of the PermittedSubclasses attribute, or 0
- int permittedSubclassesOffset = 0;
- // - The offset of the Record attribute, or 0.
- int recordOffset = 0;
// - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
// This list in the reverse order or their order in the ClassFile structure.
Attribute attributes = null;
@@ -493,8 +479,6 @@
nestHostClass = readClass(currentAttributeOffset, charBuffer);
} else if (Constants.NEST_MEMBERS.equals(attributeName)) {
nestMembersOffset = currentAttributeOffset;
- } else if (Constants.PERMITTED_SUBCLASSES.equals(attributeName)) {
- permittedSubclassesOffset = currentAttributeOffset;
} else if (Constants.SIGNATURE.equals(attributeName)) {
signature = readUTF8(currentAttributeOffset, charBuffer);
} else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
@@ -506,18 +490,12 @@
} else if (Constants.SYNTHETIC.equals(attributeName)) {
accessFlags |= Opcodes.ACC_SYNTHETIC;
} else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
- if (attributeLength > classFileBuffer.length - currentAttributeOffset) {
- throw new IllegalArgumentException();
- }
sourceDebugExtension =
readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
} else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
} else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
- } else if (Constants.RECORD.equals(attributeName)) {
- recordOffset = currentAttributeOffset;
- accessFlags |= Opcodes.ACC_RECORD;
} else if (Constants.MODULE.equals(attributeName)) {
moduleOffset = currentAttributeOffset;
} else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
@@ -675,17 +653,6 @@
}
}
- // Visit the PermittedSubclasses attribute.
- if (permittedSubclassesOffset != 0) {
- int numberOfPermittedSubclasses = readUnsignedShort(permittedSubclassesOffset);
- int currentPermittedSubclassesOffset = permittedSubclassesOffset + 2;
- while (numberOfPermittedSubclasses-- > 0) {
- classVisitor.visitPermittedSubclass(
- readClass(currentPermittedSubclassesOffset, charBuffer));
- currentPermittedSubclassesOffset += 2;
- }
- }
-
// Visit the InnerClasses attribute.
if (innerClassesOffset != 0) {
int numberOfClasses = readUnsignedShort(innerClassesOffset);
@@ -700,15 +667,6 @@
}
}
- // Visit Record components.
- if (recordOffset != 0) {
- int recordComponentsCount = readUnsignedShort(recordOffset);
- recordOffset += 2;
- while (recordComponentsCount-- > 0) {
- recordOffset = readRecordComponent(classVisitor, context, recordOffset);
- }
- }
-
// Visit the fields and methods.
int fieldsCount = readUnsignedShort(currentOffset);
currentOffset += 2;
@@ -738,8 +696,7 @@
* 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 {@literal
- * null}.
+ * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
*/
private void readModuleAttributes(
final ClassVisitor classVisitor,
@@ -857,180 +814,6 @@
}
/**
- * Reads a record component and visit it.
- *
- * @param classVisitor the current class visitor
- * @param context information about the class being parsed.
- * @param recordComponentOffset the offset of the current record component.
- * @return the offset of the first byte following the record component.
- */
- private int readRecordComponent(
- final ClassVisitor classVisitor, final Context context, final int recordComponentOffset) {
- char[] charBuffer = context.charBuffer;
-
- int currentOffset = recordComponentOffset;
- String name = readUTF8(currentOffset, charBuffer);
- String descriptor = readUTF8(currentOffset + 2, charBuffer);
- currentOffset += 4;
-
- // Read the record component attributes (the variables are ordered as in Section 4.7 of the
- // JVMS).
-
- // Attribute offsets exclude the attribute_name_index and attribute_length fields.
- // - The string corresponding to the Signature attribute, or null.
- String signature = null;
- // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
- int runtimeVisibleAnnotationsOffset = 0;
- // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
- int runtimeInvisibleAnnotationsOffset = 0;
- // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
- int runtimeVisibleTypeAnnotationsOffset = 0;
- // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
- int runtimeInvisibleTypeAnnotationsOffset = 0;
- // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
- // This list in the reverse order or their order in the ClassFile structure.
- Attribute attributes = null;
-
- int attributesCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (attributesCount-- > 0) {
- // Read the attribute_info's attribute_name and attribute_length fields.
- String attributeName = readUTF8(currentOffset, charBuffer);
- int attributeLength = readInt(currentOffset + 2);
- currentOffset += 6;
- // The tests are sorted in decreasing frequency order (based on frequencies observed on
- // typical classes).
- if (Constants.SIGNATURE.equals(attributeName)) {
- signature = readUTF8(currentOffset, charBuffer);
- } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleTypeAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleTypeAnnotationsOffset = currentOffset;
- } else {
- Attribute attribute =
- readAttribute(
- context.attributePrototypes,
- attributeName,
- currentOffset,
- attributeLength,
- charBuffer,
- -1,
- null);
- attribute.nextAttribute = attributes;
- attributes = attribute;
- }
- currentOffset += attributeLength;
- }
-
- RecordComponentVisitor recordComponentVisitor =
- classVisitor.visitRecordComponent(name, descriptor, signature);
- if (recordComponentVisitor == null) {
- return currentOffset;
- }
-
- // Visit the RuntimeVisibleAnnotations attribute.
- if (runtimeVisibleAnnotationsOffset != 0) {
- int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
- int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
- while (numAnnotations-- > 0) {
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
- currentAnnotationOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- currentAnnotationOffset =
- readElementValues(
- recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- }
-
- // Visit the RuntimeInvisibleAnnotations attribute.
- if (runtimeInvisibleAnnotationsOffset != 0) {
- int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
- int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
- while (numAnnotations-- > 0) {
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
- currentAnnotationOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- currentAnnotationOffset =
- readElementValues(
- recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- }
-
- // Visit the RuntimeVisibleTypeAnnotations attribute.
- if (runtimeVisibleTypeAnnotationsOffset != 0) {
- int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
- int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
- while (numAnnotations-- > 0) {
- // Parse the target_type, target_info and target_path fields.
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
- currentAnnotationOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- currentAnnotationOffset =
- readElementValues(
- recordComponentVisitor.visitTypeAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- annotationDescriptor,
- /* visible = */ true),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- }
-
- // Visit the RuntimeInvisibleTypeAnnotations attribute.
- if (runtimeInvisibleTypeAnnotationsOffset != 0) {
- int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
- int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
- while (numAnnotations-- > 0) {
- // Parse the target_type, target_info and target_path fields.
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
- currentAnnotationOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- currentAnnotationOffset =
- readElementValues(
- recordComponentVisitor.visitTypeAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- annotationDescriptor,
- /* visible = */ false),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- }
-
- // Visit the non standard attributes.
- while (attributes != null) {
- // Copy and reset the nextAttribute field so that it can also be used in FieldWriter.
- Attribute nextAttribute = attributes.nextAttribute;
- attributes.nextAttribute = null;
- recordComponentVisitor.visitAttribute(attributes);
- attributes = nextAttribute;
- }
-
- // Visit the end of the field.
- recordComponentVisitor.visitEnd();
- return currentOffset;
- }
-
- /**
* Reads a JVMS field_info structure and makes the given visitor visit it.
*
* @param classVisitor the visitor that must visit the field.
@@ -1345,18 +1128,19 @@
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;
}
}
// Visit the MethodParameters attribute.
- if (methodParametersOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
+ if (methodParametersOffset != 0) {
int parametersCount = readByte(methodParametersOffset);
int currentParameterOffset = methodParametersOffset + 1;
while (parametersCount-- > 0) {
@@ -1505,23 +1289,20 @@
*
* @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 #classFileBuffer} of the Code attribute, excluding
- * its attribute_name_index and attribute_length fields.
+ * @param codeOffset the start offset in {@link #b} 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[] classBuffer = classFileBuffer;
+ final byte[] classFileBuffer = b;
final char[] charBuffer = context.charBuffer;
final int maxStack = readUnsignedShort(currentOffset);
final int maxLocals = readUnsignedShort(currentOffset + 2);
final int codeLength = readInt(currentOffset + 4);
currentOffset += 8;
- if (codeLength > classFileBuffer.length - currentOffset) {
- throw new IllegalArgumentException();
- }
// Read the bytecode 'code' array to create a label for each referenced instruction.
final int bytecodeStartOffset = currentOffset;
@@ -1529,115 +1310,115 @@
final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
while (currentOffset < bytecodeEndOffset) {
final int bytecodeOffset = currentOffset - bytecodeStartOffset;
- final int opcode = classBuffer[currentOffset] & 0xFF;
+ final int opcode = classFileBuffer[currentOffset] & 0xFF;
switch (opcode) {
- case Opcodes.NOP:
- case Opcodes.ACONST_NULL:
- case Opcodes.ICONST_M1:
- case Opcodes.ICONST_0:
- case Opcodes.ICONST_1:
- case Opcodes.ICONST_2:
- case Opcodes.ICONST_3:
- case Opcodes.ICONST_4:
- case Opcodes.ICONST_5:
- case Opcodes.LCONST_0:
- case Opcodes.LCONST_1:
- case Opcodes.FCONST_0:
- case Opcodes.FCONST_1:
- case Opcodes.FCONST_2:
- case Opcodes.DCONST_0:
- case Opcodes.DCONST_1:
- case Opcodes.IALOAD:
- case Opcodes.LALOAD:
- case Opcodes.FALOAD:
- case Opcodes.DALOAD:
- case Opcodes.AALOAD:
- case Opcodes.BALOAD:
- case Opcodes.CALOAD:
- case Opcodes.SALOAD:
- case Opcodes.IASTORE:
- case Opcodes.LASTORE:
- case Opcodes.FASTORE:
- case Opcodes.DASTORE:
- case Opcodes.AASTORE:
- case Opcodes.BASTORE:
- case Opcodes.CASTORE:
- case Opcodes.SASTORE:
- case Opcodes.POP:
- case Opcodes.POP2:
- case Opcodes.DUP:
- case Opcodes.DUP_X1:
- case Opcodes.DUP_X2:
- case Opcodes.DUP2:
- case Opcodes.DUP2_X1:
- case Opcodes.DUP2_X2:
- case Opcodes.SWAP:
- case Opcodes.IADD:
- case Opcodes.LADD:
- case Opcodes.FADD:
- case Opcodes.DADD:
- case Opcodes.ISUB:
- case Opcodes.LSUB:
- case Opcodes.FSUB:
- case Opcodes.DSUB:
- case Opcodes.IMUL:
- case Opcodes.LMUL:
- case Opcodes.FMUL:
- case Opcodes.DMUL:
- case Opcodes.IDIV:
- case Opcodes.LDIV:
- case Opcodes.FDIV:
- case Opcodes.DDIV:
- case Opcodes.IREM:
- case Opcodes.LREM:
- case Opcodes.FREM:
- case Opcodes.DREM:
- case Opcodes.INEG:
- case Opcodes.LNEG:
- case Opcodes.FNEG:
- case Opcodes.DNEG:
- case Opcodes.ISHL:
- case Opcodes.LSHL:
- case Opcodes.ISHR:
- case Opcodes.LSHR:
- case Opcodes.IUSHR:
- case Opcodes.LUSHR:
- case Opcodes.IAND:
- case Opcodes.LAND:
- case Opcodes.IOR:
- case Opcodes.LOR:
- case Opcodes.IXOR:
- case Opcodes.LXOR:
- case Opcodes.I2L:
- case Opcodes.I2F:
- case Opcodes.I2D:
- case Opcodes.L2I:
- case Opcodes.L2F:
- case Opcodes.L2D:
- case Opcodes.F2I:
- case Opcodes.F2L:
- case Opcodes.F2D:
- case Opcodes.D2I:
- case Opcodes.D2L:
- case Opcodes.D2F:
- case Opcodes.I2B:
- case Opcodes.I2C:
- case Opcodes.I2S:
- case Opcodes.LCMP:
- case Opcodes.FCMPL:
- case Opcodes.FCMPG:
- case Opcodes.DCMPL:
- case Opcodes.DCMPG:
- case Opcodes.IRETURN:
- case Opcodes.LRETURN:
- case Opcodes.FRETURN:
- case Opcodes.DRETURN:
- case Opcodes.ARETURN:
- case Opcodes.RETURN:
- case Opcodes.ARRAYLENGTH:
- case Opcodes.ATHROW:
- case Opcodes.MONITORENTER:
- case Opcodes.MONITOREXIT:
+ case Constants.NOP:
+ case Constants.ACONST_NULL:
+ case Constants.ICONST_M1:
+ case Constants.ICONST_0:
+ case Constants.ICONST_1:
+ case Constants.ICONST_2:
+ case Constants.ICONST_3:
+ case Constants.ICONST_4:
+ case Constants.ICONST_5:
+ case Constants.LCONST_0:
+ case Constants.LCONST_1:
+ case Constants.FCONST_0:
+ case Constants.FCONST_1:
+ case Constants.FCONST_2:
+ case Constants.DCONST_0:
+ case Constants.DCONST_1:
+ case Constants.IALOAD:
+ case Constants.LALOAD:
+ case Constants.FALOAD:
+ case Constants.DALOAD:
+ case Constants.AALOAD:
+ case Constants.BALOAD:
+ case Constants.CALOAD:
+ case Constants.SALOAD:
+ case Constants.IASTORE:
+ case Constants.LASTORE:
+ case Constants.FASTORE:
+ case Constants.DASTORE:
+ case Constants.AASTORE:
+ case Constants.BASTORE:
+ case Constants.CASTORE:
+ case Constants.SASTORE:
+ case Constants.POP:
+ case Constants.POP2:
+ case Constants.DUP:
+ case Constants.DUP_X1:
+ case Constants.DUP_X2:
+ case Constants.DUP2:
+ case Constants.DUP2_X1:
+ case Constants.DUP2_X2:
+ case Constants.SWAP:
+ case Constants.IADD:
+ case Constants.LADD:
+ case Constants.FADD:
+ case Constants.DADD:
+ case Constants.ISUB:
+ case Constants.LSUB:
+ case Constants.FSUB:
+ case Constants.DSUB:
+ case Constants.IMUL:
+ case Constants.LMUL:
+ case Constants.FMUL:
+ case Constants.DMUL:
+ case Constants.IDIV:
+ case Constants.LDIV:
+ case Constants.FDIV:
+ case Constants.DDIV:
+ case Constants.IREM:
+ case Constants.LREM:
+ case Constants.FREM:
+ case Constants.DREM:
+ case Constants.INEG:
+ case Constants.LNEG:
+ case Constants.FNEG:
+ case Constants.DNEG:
+ case Constants.ISHL:
+ case Constants.LSHL:
+ case Constants.ISHR:
+ case Constants.LSHR:
+ case Constants.IUSHR:
+ case Constants.LUSHR:
+ case Constants.IAND:
+ case Constants.LAND:
+ case Constants.IOR:
+ case Constants.LOR:
+ case Constants.IXOR:
+ case Constants.LXOR:
+ case Constants.I2L:
+ case Constants.I2F:
+ case Constants.I2D:
+ case Constants.L2I:
+ case Constants.L2F:
+ case Constants.L2D:
+ case Constants.F2I:
+ case Constants.F2L:
+ case Constants.F2D:
+ case Constants.D2I:
+ case Constants.D2L:
+ case Constants.D2F:
+ case Constants.I2B:
+ case Constants.I2C:
+ case Constants.I2S:
+ case Constants.LCMP:
+ case Constants.FCMPL:
+ case Constants.FCMPG:
+ case Constants.DCMPL:
+ case Constants.DCMPG:
+ case Constants.IRETURN:
+ case Constants.LRETURN:
+ case Constants.FRETURN:
+ case Constants.DRETURN:
+ case Constants.ARETURN:
+ case Constants.RETURN:
+ case Constants.ARRAYLENGTH:
+ case Constants.ATHROW:
+ case Constants.MONITORENTER:
+ case Constants.MONITOREXIT:
case Constants.ILOAD_0:
case Constants.ILOAD_1:
case Constants.ILOAD_2:
@@ -1680,24 +1461,24 @@
case Constants.ASTORE_3:
currentOffset += 1;
break;
- case Opcodes.IFEQ:
- case Opcodes.IFNE:
- case Opcodes.IFLT:
- case Opcodes.IFGE:
- case Opcodes.IFGT:
- case Opcodes.IFLE:
- case Opcodes.IF_ICMPEQ:
- case Opcodes.IF_ICMPNE:
- case Opcodes.IF_ICMPLT:
- case Opcodes.IF_ICMPGE:
- case Opcodes.IF_ICMPGT:
- case Opcodes.IF_ICMPLE:
- case Opcodes.IF_ACMPEQ:
- case Opcodes.IF_ACMPNE:
- case Opcodes.GOTO:
- case Opcodes.JSR:
- case Opcodes.IFNULL:
- case Opcodes.IFNONNULL:
+ case Constants.IFEQ:
+ case Constants.IFNE:
+ case Constants.IFLT:
+ case Constants.IFGE:
+ case Constants.IFGT:
+ case Constants.IFLE:
+ case Constants.IF_ICMPEQ:
+ case Constants.IF_ICMPNE:
+ case Constants.IF_ICMPLT:
+ case Constants.IF_ICMPGE:
+ case Constants.IF_ICMPGT:
+ case Constants.IF_ICMPLE:
+ case Constants.IF_ACMPEQ:
+ case Constants.IF_ACMPNE:
+ case Constants.GOTO:
+ case Constants.JSR:
+ case Constants.IFNULL:
+ case Constants.IFNONNULL:
createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
currentOffset += 3;
break;
@@ -1729,28 +1510,28 @@
currentOffset += 5;
break;
case Constants.WIDE:
- switch (classBuffer[currentOffset + 1] & 0xFF) {
- case Opcodes.ILOAD:
- case Opcodes.FLOAD:
- case Opcodes.ALOAD:
- case Opcodes.LLOAD:
- case Opcodes.DLOAD:
- case Opcodes.ISTORE:
- case Opcodes.FSTORE:
- case Opcodes.ASTORE:
- case Opcodes.LSTORE:
- case Opcodes.DSTORE:
- case Opcodes.RET:
+ switch (classFileBuffer[currentOffset + 1] & 0xFF) {
+ case Constants.ILOAD:
+ case Constants.FLOAD:
+ case Constants.ALOAD:
+ case Constants.LLOAD:
+ case Constants.DLOAD:
+ case Constants.ISTORE:
+ case Constants.FSTORE:
+ case Constants.ASTORE:
+ case Constants.LSTORE:
+ case Constants.DSTORE:
+ case Constants.RET:
currentOffset += 4;
break;
- case Opcodes.IINC:
+ case Constants.IINC:
currentOffset += 6;
break;
default:
throw new IllegalArgumentException();
}
break;
- case Opcodes.TABLESWITCH:
+ case Constants.TABLESWITCH:
// Skip 0 to 3 padding bytes.
currentOffset += 4 - (bytecodeOffset & 3);
// Read the default label and the number of table entries.
@@ -1763,7 +1544,7 @@
currentOffset += 4;
}
break;
- case Opcodes.LOOKUPSWITCH:
+ case Constants.LOOKUPSWITCH:
// Skip 0 to 3 padding bytes.
currentOffset += 4 - (bytecodeOffset & 3);
// Read the default label and the number of switch cases.
@@ -1776,44 +1557,44 @@
currentOffset += 8;
}
break;
- case Opcodes.ILOAD:
- case Opcodes.LLOAD:
- case Opcodes.FLOAD:
- case Opcodes.DLOAD:
- case Opcodes.ALOAD:
- case Opcodes.ISTORE:
- case Opcodes.LSTORE:
- case Opcodes.FSTORE:
- case Opcodes.DSTORE:
- case Opcodes.ASTORE:
- case Opcodes.RET:
- case Opcodes.BIPUSH:
- case Opcodes.NEWARRAY:
- case Opcodes.LDC:
+ case Constants.ILOAD:
+ case Constants.LLOAD:
+ case Constants.FLOAD:
+ case Constants.DLOAD:
+ case Constants.ALOAD:
+ case Constants.ISTORE:
+ case Constants.LSTORE:
+ case Constants.FSTORE:
+ case Constants.DSTORE:
+ case Constants.ASTORE:
+ case Constants.RET:
+ case Constants.BIPUSH:
+ case Constants.NEWARRAY:
+ case Constants.LDC:
currentOffset += 2;
break;
- case Opcodes.SIPUSH:
+ case Constants.SIPUSH:
case Constants.LDC_W:
case Constants.LDC2_W:
- case Opcodes.GETSTATIC:
- case Opcodes.PUTSTATIC:
- case Opcodes.GETFIELD:
- case Opcodes.PUTFIELD:
- case Opcodes.INVOKEVIRTUAL:
- case Opcodes.INVOKESPECIAL:
- case Opcodes.INVOKESTATIC:
- case Opcodes.NEW:
- case Opcodes.ANEWARRAY:
- case Opcodes.CHECKCAST:
- case Opcodes.INSTANCEOF:
- case Opcodes.IINC:
+ case Constants.GETSTATIC:
+ case Constants.PUTSTATIC:
+ case Constants.GETFIELD:
+ case Constants.PUTFIELD:
+ case Constants.INVOKEVIRTUAL:
+ case Constants.INVOKESPECIAL:
+ case Constants.INVOKESTATIC:
+ case Constants.NEW:
+ case Constants.ANEWARRAY:
+ case Constants.CHECKCAST:
+ case Constants.INSTANCEOF:
+ case Constants.IINC:
currentOffset += 3;
break;
- case Opcodes.INVOKEINTERFACE:
- case Opcodes.INVOKEDYNAMIC:
+ case Constants.INVOKEINTERFACE:
+ case Constants.INVOKEDYNAMIC:
currentOffset += 5;
break;
- case Opcodes.MULTIANEWARRAY:
+ case Constants.MULTIANEWARRAY:
currentOffset += 4;
break;
default:
@@ -1978,11 +1759,11 @@
// 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 (classBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
+ if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
int potentialBytecodeOffset = readUnsignedShort(offset + 1);
if (potentialBytecodeOffset >= 0
&& potentialBytecodeOffset < codeLength
- && (classBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
+ && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
== Opcodes.NEW) {
createLabel(potentialBytecodeOffset, labels);
}
@@ -2078,115 +1859,115 @@
}
// Visit the instruction at this bytecode offset.
- int opcode = classBuffer[currentOffset] & 0xFF;
+ int opcode = classFileBuffer[currentOffset] & 0xFF;
switch (opcode) {
- case Opcodes.NOP:
- case Opcodes.ACONST_NULL:
- case Opcodes.ICONST_M1:
- case Opcodes.ICONST_0:
- case Opcodes.ICONST_1:
- case Opcodes.ICONST_2:
- case Opcodes.ICONST_3:
- case Opcodes.ICONST_4:
- case Opcodes.ICONST_5:
- case Opcodes.LCONST_0:
- case Opcodes.LCONST_1:
- case Opcodes.FCONST_0:
- case Opcodes.FCONST_1:
- case Opcodes.FCONST_2:
- case Opcodes.DCONST_0:
- case Opcodes.DCONST_1:
- case Opcodes.IALOAD:
- case Opcodes.LALOAD:
- case Opcodes.FALOAD:
- case Opcodes.DALOAD:
- case Opcodes.AALOAD:
- case Opcodes.BALOAD:
- case Opcodes.CALOAD:
- case Opcodes.SALOAD:
- case Opcodes.IASTORE:
- case Opcodes.LASTORE:
- case Opcodes.FASTORE:
- case Opcodes.DASTORE:
- case Opcodes.AASTORE:
- case Opcodes.BASTORE:
- case Opcodes.CASTORE:
- case Opcodes.SASTORE:
- case Opcodes.POP:
- case Opcodes.POP2:
- case Opcodes.DUP:
- case Opcodes.DUP_X1:
- case Opcodes.DUP_X2:
- case Opcodes.DUP2:
- case Opcodes.DUP2_X1:
- case Opcodes.DUP2_X2:
- case Opcodes.SWAP:
- case Opcodes.IADD:
- case Opcodes.LADD:
- case Opcodes.FADD:
- case Opcodes.DADD:
- case Opcodes.ISUB:
- case Opcodes.LSUB:
- case Opcodes.FSUB:
- case Opcodes.DSUB:
- case Opcodes.IMUL:
- case Opcodes.LMUL:
- case Opcodes.FMUL:
- case Opcodes.DMUL:
- case Opcodes.IDIV:
- case Opcodes.LDIV:
- case Opcodes.FDIV:
- case Opcodes.DDIV:
- case Opcodes.IREM:
- case Opcodes.LREM:
- case Opcodes.FREM:
- case Opcodes.DREM:
- case Opcodes.INEG:
- case Opcodes.LNEG:
- case Opcodes.FNEG:
- case Opcodes.DNEG:
- case Opcodes.ISHL:
- case Opcodes.LSHL:
- case Opcodes.ISHR:
- case Opcodes.LSHR:
- case Opcodes.IUSHR:
- case Opcodes.LUSHR:
- case Opcodes.IAND:
- case Opcodes.LAND:
- case Opcodes.IOR:
- case Opcodes.LOR:
- case Opcodes.IXOR:
- case Opcodes.LXOR:
- case Opcodes.I2L:
- case Opcodes.I2F:
- case Opcodes.I2D:
- case Opcodes.L2I:
- case Opcodes.L2F:
- case Opcodes.L2D:
- case Opcodes.F2I:
- case Opcodes.F2L:
- case Opcodes.F2D:
- case Opcodes.D2I:
- case Opcodes.D2L:
- case Opcodes.D2F:
- case Opcodes.I2B:
- case Opcodes.I2C:
- case Opcodes.I2S:
- case Opcodes.LCMP:
- case Opcodes.FCMPL:
- case Opcodes.FCMPG:
- case Opcodes.DCMPL:
- case Opcodes.DCMPG:
- case Opcodes.IRETURN:
- case Opcodes.LRETURN:
- case Opcodes.FRETURN:
- case Opcodes.DRETURN:
- case Opcodes.ARETURN:
- case Opcodes.RETURN:
- case Opcodes.ARRAYLENGTH:
- case Opcodes.ATHROW:
- case Opcodes.MONITORENTER:
- case Opcodes.MONITOREXIT:
+ case Constants.NOP:
+ case Constants.ACONST_NULL:
+ case Constants.ICONST_M1:
+ case Constants.ICONST_0:
+ case Constants.ICONST_1:
+ case Constants.ICONST_2:
+ case Constants.ICONST_3:
+ case Constants.ICONST_4:
+ case Constants.ICONST_5:
+ case Constants.LCONST_0:
+ case Constants.LCONST_1:
+ case Constants.FCONST_0:
+ case Constants.FCONST_1:
+ case Constants.FCONST_2:
+ case Constants.DCONST_0:
+ case Constants.DCONST_1:
+ case Constants.IALOAD:
+ case Constants.LALOAD:
+ case Constants.FALOAD:
+ case Constants.DALOAD:
+ case Constants.AALOAD:
+ case Constants.BALOAD:
+ case Constants.CALOAD:
+ case Constants.SALOAD:
+ case Constants.IASTORE:
+ case Constants.LASTORE:
+ case Constants.FASTORE:
+ case Constants.DASTORE:
+ case Constants.AASTORE:
+ case Constants.BASTORE:
+ case Constants.CASTORE:
+ case Constants.SASTORE:
+ case Constants.POP:
+ case Constants.POP2:
+ case Constants.DUP:
+ case Constants.DUP_X1:
+ case Constants.DUP_X2:
+ case Constants.DUP2:
+ case Constants.DUP2_X1:
+ case Constants.DUP2_X2:
+ case Constants.SWAP:
+ case Constants.IADD:
+ case Constants.LADD:
+ case Constants.FADD:
+ case Constants.DADD:
+ case Constants.ISUB:
+ case Constants.LSUB:
+ case Constants.FSUB:
+ case Constants.DSUB:
+ case Constants.IMUL:
+ case Constants.LMUL:
+ case Constants.FMUL:
+ case Constants.DMUL:
+ case Constants.IDIV:
+ case Constants.LDIV:
+ case Constants.FDIV:
+ case Constants.DDIV:
+ case Constants.IREM:
+ case Constants.LREM:
+ case Constants.FREM:
+ case Constants.DREM:
+ case Constants.INEG:
+ case Constants.LNEG:
+ case Constants.FNEG:
+ case Constants.DNEG:
+ case Constants.ISHL:
+ case Constants.LSHL:
+ case Constants.ISHR:
+ case Constants.LSHR:
+ case Constants.IUSHR:
+ case Constants.LUSHR:
+ case Constants.IAND:
+ case Constants.LAND:
+ case Constants.IOR:
+ case Constants.LOR:
+ case Constants.IXOR:
+ case Constants.LXOR:
+ case Constants.I2L:
+ case Constants.I2F:
+ case Constants.I2D:
+ case Constants.L2I:
+ case Constants.L2F:
+ case Constants.L2D:
+ case Constants.F2I:
+ case Constants.F2L:
+ case Constants.F2D:
+ case Constants.D2I:
+ case Constants.D2L:
+ case Constants.D2F:
+ case Constants.I2B:
+ case Constants.I2C:
+ case Constants.I2S:
+ case Constants.LCMP:
+ case Constants.FCMPL:
+ case Constants.FCMPG:
+ case Constants.DCMPL:
+ case Constants.DCMPG:
+ case Constants.IRETURN:
+ case Constants.LRETURN:
+ case Constants.FRETURN:
+ case Constants.DRETURN:
+ case Constants.ARETURN:
+ case Constants.RETURN:
+ case Constants.ARRAYLENGTH:
+ case Constants.ATHROW:
+ case Constants.MONITORENTER:
+ case Constants.MONITOREXIT:
methodVisitor.visitInsn(opcode);
currentOffset += 1;
break;
@@ -2238,24 +2019,24 @@
methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
currentOffset += 1;
break;
- case Opcodes.IFEQ:
- case Opcodes.IFNE:
- case Opcodes.IFLT:
- case Opcodes.IFGE:
- case Opcodes.IFGT:
- case Opcodes.IFLE:
- case Opcodes.IF_ICMPEQ:
- case Opcodes.IF_ICMPNE:
- case Opcodes.IF_ICMPLT:
- case Opcodes.IF_ICMPGE:
- case Opcodes.IF_ICMPGT:
- case Opcodes.IF_ICMPLE:
- case Opcodes.IF_ACMPEQ:
- case Opcodes.IF_ACMPNE:
- case Opcodes.GOTO:
- case Opcodes.JSR:
- case Opcodes.IFNULL:
- case Opcodes.IFNONNULL:
+ case Constants.IFEQ:
+ case Constants.IFNE:
+ case Constants.IFLT:
+ case Constants.IFGE:
+ case Constants.IFGT:
+ case Constants.IFLE:
+ case Constants.IF_ICMPEQ:
+ case Constants.IF_ICMPNE:
+ case Constants.IF_ICMPLT:
+ case Constants.IF_ICMPGE:
+ case Constants.IF_ICMPGT:
+ case Constants.IF_ICMPLE:
+ case Constants.IF_ACMPEQ:
+ case Constants.IF_ACMPNE:
+ case Constants.GOTO:
+ case Constants.JSR:
+ case Constants.IFNULL:
+ case Constants.IFNONNULL:
methodVisitor.visitJumpInsn(
opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
currentOffset += 3;
@@ -2316,17 +2097,19 @@
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 ASM_GOTO_W 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 ASM_GOTO_W L:..., see MethodWriter), so we need to insert a frame
+ // here.
+ insertFrame = true;
+ currentOffset += 5;
+ break;
+ }
case Constants.WIDE:
- opcode = classBuffer[currentOffset + 1] & 0xFF;
+ opcode = classFileBuffer[currentOffset + 1] & 0xFF;
if (opcode == Opcodes.IINC) {
methodVisitor.visitIincInsn(
readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
@@ -2336,7 +2119,7 @@
currentOffset += 4;
}
break;
- case Opcodes.TABLESWITCH:
+ case Constants.TABLESWITCH:
{
// Skip 0 to 3 padding bytes.
currentOffset += 4 - (currentBytecodeOffset & 3);
@@ -2353,7 +2136,7 @@
methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
break;
}
- case Opcodes.LOOKUPSWITCH:
+ case Constants.LOOKUPSWITCH:
{
// Skip 0 to 3 padding bytes.
currentOffset += 4 - (currentBytecodeOffset & 3);
@@ -2371,31 +2154,32 @@
methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
break;
}
- case Opcodes.ILOAD:
- case Opcodes.LLOAD:
- case Opcodes.FLOAD:
- case Opcodes.DLOAD:
- case Opcodes.ALOAD:
- case Opcodes.ISTORE:
- case Opcodes.LSTORE:
- case Opcodes.FSTORE:
- case Opcodes.DSTORE:
- case Opcodes.ASTORE:
- case Opcodes.RET:
- methodVisitor.visitVarInsn(opcode, classBuffer[currentOffset + 1] & 0xFF);
+ case Constants.ILOAD:
+ case Constants.LLOAD:
+ case Constants.FLOAD:
+ case Constants.DLOAD:
+ case Constants.ALOAD:
+ case Constants.ISTORE:
+ case Constants.LSTORE:
+ case Constants.FSTORE:
+ case Constants.DSTORE:
+ case Constants.ASTORE:
+ case Constants.RET:
+ methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
currentOffset += 2;
break;
- case Opcodes.BIPUSH:
- case Opcodes.NEWARRAY:
- methodVisitor.visitIntInsn(opcode, classBuffer[currentOffset + 1]);
+ case Constants.BIPUSH:
+ case Constants.NEWARRAY:
+ methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
currentOffset += 2;
break;
- case Opcodes.SIPUSH:
+ case Constants.SIPUSH:
methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
currentOffset += 3;
break;
- case Opcodes.LDC:
- methodVisitor.visitLdcInsn(readConst(classBuffer[currentOffset + 1] & 0xFF, charBuffer));
+ case Constants.LDC:
+ methodVisitor.visitLdcInsn(
+ readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
currentOffset += 2;
break;
case Constants.LDC_W:
@@ -2403,14 +2187,14 @@
methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
currentOffset += 3;
break;
- case Opcodes.GETSTATIC:
- case Opcodes.PUTSTATIC:
- case Opcodes.GETFIELD:
- case Opcodes.PUTFIELD:
- case Opcodes.INVOKEVIRTUAL:
- case Opcodes.INVOKESPECIAL:
- case Opcodes.INVOKESTATIC:
- case Opcodes.INVOKEINTERFACE:
+ case Constants.GETSTATIC:
+ case Constants.PUTSTATIC:
+ case Constants.GETFIELD:
+ case Constants.PUTFIELD:
+ case Constants.INVOKEVIRTUAL:
+ case Constants.INVOKESPECIAL:
+ case Constants.INVOKESTATIC:
+ case Constants.INVOKEINTERFACE:
{
int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
@@ -2421,7 +2205,7 @@
methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
} else {
boolean isInterface =
- classBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+ classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
if (opcode == Opcodes.INVOKEINTERFACE) {
@@ -2431,7 +2215,7 @@
}
break;
}
- case Opcodes.INVOKEDYNAMIC:
+ case Constants.INVOKEDYNAMIC:
{
int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
@@ -2453,21 +2237,21 @@
currentOffset += 5;
break;
}
- case Opcodes.NEW:
- case Opcodes.ANEWARRAY:
- case Opcodes.CHECKCAST:
- case Opcodes.INSTANCEOF:
+ case Constants.NEW:
+ case Constants.ANEWARRAY:
+ case Constants.CHECKCAST:
+ case Constants.INSTANCEOF:
methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
currentOffset += 3;
break;
- case Opcodes.IINC:
+ case Constants.IINC:
methodVisitor.visitIincInsn(
- classBuffer[currentOffset + 1] & 0xFF, classBuffer[currentOffset + 2]);
+ classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
currentOffset += 3;
break;
- case Opcodes.MULTIANEWARRAY:
+ case Constants.MULTIANEWARRAY:
methodVisitor.visitMultiANewArrayInsn(
- readClass(currentOffset + 1, charBuffer), classBuffer[currentOffset + 3] & 0xFF);
+ readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
currentOffset += 4;
break;
default:
@@ -2771,7 +2555,7 @@
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(classFileBuffer, currentOffset);
+ TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset);
currentOffset += 1 + 2 * pathLength;
// Parse the type_index field.
String annotationDescriptor = readUTF8(currentOffset, charBuffer);
@@ -2804,7 +2588,7 @@
* -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 {@literal null}.
+ * Runtime[In]VisibleTypeAnnotations attribute, or 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.
@@ -2896,7 +2680,7 @@
// Parse and store the target_path structure.
int pathLength = readByte(currentOffset);
context.currentTypeAnnotationTargetPath =
- pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
+ pathLength == 0 ? null : new TypePath(b, currentOffset);
// Return the start offset of the rest of the type_annotation structure.
return currentOffset + 1 + 2 * pathLength;
}
@@ -2918,7 +2702,7 @@
final int runtimeParameterAnnotationsOffset,
final boolean visible) {
int currentOffset = runtimeParameterAnnotationsOffset;
- int numParameters = classFileBuffer[currentOffset++] & 0xFF;
+ int numParameters = b[currentOffset++] & 0xFF;
methodVisitor.visitAnnotableParameterCount(numParameters, visible);
char[] charBuffer = context.charBuffer;
for (int i = 0; i < numParameters; ++i) {
@@ -2973,7 +2757,7 @@
// Parse the array_value array.
while (numElementValuePairs-- > 0) {
currentOffset =
- readElementValue(annotationVisitor, currentOffset, /* elementName= */ null, charBuffer);
+ readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer);
}
}
if (annotationVisitor != null) {
@@ -2986,8 +2770,8 @@
* 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 #classFileBuffer} of the element_value
- * structure to be read.
+ * @param elementValueOffset the start offset in {@link #b} 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.
@@ -2999,7 +2783,7 @@
final char[] charBuffer) {
int currentOffset = elementValueOffset;
if (annotationVisitor == null) {
- switch (classFileBuffer[currentOffset] & 0xFF) {
+ switch (b[currentOffset] & 0xFF) {
case 'e': // enum_const_value
return currentOffset + 5;
case '@': // annotation_value
@@ -3010,7 +2794,7 @@
return currentOffset + 3;
}
}
- switch (classFileBuffer[currentOffset++] & 0xFF) {
+ switch (b[currentOffset++] & 0xFF) {
case 'B': // const_value_index, CONSTANT_Integer
annotationVisitor.visit(
elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
@@ -3076,7 +2860,7 @@
/* named = */ false,
charBuffer);
}
- switch (classFileBuffer[currentOffset] & 0xFF) {
+ switch (b[currentOffset] & 0xFF) {
case 'B':
byte[] byteValues = new byte[numValues];
for (int i = 0; i < numValues; i++) {
@@ -3238,9 +3022,9 @@
* 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 #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 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 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}.
@@ -3258,7 +3042,7 @@
int frameType;
if (compressed) {
// Read the frame_type field.
- frameType = classFileBuffer[currentOffset++] & 0xFF;
+ frameType = b[currentOffset++] & 0xFF;
} else {
frameType = Frame.FULL_FRAME;
context.currentFrameOffset = -1;
@@ -3353,7 +3137,7 @@
final char[] charBuffer,
final Label[] labels) {
int currentOffset = verificationTypeInfoOffset;
- int tag = classFileBuffer[currentOffset++] & 0xFF;
+ int tag = b[currentOffset++] & 0xFF;
switch (tag) {
case Frame.ITEM_TOP:
frame[index] = Opcodes.TOP;
@@ -3395,11 +3179,9 @@
// ----------------------------------------------------------------------------------------------
/**
- * Returns the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
- * field entry.
+ * Returns the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
*
- * @return the offset in {@link #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.
*/
final int getFirstAttributeOffset() {
// Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
@@ -3446,11 +3228,12 @@
*
* @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.
+ * @return the offsets of the bootstrap methods or null.
*/
private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
char[] charBuffer = new char[maxStringLength];
int currentAttributeOffset = getFirstAttributeOffset();
+ int[] currentBootstrapMethodOffsets = null;
for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
// Read the attribute_info's attribute_name and attribute_length fields.
String attributeName = readUTF8(currentAttributeOffset, charBuffer);
@@ -3458,39 +3241,37 @@
currentAttributeOffset += 6;
if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
// Read the num_bootstrap_methods field and create an array of this size.
- int[] result = new int[readUnsignedShort(currentAttributeOffset)];
+ currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)];
// Compute and store the offset of each 'bootstrap_methods' array field entry.
int currentBootstrapMethodOffset = currentAttributeOffset + 2;
- for (int j = 0; j < result.length; ++j) {
- result[j] = currentBootstrapMethodOffset;
+ for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) {
+ currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset;
// Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each),
// as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2).
currentBootstrapMethodOffset +=
4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
}
- return result;
+ return currentBootstrapMethodOffsets;
}
currentAttributeOffset += attributeLength;
}
- throw new IllegalArgumentException();
+ return null;
}
/**
- * Reads a non standard JVMS 'attribute' structure in {@link #classFileBuffer}.
+ * Reads a non standard JVMS 'attribute' structure in {@link #b}.
*
* @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 #classFileBuffer}.
- * 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 #b}. 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
- * #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 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 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.
@@ -3526,14 +3307,13 @@
}
/**
- * Returns the start offset in this {@link ClassReader} of a JVMS 'cp_info' structure (i.e. a
- * constant pool entry), plus one. This method is intended for {@link Attribute} sub classes,
- * and is normally not needed by class generators or adapters.
+ * Returns the start offset in {@link #b} of a JVMS 'cp_info' structure (i.e. a constant pool
+ * entry), plus one. This method is intended for {@link Attribute} sub classes, and is normally
+ * not needed by class generators or adapters.
*
* @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool
* table.
- * @return the start offset in this {@link ClassReader} of the corresponding JVMS 'cp_info'
- * structure, plus one.
+ * @return the start offset in {@link #b} of the corresponding JVMS 'cp_info' structure, plus one.
*/
public int getItem(final int constantPoolEntryIndex) {
return cpInfoOffsets[constantPoolEntryIndex];
@@ -3551,60 +3331,60 @@
}
/**
- * Reads a byte value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ * Reads a byte value in {@link #b}. This method is intended for {@link Attribute} sub classes,
+ * and is normally not needed by class generators or adapters.
*
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @param offset the start offset of the value to be read in {@link #b}.
* @return the read value.
*/
public int readByte(final int offset) {
- return classFileBuffer[offset] & 0xFF;
+ return b[offset] & 0xFF;
}
/**
- * Reads an unsigned short value in this {@link ClassReader}. This method is intended for
- * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.
+ * Reads an unsigned short value in {@link #b}. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
*
- * @param offset the start index of the value to be read in this {@link ClassReader}.
+ * @param offset the start index of the value to be read in {@link #b}.
* @return the read value.
*/
public int readUnsignedShort(final int offset) {
- byte[] classBuffer = classFileBuffer;
- return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);
+ byte[] classFileBuffer = b;
+ return ((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF);
}
/**
- * Reads a signed short value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ * Reads a signed short value in {@link #b}. This method is intended for {@link Attribute} sub
+ * classes, and is normally not needed by class generators or adapters.
*
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @param offset the start offset of the value to be read in {@link #b}.
* @return the read value.
*/
public short readShort(final int offset) {
- byte[] classBuffer = classFileBuffer;
- return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));
+ byte[] classFileBuffer = b;
+ return (short) (((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF));
}
/**
- * Reads a signed int value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ * Reads a signed int value in {@link #b}. This method is intended for {@link Attribute} sub
+ * classes, and is normally not needed by class generators or adapters.
*
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @param offset the start offset of the value to be read in {@link #b}.
* @return the read value.
*/
public int readInt(final int offset) {
- byte[] classBuffer = classFileBuffer;
- return ((classBuffer[offset] & 0xFF) << 24)
- | ((classBuffer[offset + 1] & 0xFF) << 16)
- | ((classBuffer[offset + 2] & 0xFF) << 8)
- | (classBuffer[offset + 3] & 0xFF);
+ byte[] classFileBuffer = b;
+ return ((classFileBuffer[offset] & 0xFF) << 24)
+ | ((classFileBuffer[offset + 1] & 0xFF) << 16)
+ | ((classFileBuffer[offset + 2] & 0xFF) << 8)
+ | (classFileBuffer[offset + 3] & 0xFF);
}
/**
- * Reads a signed long value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ * Reads a signed long value in {@link #b}. This method is intended for {@link Attribute} sub
+ * classes, and is normally not needed by class generators or adapters.
*
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @param offset the start offset of the value to be read in {@link #b}.
* @return the read value.
*/
public long readLong(final int offset) {
@@ -3614,12 +3394,11 @@
}
/**
- * Reads a CONSTANT_Utf8 constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
+ * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
*
- * @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 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 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.
@@ -3634,7 +3413,7 @@
}
/**
- * Reads a CONSTANT_Utf8 constant pool entry in {@link #classFileBuffer}.
+ * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}.
*
* @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool
* table.
@@ -3653,7 +3432,7 @@
}
/**
- * Reads an UTF8 string in {@link #classFileBuffer}.
+ * Reads an UTF8 string in {@link #b}.
*
* @param utfOffset the start offset of the UTF8 string to be read.
* @param utfLength the length of the UTF8 string to be read.
@@ -3665,20 +3444,20 @@
int currentOffset = utfOffset;
int endOffset = currentOffset + utfLength;
int strLength = 0;
- byte[] classBuffer = classFileBuffer;
+ byte[] classFileBuffer = b;
while (currentOffset < endOffset) {
- int currentByte = classBuffer[currentOffset++];
+ int currentByte = classFileBuffer[currentOffset++];
if ((currentByte & 0x80) == 0) {
charBuffer[strLength++] = (char) (currentByte & 0x7F);
} else if ((currentByte & 0xE0) == 0xC0) {
charBuffer[strLength++] =
- (char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F));
+ (char) (((currentByte & 0x1F) << 6) + (classFileBuffer[currentOffset++] & 0x3F));
} else {
charBuffer[strLength++] =
(char)
(((currentByte & 0xF) << 12)
- + ((classBuffer[currentOffset++] & 0x3F) << 6)
- + (classBuffer[currentOffset++] & 0x3F));
+ + ((classFileBuffer[currentOffset++] & 0x3F) << 6)
+ + (classFileBuffer[currentOffset++] & 0x3F));
}
}
return new String(charBuffer, 0, strLength);
@@ -3686,13 +3465,12 @@
/**
* Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
- * CONSTANT_Package constant pool entry in {@link #classFileBuffer}. This method is intended
- * for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @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.
+ * CONSTANT_Package constant pool entry in {@link #b}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @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 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.
@@ -3704,12 +3482,11 @@
}
/**
- * Reads a CONSTANT_Class constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
+ * Reads a CONSTANT_Class constant pool entry in {@link #b}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
*
- * @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 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 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.
@@ -3719,12 +3496,11 @@
}
/**
- * Reads a CONSTANT_Module constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
+ * Reads a CONSTANT_Module constant pool entry in {@link #b}. This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.
*
- * @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 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 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.
@@ -3734,12 +3510,11 @@
}
/**
- * Reads a CONSTANT_Package constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
+ * Reads a CONSTANT_Package constant pool entry in {@link #b}. This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.
*
- * @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 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 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.
@@ -3749,7 +3524,7 @@
}
/**
- * Reads a CONSTANT_Dynamic constant pool entry in {@link #classFileBuffer}.
+ * Reads a CONSTANT_Dynamic constant pool entry in {@link #b}.
*
* @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
* pool table.
@@ -3780,9 +3555,8 @@
}
/**
- * Reads a numeric or string constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
+ * Reads a numeric or string constant pool entry in {@link #b}. This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.
*
* @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
* CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
@@ -3795,7 +3569,7 @@
*/
public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
- switch (classFileBuffer[cpInfoOffset - 1]) {
+ switch (b[cpInfoOffset - 1]) {
case Symbol.CONSTANT_INTEGER_TAG:
return readInt(cpInfoOffset);
case Symbol.CONSTANT_FLOAT_TAG:
@@ -3818,7 +3592,7 @@
String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
boolean isInterface =
- classFileBuffer[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+ b[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
return new Handle(referenceKind, owner, name, descriptor, isInterface);
case Symbol.CONSTANT_DYNAMIC_TAG:
return readConstantDynamic(constantPoolEntryIndex, charBuffer);
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/ClassVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/ClassVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/ClassVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/ClassVisitor.java 2018-10-27 13:28:52.000000000 +0000
@@ -30,10 +30,9 @@
/**
* A visitor to visit a Java class. The methods of this class must be called in the following order:
* {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code
- * visitPermittedSubclass} ][ {@code visitOuterClass} ] ( {@code visitAnnotation} | {@code
- * visitTypeAnnotation} | {@code visitAttribute} )* ( {@code visitNestMember} | {@code
- * visitInnerClass} | {@code visitRecordComponent} | {@code visitField} | {@code visitMethod} )*
- * {@code visitEnd}.
+ * visitOuterClass} ] ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code
+ * visitAttribute} )* ( {@code visitNestMember} | {@code visitInnerClass} | {@code visitField} |
+ * {@code visitMethod} )* {@code visitEnd}.
*
* @author Eric Bruneton
*/
@@ -45,7 +44,7 @@
*/
protected final int api;
- /** The class visitor to which this visitor must delegate method calls. May be {@literal null}. */
+ /** The class visitor to which this visitor must delegate method calls. May be null. */
protected ClassVisitor cv;
/**
@@ -62,23 +61,13 @@
* Constructs a new {@link ClassVisitor}.
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7}, {@link
- * Opcodes#ASM8} or {@link Opcodes#ASM9}.
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
* @param classVisitor the class visitor to which this visitor must delegate method calls. May be
* null.
*/
public ClassVisitor(final int api, final ClassVisitor classVisitor) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
- }
- if (api == Opcodes.ASM10_EXPERIMENTAL) {
- Constants.checkAsmExperimental(this);
+ if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
+ throw new IllegalArgumentException();
}
this.api = api;
this.cv = classVisitor;
@@ -90,8 +79,7 @@
* @param version the class version. The minor version is stored in the 16 most significant bits,
* and the major version in the 16 least significant bits.
* @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if
- * the class is deprecated {@link Opcodes#ACC_DEPRECATED} or a record {@link
- * Opcodes#ACC_RECORD}.
+ * the class is deprecated.
* @param name the internal name of the class (see {@link Type#getInternalName()}).
* @param signature the signature of this class. May be {@literal null} if the class is not a
* generic one, and does not extend or implement generic classes or interfaces.
@@ -108,9 +96,6 @@
final String signature,
final String superName,
final String[] interfaces) {
- if (api < Opcodes.ASM8 && (access & Opcodes.ACC_RECORD) != 0) {
- throw new UnsupportedOperationException("Records requires ASM8");
- }
if (cv != null) {
cv.visit(version, access, name, signature, superName, interfaces);
}
@@ -142,7 +127,7 @@
*/
public ModuleVisitor visitModule(final String name, final int access, final String version) {
if (api < Opcodes.ASM6) {
- throw new UnsupportedOperationException("Module requires ASM6");
+ throw new UnsupportedOperationException("This feature requires ASM6");
}
if (cv != null) {
return cv.visitModule(name, access, version);
@@ -162,7 +147,7 @@
*/
public void visitNestHost(final String nestHost) {
if (api < Opcodes.ASM7) {
- throw new UnsupportedOperationException("NestHost requires ASM7");
+ throw new UnsupportedOperationException("This feature requires ASM7");
}
if (cv != null) {
cv.visitNestHost(nestHost);
@@ -218,7 +203,7 @@
public AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
if (api < Opcodes.ASM5) {
- throw new UnsupportedOperationException("TypeAnnotation requires ASM5");
+ throw new UnsupportedOperationException("This feature requires ASM5");
}
if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
@@ -248,7 +233,7 @@
*/
public void visitNestMember(final String nestMember) {
if (api < Opcodes.ASM7) {
- throw new UnsupportedOperationException("NestMember requires ASM7");
+ throw new UnsupportedOperationException("This feature requires ASM7");
}
if (cv != null) {
cv.visitNestMember(nestMember);
@@ -256,21 +241,6 @@
}
/**
- * Visits a permitted subclasses. A permitted subclass is one of the allowed subclasses of the
- * current class.
- *
- * @param permittedSubclass the internal name of a permitted subclass.
- */
- public void visitPermittedSubclass(final String permittedSubclass) {
- if (api < Opcodes.ASM9) {
- throw new UnsupportedOperationException("PermittedSubclasses requires ASM9");
- }
- if (cv != null) {
- cv.visitPermittedSubclass(permittedSubclass);
- }
- }
-
- /**
* Visits information about an inner class. This inner class is not necessarily a member of the
* class being visited.
*
@@ -290,27 +260,6 @@
}
/**
- * Visits a record component of the class.
- *
- * @param name the record component name.
- * @param descriptor the record component descriptor (see {@link Type}).
- * @param signature the record component signature. May be {@literal null} if the record component
- * type does not use generic types.
- * @return a visitor to visit this record component annotations and attributes, or {@literal null}
- * if this class visitor is not interested in visiting these annotations and attributes.
- */
- public RecordComponentVisitor visitRecordComponent(
- final String name, final String descriptor, final String signature) {
- if (api < Opcodes.ASM8) {
- throw new UnsupportedOperationException("Record requires ASM8");
- }
- if (cv != null) {
- return cv.visitRecordComponent(name, descriptor, signature);
- }
- return null;
- }
-
- /**
* Visits a field of the class.
*
* @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/ClassWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/ClassWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/ClassWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/ClassWriter.java 2018-10-27 13:28:52.000000000 +0000
@@ -79,8 +79,8 @@
/**
* The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
- * access flags, such as {@link Opcodes#ACC_DEPRECATED} or {}@link Opcodes#ACC_RECORD}, which are
- * removed when generating the ClassFile structure.
+ * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
+ * ClassFile structure.
*/
private int accessFlags;
@@ -177,26 +177,6 @@
/** The 'classes' array of the NestMembers attribute, or {@literal null}. */
private ByteVector nestMemberClasses;
- /** The number_of_classes field of the PermittedSubclasses attribute, or 0. */
- private int numberOfPermittedSubclasses;
-
- /** The 'classes' array of the PermittedSubclasses attribute, or {@literal null}. */
- private ByteVector permittedSubclasses;
-
- /**
- * The record components of this class, stored in a linked list of {@link RecordComponentWriter}
- * linked via their {@link RecordComponentWriter#delegate} field. This field stores the first
- * element of this list.
- */
- private RecordComponentWriter firstRecordComponent;
-
- /**
- * The record components of this class, stored in a linked list of {@link RecordComponentWriter}
- * linked via their {@link RecordComponentWriter#delegate} field. This field stores the last
- * element of this list.
- */
- private RecordComponentWriter lastRecordComponent;
-
/**
* The first non standard attribute of this class. The next ones can be accessed with the {@link
* Attribute#nextAttribute} field. May be {@literal null}.
@@ -254,7 +234,7 @@
* maximum stack size nor the stack frames will be computed for these methods.
*/
public ClassWriter(final ClassReader classReader, final int flags) {
- super(/* latest api = */ Opcodes.ASM9);
+ super(Opcodes.ASM7);
symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
if ((flags & COMPUTE_FRAMES) != 0) {
this.compute = MethodWriter.COMPUTE_ALL_FRAMES;
@@ -318,7 +298,7 @@
}
@Override
- public final void visitNestHost(final String nestHost) {
+ public void visitNestHost(final String nestHost) {
nestHostClassIndex = symbolTable.addConstantClass(nestHost).index;
}
@@ -333,26 +313,37 @@
@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 =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
+ new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
} else {
return lastRuntimeInvisibleAnnotation =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
+ new AnnotationWriter(symbolTable, annotation, 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 =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
} else {
return lastRuntimeInvisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
}
}
@@ -364,7 +355,7 @@
}
@Override
- public final void visitNestMember(final String nestMember) {
+ public void visitNestMember(final String nestMember) {
if (nestMemberClasses == null) {
nestMemberClasses = new ByteVector();
}
@@ -373,15 +364,6 @@
}
@Override
- public final void visitPermittedSubclass(final String permittedSubclass) {
- if (permittedSubclasses == null) {
- permittedSubclasses = new ByteVector();
- }
- ++numberOfPermittedSubclasses;
- permittedSubclasses.putShort(symbolTable.addConstantClass(permittedSubclass).index);
- }
-
- @Override
public final void visitInnerClass(
final String name, final String outerName, final String innerName, final int access) {
if (innerClasses == null) {
@@ -407,19 +389,6 @@
}
@Override
- public final RecordComponentVisitor visitRecordComponent(
- final String name, final String descriptor, final String signature) {
- RecordComponentWriter recordComponentWriter =
- new RecordComponentWriter(symbolTable, name, descriptor, signature);
- if (firstRecordComponent == null) {
- firstRecordComponent = recordComponentWriter;
- } else {
- lastRecordComponent.delegate = recordComponentWriter;
- }
- return lastRecordComponent = recordComponentWriter;
- }
-
- @Override
public final FieldVisitor visitField(
final int access,
final String name,
@@ -469,7 +438,7 @@
* @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() {
+ public byte[] toByteArray() throws ClassTooLargeException, MethodTooLargeException {
// 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,
@@ -489,7 +458,6 @@
size += methodWriter.computeMethodInfoSize();
methodWriter = (MethodWriter) methodWriter.mv;
}
-
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
int attributesCount = 0;
if (innerClasses != null) {
@@ -569,24 +537,6 @@
size += 8 + nestMemberClasses.length;
symbolTable.addConstantUtf8(Constants.NEST_MEMBERS);
}
- if (permittedSubclasses != null) {
- ++attributesCount;
- size += 8 + permittedSubclasses.length;
- symbolTable.addConstantUtf8(Constants.PERMITTED_SUBCLASSES);
- }
- int recordComponentCount = 0;
- int recordSize = 0;
- if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
- RecordComponentWriter recordComponentWriter = firstRecordComponent;
- while (recordComponentWriter != null) {
- ++recordComponentCount;
- recordSize += recordComponentWriter.computeRecordComponentInfoSize();
- recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
- }
- ++attributesCount;
- size += 8 + recordSize;
- symbolTable.addConstantUtf8(Constants.RECORD);
- }
if (firstAttribute != null) {
attributesCount += firstAttribute.getAttributeCount();
size += firstAttribute.computeAttributesSize(symbolTable);
@@ -667,13 +617,22 @@
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
}
- AnnotationWriter.putAnnotations(
- symbolTable,
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation,
- result);
+ 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);
+ }
symbolTable.putBootstrapMethods(result);
if (moduleWriter != null) {
moduleWriter.putAttributes(result);
@@ -691,24 +650,6 @@
.putShort(numberOfNestMemberClasses)
.putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length);
}
- if (permittedSubclasses != null) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.PERMITTED_SUBCLASSES))
- .putInt(permittedSubclasses.length + 2)
- .putShort(numberOfPermittedSubclasses)
- .putByteArray(permittedSubclasses.data, 0, permittedSubclasses.length);
- }
- if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.RECORD))
- .putInt(recordSize + 2)
- .putShort(recordComponentCount);
- RecordComponentWriter recordComponentWriter = firstRecordComponent;
- while (recordComponentWriter != null) {
- recordComponentWriter.putRecordComponentInfo(result);
- recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
- }
- }
if (firstAttribute != null) {
firstAttribute.putAttributes(symbolTable, result);
}
@@ -745,10 +686,6 @@
nestHostClassIndex = 0;
numberOfNestMemberClasses = 0;
nestMemberClasses = null;
- numberOfPermittedSubclasses = 0;
- permittedSubclasses = null;
- firstRecordComponent = null;
- lastRecordComponent = null;
firstAttribute = null;
compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
new ClassReader(classFile, 0, /* checkClassVersion = */ false)
@@ -777,11 +714,6 @@
methodWriter.collectAttributePrototypes(attributePrototypes);
methodWriter = (MethodWriter) methodWriter.mv;
}
- RecordComponentWriter recordComponentWriter = firstRecordComponent;
- while (recordComponentWriter != null) {
- recordComponentWriter.collectAttributePrototypes(attributePrototypes);
- recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
- }
return attributePrototypes.toArray();
}
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/Constants.java asm-7.0/asm/src/main/java/org/objectweb/asm/Constants.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/Constants.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/Constants.java 2018-10-27 13:28:52.000000000 +0000
@@ -27,11 +27,6 @@
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.regex.Pattern;
-
/**
* Defines additional JVM opcodes, access flags and constants which are not part of the ASM public
* API.
@@ -39,7 +34,7 @@
* @see JVMS 6
* @author Eric Bruneton
*/
-final class Constants {
+final class Constants implements Opcodes {
// The ClassFile attribute names, in the order they are defined in
// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300.
@@ -73,8 +68,6 @@
static final String MODULE_MAIN_CLASS = "ModuleMainClass";
static final String NEST_HOST = "NestHost";
static final String NEST_MEMBERS = "NestMembers";
- static final String PERMITTED_SUBCLASSES = "PermittedSubclasses";
- static final String RECORD = "Record";
// ASM specific access flags.
// WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard
@@ -147,7 +140,7 @@
// Constants to convert between normal and wide jump instructions.
// The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP.
- static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - Opcodes.GOTO;
+ static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO;
// Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa.
@@ -160,62 +153,25 @@
// ASM specific opcodes, used for long forward jump instructions.
- static final int ASM_IFEQ = Opcodes.IFEQ + ASM_OPCODE_DELTA;
- static final int ASM_IFNE = Opcodes.IFNE + ASM_OPCODE_DELTA;
- static final int ASM_IFLT = Opcodes.IFLT + ASM_OPCODE_DELTA;
- static final int ASM_IFGE = Opcodes.IFGE + ASM_OPCODE_DELTA;
- static final int ASM_IFGT = Opcodes.IFGT + ASM_OPCODE_DELTA;
- static final int ASM_IFLE = Opcodes.IFLE + ASM_OPCODE_DELTA;
- static final int ASM_IF_ICMPEQ = Opcodes.IF_ICMPEQ + ASM_OPCODE_DELTA;
- static final int ASM_IF_ICMPNE = Opcodes.IF_ICMPNE + ASM_OPCODE_DELTA;
- static final int ASM_IF_ICMPLT = Opcodes.IF_ICMPLT + ASM_OPCODE_DELTA;
- static final int ASM_IF_ICMPGE = Opcodes.IF_ICMPGE + ASM_OPCODE_DELTA;
- static final int ASM_IF_ICMPGT = Opcodes.IF_ICMPGT + ASM_OPCODE_DELTA;
- static final int ASM_IF_ICMPLE = Opcodes.IF_ICMPLE + ASM_OPCODE_DELTA;
- static final int ASM_IF_ACMPEQ = Opcodes.IF_ACMPEQ + ASM_OPCODE_DELTA;
- static final int ASM_IF_ACMPNE = Opcodes.IF_ACMPNE + ASM_OPCODE_DELTA;
- static final int ASM_GOTO = Opcodes.GOTO + ASM_OPCODE_DELTA;
- static final int ASM_JSR = Opcodes.JSR + ASM_OPCODE_DELTA;
- static final int ASM_IFNULL = Opcodes.IFNULL + ASM_IFNULL_OPCODE_DELTA;
- static final int ASM_IFNONNULL = Opcodes.IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
+ static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA;
+ static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA;
+ static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA;
+ static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA;
+ static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA;
+ static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA;
+ static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA;
+ static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA;
+ static final int ASM_JSR = JSR + ASM_OPCODE_DELTA;
+ static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA;
+ static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
static final int ASM_GOTO_W = 220;
private Constants() {}
-
- static void checkAsmExperimental(final Object caller) {
- Class> callerClass = caller.getClass();
- String internalName = callerClass.getName().replace('.', '/');
- if (!isWhitelisted(internalName)) {
- checkIsPreview(callerClass.getClassLoader().getResourceAsStream(internalName + ".class"));
- }
- }
-
- static boolean isWhitelisted(final String internalName) {
- if (!internalName.startsWith("org/objectweb/asm/")) {
- return false;
- }
- String member = "(Annotation|Class|Field|Method|Module|RecordComponent|Signature)";
- return internalName.contains("Test$")
- || Pattern.matches(
- "org/objectweb/asm/util/Trace" + member + "Visitor(\\$.*)?", internalName)
- || Pattern.matches(
- "org/objectweb/asm/util/Check" + member + "Adapter(\\$.*)?", internalName);
- }
-
- static void checkIsPreview(final InputStream classInputStream) {
- if (classInputStream == null) {
- throw new IllegalStateException("Bytecode not available, can't check class version");
- }
- int minorVersion;
- try (DataInputStream callerClassStream = new DataInputStream(classInputStream); ) {
- callerClassStream.readInt();
- minorVersion = callerClassStream.readUnsignedShort();
- } catch (IOException ioe) {
- throw new IllegalStateException("I/O error, can't check class version", ioe);
- }
- if (minorVersion != 0xFFFF) {
- throw new IllegalStateException(
- "ASM9_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
- }
- }
}
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/FieldVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/FieldVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/FieldVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/FieldVisitor.java 2018-10-27 13:28:52.000000000 +0000
@@ -38,20 +38,18 @@
/**
* The ASM API version implemented by this visitor. The value of this field must be one of {@link
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7}, {@link
- * Opcodes#ASM8} or {@link Opcodes#ASM9}.
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
*/
protected final int api;
- /** The field visitor to which this visitor must delegate method calls. May be {@literal null}. */
+ /** The field visitor to which this visitor must delegate method calls. May be null. */
protected FieldVisitor fv;
/**
* Constructs a new {@link FieldVisitor}.
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7}, {@link
- * Opcodes#ASM8} or {@link Opcodes#ASM9}.
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
*/
public FieldVisitor(final int api) {
this(api, null);
@@ -61,23 +59,13 @@
* Constructs a new {@link FieldVisitor}.
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
- * Opcodes#ASM8}.
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
* @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be
* null.
*/
public FieldVisitor(final int api, final FieldVisitor fieldVisitor) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
- }
- if (api == Opcodes.ASM10_EXPERIMENTAL) {
- Constants.checkAsmExperimental(this);
+ if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
+ throw new IllegalArgumentException();
}
this.api = api;
this.fv = fieldVisitor;
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/FieldWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/FieldWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/FieldWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/FieldWriter.java 2018-10-27 13:28:52.000000000 +0000
@@ -124,7 +124,7 @@
final String descriptor,
final String signature,
final Object constantValue) {
- super(/* latest api = */ Opcodes.ASM9);
+ super(Opcodes.ASM7);
this.symbolTable = symbolTable;
this.accessFlags = access;
this.nameIndex = symbolTable.addConstantUtf8(name);
@@ -143,26 +143,37 @@
@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 =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
+ new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
} else {
return lastRuntimeInvisibleAnnotation =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
+ new AnnotationWriter(symbolTable, annotation, 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 =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
} else {
return lastRuntimeInvisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
}
}
@@ -197,13 +208,44 @@
symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE);
size += 8;
}
- size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex);
- size +=
- AnnotationWriter.computeAnnotationsSize(
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation);
+ // 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);
+ }
if (firstAttribute != null) {
size += firstAttribute.computeAttributesSize(symbolTable);
}
@@ -260,14 +302,34 @@
.putInt(2)
.putShort(constantValueIndex);
}
- Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output);
- AnnotationWriter.putAnnotations(
- symbolTable,
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation,
- output);
+ 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);
+ }
if (firstAttribute != null) {
firstAttribute.putAttributes(symbolTable, output);
}
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/Frame.java asm-7.0/asm/src/main/java/org/objectweb/asm/Frame.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/Frame.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/Frame.java 2018-10-27 13:28:52.000000000 +0000
@@ -54,19 +54,19 @@
*
*
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}.
+ *
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}.
*
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}.
- *
the FLAGS field, stored in 2 bits, contains up to 2 boolean flags. Currently only one flag
+ *
the FLAGS field, stored in 4 bits, contains up to 4 boolean flags. Currently only one flag
* is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}.
*
the VALUE field, stored in the remaining 20 bits, contains either
*
@@ -89,9 +89,9 @@
*
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 {@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).
+ * 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).
*
*
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,25 +129,18 @@
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 = ((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;
+ 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;
// 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;
@@ -156,11 +149,11 @@
// Possible values for the KIND field of an abstract type.
- 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;
+ 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;
// Possible flags for the FLAGS field of an abstract type.
@@ -169,7 +162,7 @@
* 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 = 1 << FLAGS_SHIFT;
+ private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK;
// Useful predefined abstract types (all the possible CONSTANT_KIND types).
@@ -547,8 +540,7 @@
* @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) == '(' ? Type.getReturnTypeOffset(descriptor) : 0;
+ int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0;
int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset);
if (abstractType != 0) {
push(abstractType);
@@ -1112,42 +1104,6 @@
// -----------------------------------------------------------------------------------------------
/**
- * 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
* (the input and output frames of this {@link Frame} are never changed).
@@ -1181,7 +1137,29 @@
// value at the beginning of the block.
concreteOutputType = inputLocals[i];
} else {
- concreteOutputType = getConcreteOutputType(abstractOutputType, 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).
+ 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;
+ }
}
} else {
// If the local variable has never been assigned in this basic block, it is equal to its
@@ -1235,8 +1213,25 @@
// 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 concreteOutputType = getConcreteOutputType(abstractOutputType, numStack);
+ 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;
+ }
if (initializations != null) {
concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
}
@@ -1253,10 +1248,10 @@
* @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 {@literal null} array dimensions.
+ * #UNINITIALIZED_KIND} kind, with positive or 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 {@literal
- * null} array dimensions.
+ * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or 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.
*/
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/Label.java asm-7.0/asm/src/main/java/org/objectweb/asm/Label.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/Label.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/Label.java 2018-10-27 13:28:52.000000000 +0000
@@ -227,8 +227,7 @@
/**
* 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 {@literal
- * null}.
+ * the basic block corresponding to this label. This maximum is always positive or null.
*/
short outputStackMax;
@@ -265,12 +264,12 @@
Edge outgoingEdges;
/**
- * 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).
+ * 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).
*
*
List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
* MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/MethodVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/MethodVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/MethodVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/MethodVisitor.java 2018-10-27 13:28:52.000000000 +0000
@@ -56,9 +56,7 @@
*/
protected final int api;
- /**
- * The method visitor to which this visitor must delegate method calls. May be {@literal null}.
- */
+ /** The method visitor to which this visitor must delegate method calls. May be null. */
protected MethodVisitor mv;
/**
@@ -80,17 +78,8 @@
* be null.
*/
public MethodVisitor(final int api, final MethodVisitor methodVisitor) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
- }
- if (api == Opcodes.ASM10_EXPERIMENTAL) {
- Constants.checkAsmExperimental(this);
+ if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
+ throw new IllegalArgumentException();
}
this.api = api;
this.mv = methodVisitor;
@@ -103,7 +92,7 @@
/**
* Visits a parameter of this method.
*
- * @param name parameter name or {@literal null} if none is provided.
+ * @param name parameter name or 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}).
*/
@@ -406,8 +395,14 @@
@Deprecated
public void visitMethodInsn(
final int opcode, final String owner, final String name, final String descriptor) {
- int opcodeAndSource = opcode | (api < Opcodes.ASM5 ? Opcodes.SOURCE_DEPRECATED : 0);
- visitMethodInsn(opcodeAndSource, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
+ 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);
+ }
}
/**
@@ -427,15 +422,15 @@
final String name,
final String descriptor,
final boolean isInterface) {
- if (api < Opcodes.ASM5 && (opcode & Opcodes.SOURCE_DEPRECATED) == 0) {
+ if (api < Opcodes.ASM5) {
if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
- throw new UnsupportedOperationException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
+ throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
}
visitMethodInsn(opcode, owner, name, descriptor);
return;
}
if (mv != null) {
- mv.visitMethodInsn(opcode & ~Opcodes.SOURCE_MASK, owner, name, descriptor, isInterface);
+ mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
@@ -543,7 +538,7 @@
|| (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) {
throw new UnsupportedOperationException(REQUIRES_ASM5);
}
- if (api < Opcodes.ASM7 && value instanceof ConstantDynamic) {
+ if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) {
throw new UnsupportedOperationException("This feature requires ASM7");
}
if (mv != null) {
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/MethodWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/MethodWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/MethodWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/MethodWriter.java 2018-10-27 13:28:52.000000000 +0000
@@ -592,7 +592,7 @@
final String signature,
final String[] exceptions,
final int compute) {
- super(/* latest api = */ Opcodes.ASM9);
+ super(Opcodes.ASM7);
this.symbolTable = symbolTable;
this.accessFlags = "".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access;
this.nameIndex = symbolTable.addConstantUtf8(name);
@@ -654,26 +654,37 @@
@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 =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
+ new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
} else {
return lastRuntimeInvisibleAnnotation =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
+ new AnnotationWriter(symbolTable, annotation, 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 =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
} else {
return lastRuntimeInvisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
}
}
@@ -689,24 +700,27 @@
@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] =
- AnnotationWriter.create(
- symbolTable, annotationDescriptor, lastRuntimeVisibleParameterAnnotations[parameter]);
+ new AnnotationWriter(
+ symbolTable, annotation, lastRuntimeVisibleParameterAnnotations[parameter]);
} else {
if (lastRuntimeInvisibleParameterAnnotations == null) {
lastRuntimeInvisibleParameterAnnotations =
new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
return lastRuntimeInvisibleParameterAnnotations[parameter] =
- AnnotationWriter.create(
- symbolTable,
- annotationDescriptor,
- lastRuntimeInvisibleParameterAnnotations[parameter]);
+ new AnnotationWriter(
+ symbolTable, annotation, lastRuntimeInvisibleParameterAnnotations[parameter]);
}
}
@@ -775,9 +789,6 @@
}
visitFrameEnd();
} else {
- if (symbolTable.getMajorVersion() < Opcodes.V1_6) {
- throw new IllegalArgumentException("Class versions V1_5 or less must use F_NEW frames.");
- }
int offsetDelta;
if (stackMapTableEntries == null) {
stackMapTableEntries = new ByteVector();
@@ -1404,22 +1415,20 @@
@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 =
- AnnotationWriter.create(
- symbolTable,
- (typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8),
- typePath,
- descriptor,
- lastCodeRuntimeVisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
} else {
return lastCodeRuntimeInvisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable,
- (typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8),
- typePath,
- descriptor,
- lastCodeRuntimeInvisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@@ -1440,14 +1449,20 @@
@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 =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeVisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
} else {
return lastCodeRuntimeInvisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeInvisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@@ -1515,18 +1530,10 @@
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
if (visible) {
return lastCodeRuntimeVisibleTypeAnnotation =
- new AnnotationWriter(
- symbolTable,
- /* useNamedValues = */ true,
- typeAnnotation,
- lastCodeRuntimeVisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
} else {
return lastCodeRuntimeInvisibleTypeAnnotation =
- new AnnotationWriter(
- symbolTable,
- /* useNamedValues = */ true,
- typeAnnotation,
- lastCodeRuntimeInvisibleTypeAnnotation);
+ new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@@ -1997,6 +2004,10 @@
* 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
@@ -2013,6 +2024,8 @@
*/
boolean canCopyMethodAttributes(
final ClassReader source,
+ final int methodInfoOffset,
+ final int methodInfoLength,
final boolean hasSyntheticAttribute,
final boolean hasDeprecatedAttribute,
final int descriptorIndex,
@@ -2047,23 +2060,12 @@
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;
}
/**
@@ -2131,13 +2133,29 @@
symbolTable.addConstantUtf8(Constants.EXCEPTIONS);
size += 8 + 2 * numberOfExceptions;
}
- size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex);
- size +=
- AnnotationWriter.computeAnnotationsSize(
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation);
+ 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);
+ }
if (lastRuntimeVisibleParameterAnnotations != null) {
size +=
AnnotationWriter.computeParameterAnnotationsSize(
@@ -2156,6 +2174,16 @@
? 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;
@@ -2183,7 +2211,7 @@
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().classFileBuffer, sourceOffset, sourceLength);
+ output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength);
return;
}
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
@@ -2337,14 +2365,26 @@
output.putShort(exceptionIndex);
}
}
- Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output);
- AnnotationWriter.putAnnotations(
- symbolTable,
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation,
- output);
+ 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 (lastRuntimeVisibleParameterAnnotations != null) {
AnnotationWriter.putParameterAnnotations(
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS),
@@ -2363,6 +2403,14 @@
: 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))
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java 2018-10-27 13:28:52.000000000 +0000
@@ -42,9 +42,7 @@
*/
protected final int api;
- /**
- * The module visitor to which this visitor must delegate method calls. May be {@literal null}.
- */
+ /** The module visitor to which this visitor must delegate method calls. May be null. */
protected ModuleVisitor mv;
/**
@@ -66,17 +64,8 @@
* be null.
*/
public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
- }
- if (api == Opcodes.ASM10_EXPERIMENTAL) {
- Constants.checkAsmExperimental(this);
+ if (api != Opcodes.ASM6 && api != Opcodes.ASM7) {
+ throw new IllegalArgumentException();
}
this.api = api;
this.mv = moduleVisitor;
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/ModuleWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/ModuleWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/ModuleWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/ModuleWriter.java 2018-10-27 13:28:52.000000000 +0000
@@ -94,7 +94,7 @@
private int mainClassIndex;
ModuleWriter(final SymbolTable symbolTable, final int name, final int access, final int version) {
- super(/* latest api = */ Opcodes.ASM9);
+ super(Opcodes.ASM7);
this.symbolTable = symbolTable;
this.moduleNameIndex = name;
this.moduleFlags = access;
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/Opcodes.java asm-7.0/asm/src/main/java/org/objectweb/asm/Opcodes.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/Opcodes.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/Opcodes.java 2018-10-27 13:28:52.000000000 +0000
@@ -47,223 +47,9 @@
int ASM5 = 5 << 16 | 0 << 8;
int ASM6 = 6 << 16 | 0 << 8;
int ASM7 = 7 << 16 | 0 << 8;
- int ASM8 = 8 << 16 | 0 << 8;
- int ASM9 = 9 << 16 | 0 << 8;
- /**
- * Experimental, use at your own risk. This field will be renamed when it becomes stable, this
- * will break existing code using it. Only code compiled with --enable-preview can use this.
- *
- * @deprecated This API is experimental.
- */
- @Deprecated int ASM10_EXPERIMENTAL = 1 << 24 | 10 << 16 | 0 << 8;
-
- /*
- * 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:
- *
- *
- * 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 ]
- * }
- * }
- * }
- *
- *
- *
If 'api' is equal to API_NEW, there are two cases:
- *
- *
- *
call visitNewStuff: the redirection test is skipped and 'do stuff' is executed directly.
- *
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'.
- *
- *
- *
If 'api' is equal to API_OLD, there are two cases:
- *
- *
- *
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'.
- *
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'.
- *
- *
- *
User subclasses
- *
- *
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.
- *
- *
If 'api' is equal to API_NEW, the class hierarchy is equivalent to
- *
- *
- * 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 ]
- * }
- * }
- *
- *
- *
It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff' and 'do
- * user stuff' will be executed, in this order.
- *
- *
If 'api' is equal to API_OLD, the class hierarchy is equivalent to
- *
- *
- * public class StuffVisitor {
- * @Deprecated public void visitOldStuff(int arg, ...) {
- * visitNewStuff(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 ]
- * }
- * }
- *
- *
- *
and there are two cases:
- *
- *
- *
call visitOldStuff: 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.
- *
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.
- *
- *
- *
ASM subclasses
- *
- *
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):
- *
- *
- * 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 ]
- * }
- * }
- *
- *
- *
If a user class extends this with 'api' equal to API_NEW, the class hierarchy is equivalent
- * to
- *
- *
- * 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 ]
- * }
- * }
- *
- *
- *
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
- *
- *
- * 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 ]
- * }
- * }
- *
- *
- *
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).
- *
- *
Notes
- *
- *
- *
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.
- *
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).
- *
all the scenarios discussed above are unit tested in MethodVisitorTest.
- *
- */
-
- 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
+ // 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;
@@ -278,11 +64,6 @@
int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
int V12 = 0 << 16 | 56;
- int V13 = 0 << 16 | 57;
- int V14 = 0 << 16 | 58;
- int V15 = 0 << 16 | 59;
- int V16 = 0 << 16 | 60;
- int V17 = 0 << 16 | 61;
/**
* Version flag indicating that the class is using 'preview' features.
@@ -319,7 +100,7 @@
int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module *
int ACC_ANNOTATION = 0x2000; // class
int ACC_ENUM = 0x4000; // class(?) field inner
- int ACC_MANDATED = 0x8000; // field, method, parameter, module, module *
+ int ACC_MANDATED = 0x8000; // parameter, module, module *
int ACC_MODULE = 0x8000; // class
// ASM specific access flags.
@@ -327,7 +108,6 @@
// access flags, and also to make sure that these flags are automatically filtered out when
// written in class files (because access flags are stored using 16 bits only).
- int ACC_RECORD = 0x10000; // class
int ACC_DEPRECATED = 0x20000; // class, field, method
// Possible values for the type operand of the NEWARRAY instruction.
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/package.html asm-7.0/asm/src/main/java/org/objectweb/asm/package.html
--- asm-9.1/asm/src/main/java/org/objectweb/asm/package.html 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/package.html 2018-10-27 13:28:52.000000000 +0000
@@ -1,5 +1,4 @@
-
-
+
-
- Package org.objectweb.asm
-
Provides a small and fast bytecode manipulation framework.
@@ -54,7 +50,7 @@
In order to generate a class from scratch, only the {@link
org.objectweb.asm.ClassWriter ClassWriter} class is necessary. Indeed,
-in order to generate a class, one must just call its visitXxx
+in order to generate a class, one must just call its visitXxx
methods with the appropriate arguments to generate the desired fields
and methods.
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/RecordComponentVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/RecordComponentVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/RecordComponentVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/RecordComponentVisitor.java 1970-01-01 00:00:00.000000000 +0000
@@ -1,152 +0,0 @@
-// ASM: a very small and fast Java bytecode manipulation framework
-// Copyright (c) 2000-2011 INRIA, France Telecom
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// 1. Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-// 3. Neither the name of the copyright holders nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-// THE POSSIBILITY OF SUCH DAMAGE.
-package org.objectweb.asm;
-
-/**
- * A visitor to visit a record component. The methods of this class must be called in the following
- * order: ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code visitAttribute} )* {@code
- * visitEnd}.
- *
- * @author Remi Forax
- * @author Eric Bruneton
- */
-public abstract class RecordComponentVisitor {
- /**
- * The ASM API version implemented by this visitor. The value of this field must be one of {@link
- * Opcodes#ASM8} or {@link Opcodes#ASM9}.
- */
- protected final int api;
-
- /**
- * The record visitor to which this visitor must delegate method calls. May be {@literal null}.
- */
- /*package-private*/ RecordComponentVisitor delegate;
-
- /**
- * Constructs a new {@link RecordComponentVisitor}.
- *
- * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM8}
- * or {@link Opcodes#ASM9}.
- */
- public RecordComponentVisitor(final int api) {
- this(api, null);
- }
-
- /**
- * Constructs a new {@link RecordComponentVisitor}.
- *
- * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}.
- * @param recordComponentVisitor the record component visitor to which this visitor must delegate
- * method calls. May be null.
- */
- public RecordComponentVisitor(
- final int api, final RecordComponentVisitor recordComponentVisitor) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
- }
- if (api == Opcodes.ASM10_EXPERIMENTAL) {
- Constants.checkAsmExperimental(this);
- }
- this.api = api;
- this.delegate = recordComponentVisitor;
- }
-
- /**
- * The record visitor to which this visitor must delegate method calls. May be {@literal null}.
- *
- * @return the record visitor to which this visitor must delegate method calls or {@literal null}.
- */
- public RecordComponentVisitor getDelegate() {
- return delegate;
- }
-
- /**
- * Visits an annotation of the record component.
- *
- * @param descriptor the class descriptor of the annotation class.
- * @param visible {@literal true} if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
- * interested in visiting this annotation.
- */
- public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
- if (delegate != null) {
- return delegate.visitAnnotation(descriptor, visible);
- }
- return null;
- }
-
- /**
- * Visits an annotation on a type in the record component signature.
- *
- * @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 visible {@literal true} if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
- * interested in visiting this annotation.
- */
- public AnnotationVisitor visitTypeAnnotation(
- final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
- if (delegate != null) {
- return delegate.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
- }
- return null;
- }
-
- /**
- * Visits a non standard attribute of the record component.
- *
- * @param attribute an attribute.
- */
- public void visitAttribute(final Attribute attribute) {
- if (delegate != null) {
- delegate.visitAttribute(attribute);
- }
- }
-
- /**
- * Visits the end of the record component. This method, which is the last one to be called, is
- * used to inform the visitor that everything have been visited.
- */
- public void visitEnd() {
- if (delegate != null) {
- delegate.visitEnd();
- }
- }
-}
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/RecordComponentWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/RecordComponentWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/RecordComponentWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/RecordComponentWriter.java 1970-01-01 00:00:00.000000000 +0000
@@ -1,225 +0,0 @@
-// ASM: a very small and fast Java bytecode manipulation framework
-// Copyright (c) 2000-2011 INRIA, France Telecom
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// 1. Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-// 3. Neither the name of the copyright holders nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-// THE POSSIBILITY OF SUCH DAMAGE.
-package org.objectweb.asm;
-
-final class RecordComponentWriter extends RecordComponentVisitor {
- /** Where the constants used in this RecordComponentWriter must be stored. */
- private final SymbolTable symbolTable;
-
- // Note: fields are ordered as in the record_component_info structure, and those related to
- // attributes are ordered as in Section 4.7 of the JVMS.
-
- /** The name_index field of the Record attribute. */
- private final int nameIndex;
-
- /** The descriptor_index field of the the Record attribute. */
- private final int descriptorIndex;
-
- /**
- * The signature_index field of the Signature attribute of this record component, or 0 if there is
- * no Signature attribute.
- */
- private int signatureIndex;
-
- /**
- * The last runtime visible annotation of this record component. The previous ones can be accessed
- * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
- */
- private AnnotationWriter lastRuntimeVisibleAnnotation;
-
- /**
- * The last runtime invisible annotation of this record component. The previous ones can be
- * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
- */
- private AnnotationWriter lastRuntimeInvisibleAnnotation;
-
- /**
- * The last runtime visible type annotation of this record component. The previous ones can be
- * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
- */
- private AnnotationWriter lastRuntimeVisibleTypeAnnotation;
-
- /**
- * The last runtime invisible type annotation of this record component. The previous ones can be
- * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
- */
- private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
-
- /**
- * The first non standard attribute of this record component. The next ones can be accessed with
- * the {@link Attribute#nextAttribute} field. May be {@literal null}.
- *
- *
WARNING: this list stores the attributes in the reverse order of their visit.
- * firstAttribute is actually the last attribute visited in {@link #visitAttribute(Attribute)}.
- * The {@link #putRecordComponentInfo(ByteVector)} method writes the attributes in the order
- * defined by this list, i.e. in the reverse order specified by the user.
- */
- private Attribute firstAttribute;
-
- /**
- * Constructs a new {@link RecordComponentWriter}.
- *
- * @param symbolTable where the constants used in this RecordComponentWriter must be stored.
- * @param name the record component name.
- * @param descriptor the record component descriptor (see {@link Type}).
- * @param signature the record component signature. May be {@literal null}.
- */
- RecordComponentWriter(
- final SymbolTable symbolTable,
- final String name,
- final String descriptor,
- final String signature) {
- super(/* latest api = */ Opcodes.ASM9);
- this.symbolTable = symbolTable;
- this.nameIndex = symbolTable.addConstantUtf8(name);
- this.descriptorIndex = symbolTable.addConstantUtf8(descriptor);
- if (signature != null) {
- this.signatureIndex = symbolTable.addConstantUtf8(signature);
- }
- }
-
- // -----------------------------------------------------------------------------------------------
- // Implementation of the FieldVisitor abstract class
- // -----------------------------------------------------------------------------------------------
-
- @Override
- public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
- if (visible) {
- return lastRuntimeVisibleAnnotation =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
- } else {
- return lastRuntimeInvisibleAnnotation =
- AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
- }
- }
-
- @Override
- public AnnotationVisitor visitTypeAnnotation(
- final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
- if (visible) {
- return lastRuntimeVisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
- } else {
- return lastRuntimeInvisibleTypeAnnotation =
- AnnotationWriter.create(
- symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
- }
- }
-
- @Override
- public void visitAttribute(final Attribute attribute) {
- // Store the attributes in the reverse order of their visit by this method.
- attribute.nextAttribute = firstAttribute;
- firstAttribute = attribute;
- }
-
- @Override
- public void visitEnd() {
- // Nothing to do.
- }
-
- // -----------------------------------------------------------------------------------------------
- // Utility methods
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Returns the size of the record component JVMS structure generated by this
- * RecordComponentWriter. Also adds the names of the attributes of this record component in the
- * constant pool.
- *
- * @return the size in bytes of the record_component_info of the Record attribute.
- */
- int computeRecordComponentInfoSize() {
- // name_index, descriptor_index and attributes_count fields use 6 bytes.
- int size = 6;
- size += Attribute.computeAttributesSize(symbolTable, 0, signatureIndex);
- size +=
- AnnotationWriter.computeAnnotationsSize(
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation);
- if (firstAttribute != null) {
- size += firstAttribute.computeAttributesSize(symbolTable);
- }
- return size;
- }
-
- /**
- * Puts the content of the record component generated by this RecordComponentWriter into the given
- * ByteVector.
- *
- * @param output where the record_component_info structure must be put.
- */
- void putRecordComponentInfo(final ByteVector output) {
- output.putShort(nameIndex).putShort(descriptorIndex);
- // Compute and put the attributes_count field.
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
- int attributesCount = 0;
- if (signatureIndex != 0) {
- ++attributesCount;
- }
- if (lastRuntimeVisibleAnnotation != null) {
- ++attributesCount;
- }
- if (lastRuntimeInvisibleAnnotation != null) {
- ++attributesCount;
- }
- if (lastRuntimeVisibleTypeAnnotation != null) {
- ++attributesCount;
- }
- if (lastRuntimeInvisibleTypeAnnotation != null) {
- ++attributesCount;
- }
- if (firstAttribute != null) {
- attributesCount += firstAttribute.getAttributeCount();
- }
- output.putShort(attributesCount);
- Attribute.putAttributes(symbolTable, 0, signatureIndex, output);
- AnnotationWriter.putAnnotations(
- symbolTable,
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation,
- output);
- if (firstAttribute != null) {
- firstAttribute.putAttributes(symbolTable, output);
- }
- }
-
- /**
- * Collects the attributes of this record component into the given set of attribute prototypes.
- *
- * @param attributePrototypes a set of attribute prototypes.
- */
- final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
- attributePrototypes.addAttributes(firstAttribute);
- }
-}
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/signature/package.html asm-7.0/asm/src/main/java/org/objectweb/asm/signature/package.html
--- asm-9.1/asm/src/main/java/org/objectweb/asm/signature/package.html 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/signature/package.html 2018-10-27 13:28:52.000000000 +0000
@@ -1,5 +1,4 @@
-
-
+
-
- Package org.objectweb.asm.signature
-
Provides support for type signatures.
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/signature/SignatureVisitor.java asm-7.0/asm/src/main/java/org/objectweb/asm/signature/SignatureVisitor.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/signature/SignatureVisitor.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/signature/SignatureVisitor.java 2018-10-27 13:28:52.000000000 +0000
@@ -72,14 +72,8 @@
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
*/
public SignatureVisitor(final int api) {
- if (api != Opcodes.ASM9
- && api != Opcodes.ASM8
- && api != Opcodes.ASM7
- && api != Opcodes.ASM6
- && api != Opcodes.ASM5
- && api != Opcodes.ASM4
- && api != Opcodes.ASM10_EXPERIMENTAL) {
- throw new IllegalArgumentException("Unsupported api " + api);
+ if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
+ throw new IllegalArgumentException();
}
this.api = api;
}
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java asm-7.0/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java 2018-10-27 13:28:52.000000000 +0000
@@ -70,7 +70,7 @@
/** Constructs a new {@link SignatureWriter}. */
public SignatureWriter() {
- super(/* latest api =*/ Opcodes.ASM9);
+ super(Opcodes.ASM7);
}
// -----------------------------------------------------------------------------------------------
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/SymbolTable.java asm-7.0/asm/src/main/java/org/objectweb/asm/SymbolTable.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/SymbolTable.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/SymbolTable.java 2018-10-27 13:28:52.000000000 +0000
@@ -31,11 +31,11 @@
* The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type
* table entries of a class.
*
- * @author Eric Bruneton
* @see JVMS
* 4.4
* @see JVMS
* 4.7.23
+ * @author Eric Bruneton
*/
final class SymbolTable {
@@ -139,7 +139,7 @@
this.sourceClassReader = classReader;
// Copy the constant pool binary content.
- byte[] inputBytes = classReader.classFileBuffer;
+ byte[] inputBytes = classReader.b;
int constantPoolOffset = classReader.getItem(1) - 1;
int constantPoolLength = classReader.header - constantPoolOffset;
constantPoolCount = classReader.getItemCount();
@@ -242,7 +242,7 @@
*/
private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) {
// Find attributOffset of the 'bootstrap_methods' array.
- byte[] inputBytes = classReader.classFileBuffer;
+ byte[] inputBytes = classReader.b;
int currentAttributeOffset = classReader.getFirstAttributeOffset();
for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer);
@@ -1046,10 +1046,8 @@
// bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool
// and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified
// while adding the given bootstrap method to it, in the rest of this method.
- int numBootstrapArguments = bootstrapMethodArguments.length;
- int[] bootstrapMethodArgumentIndexes = new int[numBootstrapArguments];
- for (int i = 0; i < numBootstrapArguments; i++) {
- bootstrapMethodArgumentIndexes[i] = addConstant(bootstrapMethodArguments[i]).index;
+ for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
+ addConstant(bootstrapMethodArgument);
}
// Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to
@@ -1064,10 +1062,10 @@
bootstrapMethodHandle.getDesc(),
bootstrapMethodHandle.isInterface())
.index);
-
+ int numBootstrapArguments = bootstrapMethodArguments.length;
bootstrapMethodsAttribute.putShort(numBootstrapArguments);
- for (int i = 0; i < numBootstrapArguments; i++) {
- bootstrapMethodsAttribute.putShort(bootstrapMethodArgumentIndexes[i]);
+ for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
+ bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index);
}
// Compute the length and the hash code of the bootstrap method.
@@ -1185,10 +1183,8 @@
* corresponding to the common super class of the given types.
*/
int addMergedType(final int typeTableIndex1, final int typeTableIndex2) {
- long data =
- typeTableIndex1 < typeTableIndex2
- ? typeTableIndex1 | (((long) typeTableIndex2) << 32)
- : typeTableIndex2 | (((long) typeTableIndex1) << 32);
+ // TODO sort the arguments? The merge result should be independent of their order.
+ long data = typeTableIndex1 | (((long) typeTableIndex2) << 32);
int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2);
Entry entry = get(hashCode);
while (entry != null) {
diff -Nru asm-9.1/asm/src/main/java/org/objectweb/asm/Type.java asm-7.0/asm/src/main/java/org/objectweb/asm/Type.java
--- asm-9.1/asm/src/main/java/org/objectweb/asm/Type.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/main/java/org/objectweb/asm/Type.java 2018-10-27 13:28:52.000000000 +0000
@@ -305,8 +305,7 @@
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
- currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+ currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
}
++numArgumentTypes;
}
@@ -324,8 +323,7 @@
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
- currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+ currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
}
argumentTypes[currentArgumentTypeIndex++] =
getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
@@ -365,27 +363,6 @@
* @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.
@@ -395,11 +372,20 @@
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
- currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+ currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
}
}
- return 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());
}
/**
@@ -519,7 +505,11 @@
if (sort == OBJECT) {
return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
} else if (sort == INTERNAL) {
- return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
+ return new StringBuilder()
+ .append('L')
+ .append(valueBuffer, valueBegin, valueEnd)
+ .append(';')
+ .toString();
} else {
return valueBuffer.substring(valueBegin, valueEnd);
}
@@ -641,7 +631,14 @@
}
stringBuilder.append(descriptor);
} else {
- stringBuilder.append('L').append(getInternalName(currentClass)).append(';');
+ 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(';');
}
}
@@ -740,8 +737,7 @@
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
- currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+ currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
}
argumentsSize += 1;
}
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/AnnotationVisitorTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/AnnotationVisitorTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/AnnotationVisitorTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/AnnotationVisitorTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -27,38 +27,24 @@
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.objectweb.asm.test.AsmTest;
-import org.objectweb.asm.test.ClassFile;
/**
- * Unit tests for {@link AnnotationVisitor}.
+ * AnnotationVisitor tests.
*
* @author Eric Bruneton
*/
public class AnnotationVisitorTest extends AsmTest {
@Test
- public void testConstructor_validApi() {
- Executable constructor = () -> new AnnotationVisitor(Opcodes.ASM4) {};
-
- assertDoesNotThrow(constructor);
- }
-
- @Test
- public void testConstructor_invalidApi() {
- Executable constructor = () -> new AnnotationVisitor(0) {};
-
- Exception exception = assertThrows(IllegalArgumentException.class, constructor);
- assertEquals("Unsupported api 0", exception.getMessage());
+ public void testConstuctor() {
+ assertThrows(IllegalArgumentException.class, () -> new AnnotationVisitor(0) {});
+ assertThrows(IllegalArgumentException.class, () -> new AnnotationVisitor(Integer.MAX_VALUE) {});
}
/**
@@ -67,32 +53,25 @@
*/
@ParameterizedTest
@MethodSource("allClassesAndAllApis")
- public void testReadAndWrite_removeOrDeleteAnnotations(
- final PrecompiledClass classParameter, final Api apiParameter) {
+ public void testRemoveOrDelete(final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
- ClassWriter removedAnnotationsClassWriter = new ClassWriter(0);
- ClassWriter deletedAnnotationsClassWriter = new ClassWriter(0);
- ClassVisitor removeAnnotationsAdapter =
- new RemoveAnnotationsAdapter(apiParameter.value(), removedAnnotationsClassWriter);
- ClassVisitor deleteAnnotationsAdapter =
- new DeleteAnnotationsAdapter(apiParameter.value(), deletedAnnotationsClassWriter);
-
- Executable removeAnnotations = () -> classReader.accept(removeAnnotationsAdapter, 0);
- Executable deleteAnnotations = () -> classReader.accept(deleteAnnotationsAdapter, 0);
-
+ ClassWriter classWriter1 = new ClassWriter(0);
+ ClassWriter classWriter2 = new ClassWriter(0);
if (classParameter.isMoreRecentThan(apiParameter)) {
- Exception removeException =
- assertThrows(UnsupportedOperationException.class, removeAnnotations);
- Exception deleteException =
- assertThrows(UnsupportedOperationException.class, deleteAnnotations);
- assertTrue(removeException.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- assertTrue(deleteException.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
+ assertThrows(
+ RuntimeException.class,
+ () ->
+ classReader.accept(
+ new RemoveAnnotationsAdapter(apiParameter.value(), classWriter1), 0));
+ assertThrows(
+ RuntimeException.class,
+ () ->
+ classReader.accept(
+ new DeleteAnnotationsAdapter(apiParameter.value(), classWriter2), 0));
} else {
- assertDoesNotThrow(removeAnnotations);
- assertDoesNotThrow(deleteAnnotations);
- assertEquals(
- new ClassFile(removedAnnotationsClassWriter.toByteArray()),
- new ClassFile(deletedAnnotationsClassWriter.toByteArray()));
+ classReader.accept(new RemoveAnnotationsAdapter(apiParameter.value(), classWriter1), 0);
+ classReader.accept(new DeleteAnnotationsAdapter(apiParameter.value(), classWriter2), 0);
+ assertThatClass(classWriter1.toByteArray()).isEqualTo(classWriter2.toByteArray());
}
}
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/AttributeTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/AttributeTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/AttributeTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/AttributeTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -33,7 +33,7 @@
import org.junit.jupiter.api.Test;
/**
- * Unit tests for {@link Attribute}.
+ * Attribute unit tests.
*
* @author Eric Bruneton
*/
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/ByteVectorTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/ByteVectorTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/ByteVectorTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/ByteVectorTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -27,19 +27,17 @@
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
/**
- * Unit tests for {@link ByteVector}.
+ * ByteVector tests.
*
* @author Eric Bruneton
*/
@@ -48,135 +46,111 @@
@Test
public void testPutByte() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putByte(1);
-
- assertArrayEquals(new byte[] {1}, toArray(byteVector));
+ assertContains(byteVector, 1);
}
@Test
public void testPut11() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.put11(1, 2);
-
- assertArrayEquals(new byte[] {1, 2}, toArray(byteVector));
+ assertContains(byteVector, 1, 2);
}
@Test
public void testPutShort() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putShort(0x0102);
-
- assertArrayEquals(new byte[] {1, 2}, toArray(byteVector));
+ assertContains(byteVector, 1, 2);
}
@Test
public void testPut12() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.put12(1, 0x0203);
-
- assertArrayEquals(new byte[] {1, 2, 3}, toArray(byteVector));
+ assertContains(byteVector, 1, 2, 3);
}
@Test
public void testPut112() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.put112(1, 2, 0x0304);
-
- assertArrayEquals(new byte[] {1, 2, 3, 4}, toArray(byteVector));
+ assertContains(byteVector, 1, 2, 3, 4);
}
@Test
public void testPutInt() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putInt(0x01020304);
-
- assertArrayEquals(new byte[] {1, 2, 3, 4}, toArray(byteVector));
+ assertContains(byteVector, 1, 2, 3, 4);
}
@Test
public void testPut122() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.put122(1, 0x0203, 0x0405);
-
- assertArrayEquals(new byte[] {1, 2, 3, 4, 5}, toArray(byteVector));
+ assertContains(byteVector, 1, 2, 3, 4, 5);
}
@Test
public void testPutLong() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putLong(0x0102030405060708L);
-
- assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 7, 8}, toArray(byteVector));
+ assertContains(byteVector, 1, 2, 3, 4, 5, 6, 7, 8);
}
@Test
public void testPutUtf8_ascii() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putUTF8("abc");
+ assertContains(byteVector, 0, 3, 'a', 'b', 'c');
- assertArrayEquals(new byte[] {0, 3, 'a', 'b', 'c'}, toArray(byteVector));
- }
-
- @ParameterizedTest
- @ValueSource(ints = {65535, 65536})
- public void testPutUtf8_ascii_tooLarge(final int size) {
- ByteVector byteVector = new ByteVector(0);
- char[] charBuffer = new char[size];
- Arrays.fill(charBuffer, 'A');
-
- Executable putUtf8 = () -> byteVector.putUTF8(new String(charBuffer));
-
- if (size > 65535) {
- Exception exception = assertThrows(IllegalArgumentException.class, putUtf8);
- assertEquals("UTF8 string too large", exception.getMessage());
- } else {
- assertDoesNotThrow(putUtf8);
- }
+ char[] charBuffer = new char[65536];
+ assertThrows(IllegalArgumentException.class, () -> byteVector.putUTF8(new String(charBuffer)));
}
@Test
public void testPutUtf8_unicode() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putUTF8(new String(new char[] {'a', 0x0000, 0x0080, 0x0800}));
+ assertContains(byteVector, 0, 8, 'a', -64, -128, -62, -128, -32, -96, -128);
- assertArrayEquals(
- new byte[] {0, 8, 'a', -64, -128, -62, -128, -32, -96, -128}, toArray(byteVector));
- }
-
- @Test
- public void testPutUtf8_unicode_tooLarge() {
- ByteVector byteVector = new ByteVector(0);
char[] charBuffer = new char[32768];
Arrays.fill(charBuffer, (char) 0x07FF);
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class, () -> byteVector.putUTF8(new String(charBuffer)));
+ assertEquals("UTF8 string too large", thrown.getMessage());
+ }
- Executable putUtf8 = () -> byteVector.putUTF8(new String(charBuffer));
-
- Exception exception = assertThrows(IllegalArgumentException.class, putUtf8);
- assertEquals("UTF8 string too large", exception.getMessage());
+ @ParameterizedTest
+ @ValueSource(ints = {65535, 65536})
+ public void testPutUtf8_tooLong(final int size) {
+ ByteVector byteVector = new ByteVector(0);
+ char[] charBuffer = new char[size];
+ Arrays.fill(charBuffer, 'A');
+ String utf8 = new String(charBuffer);
+ if (size > 65535) {
+ IllegalArgumentException thrown =
+ assertThrows(IllegalArgumentException.class, () -> byteVector.putUTF8(utf8));
+ assertEquals("UTF8 string too large", thrown.getMessage());
+ } else {
+ byteVector.putUTF8(utf8);
+ }
}
@Test
public void testPutByteArray() {
ByteVector byteVector = new ByteVector(0);
-
byteVector.putByteArray(new byte[] {0, 1, 2, 3, 4, 5}, 1, 3);
-
- assertArrayEquals(new byte[] {1, 2, 3}, toArray(byteVector));
+ assertContains(byteVector, 1, 2, 3);
}
- private static byte[] toArray(final ByteVector byteVector) {
- byte[] result = new byte[byteVector.length];
- System.arraycopy(byteVector.data, 0, result, 0, byteVector.length);
- return result;
+ private void assertContains(final ByteVector byteVector, final int... values) {
+ assertTrue(byteVector.data.length >= values.length);
+ assertEquals(values.length, byteVector.length);
+ for (int i = 0; i < values.length; ++i) {
+ assertEquals(values[i], byteVector.data[i]);
+ }
}
}
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -27,7 +27,6 @@
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -35,96 +34,81 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assumptions.assumeFalse;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import static org.objectweb.asm.test.Assertions.assertThat;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.objectweb.asm.test.AsmTest;
/**
- * Unit tests for {@link ClassReader}.
+ * ClassReader tests.
*
* @author Eric Bruneton
*/
public class ClassReaderTest extends AsmTest implements Opcodes {
@Test
- public void testReadByte() throws IOException {
- ClassReader classReader = new ClassReader(getClass().getName());
-
- assertEquals(classReader.classFileBuffer[0] & 0xFF, classReader.readByte(0));
+ public void testIllegalConstructorArgument() {
+ assertThrows(IOException.class, () -> new ClassReader((InputStream) null));
}
@Test
public void testGetItem() throws IOException {
ClassReader classReader = new ClassReader(getClass().getName());
-
int item = classReader.getItem(1);
-
assertTrue(item >= 10);
assertTrue(item < classReader.header);
}
@Test
+ public void testReadByte() throws IOException {
+ ClassReader classReader = new ClassReader(getClass().getName());
+ assertEquals(classReader.b[0] & 0xFF, classReader.readByte(0));
+ }
+
+ @Test
public void testGetAccess() throws Exception {
String name = getClass().getName();
-
assertEquals(ACC_PUBLIC | ACC_SUPER, new ClassReader(name).getAccess());
}
@Test
public void testGetClassName() throws Exception {
String name = getClass().getName();
-
assertEquals(name.replace('.', '/'), new ClassReader(name).getClassName());
}
@Test
public void testGetSuperName() throws Exception {
- ClassReader thisClassReader = new ClassReader(getClass().getName());
- ClassReader objectClassReader = new ClassReader(Object.class.getName());
-
- assertEquals(AsmTest.class.getName().replace('.', '/'), thisClassReader.getSuperName());
- assertEquals(null, objectClassReader.getSuperName());
+ assertEquals(
+ AsmTest.class.getName().replace('.', '/'),
+ new ClassReader(getClass().getName()).getSuperName());
+ assertEquals(null, new ClassReader(Object.class.getName()).getSuperName());
}
@Test
public void testGetInterfaces() throws Exception {
- ClassReader classReader = new ClassReader(getClass().getName());
-
- String[] interfaces = classReader.getInterfaces();
-
+ String[] interfaces = new ClassReader(getClass().getName()).getInterfaces();
assertNotNull(interfaces);
assertEquals(1, interfaces.length);
assertEquals(Opcodes.class.getName().replace('.', '/'), interfaces[0]);
- }
-
- @Test
- public void testGetInterfaces_empty() throws Exception {
- ClassReader classReader = new ClassReader(Opcodes.class.getName());
-
- String[] interfaces = classReader.getInterfaces();
+ interfaces = new ClassReader(Opcodes.class.getName()).getInterfaces();
assertNotNull(interfaces);
}
- /** Tests {@link ClassReader#ClassReader(byte[])}. */
+ /** Tests {@link ClassReader#ClassReader(byte[])} and the basic ClassReader accessors. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testByteArrayConstructor(
+ public void testByteArrayConstructorAndAccessors(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
-
assertNotEquals(0, classReader.getAccess());
assertEquals(classParameter.getInternalName(), classReader.getClassName());
if (classParameter.getInternalName().equals("module-info")) {
@@ -138,14 +122,12 @@
/** Tests {@link ClassReader#ClassReader(byte[],int,int)} and the basic ClassReader accessors. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
- public void testByteArrayConstructor_withOffset(
+ public void testByteArrayConstructorWithOffsetAndAccessors(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
byte[] byteBuffer = new byte[classFile.length + 1];
System.arraycopy(classFile, 0, byteBuffer, 1, classFile.length);
-
ClassReader classReader = new ClassReader(byteBuffer, 1, classFile.length);
-
assertNotEquals(0, classReader.getAccess());
assertEquals(classParameter.getInternalName(), classReader.getClassName());
if (classParameter.getInternalName().equals("module-info")) {
@@ -154,7 +136,7 @@
assertTrue(classReader.getSuperName().startsWith("java"));
}
assertNotNull(classReader.getInterfaces());
- AtomicInteger classVersion = new AtomicInteger(0);
+
classReader.accept(
new ClassVisitor(apiParameter.value()) {
@Override
@@ -165,36 +147,18 @@
final String signature,
final String superName,
final String[] interfaces) {
- classVersion.set(version);
+ assertTrue((version & 0xFFFF) >= (Opcodes.V1_1 & 0xFFFF));
}
},
0);
- assertTrue((classVersion.get() & 0xFFFF) >= (Opcodes.V1_1 & 0xFFFF));
- }
-
- /**
- * Tests that constructing a ClassReader fails if the class version or constant pool is invalid or
- * not supported.
- */
- @ParameterizedTest
- @EnumSource(InvalidClass.class)
- public void testByteArrayConstructor_invalidClassHeader(final InvalidClass invalidClass) {
- assumeTrue(
- invalidClass == InvalidClass.INVALID_CLASS_VERSION
- || invalidClass == InvalidClass.INVALID_CP_INFO_TAG);
-
- Executable constructor = () -> new ClassReader(invalidClass.getBytes());
-
- assertThrows(IllegalArgumentException.class, constructor);
}
/** Tests {@link ClassReader#ClassReader(String)} and the basic ClassReader accessors. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testStringConstructor(final PrecompiledClass classParameter, final Api apiParameter)
- throws IOException {
+ public void testNameConstructorAndAccessors(
+ final PrecompiledClass classParameter, final Api apiParameter) throws IOException {
ClassReader classReader = new ClassReader(classParameter.getName());
-
assertNotEquals(0, classReader.getAccess());
assertEquals(classParameter.getInternalName(), classReader.getClassName());
if (classParameter.getInternalName().equals("module-info")) {
@@ -210,17 +174,12 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testStreamConstructor(final PrecompiledClass classParameter, final Api apiParameter)
- throws IOException {
- ClassReader classReader;
- try (InputStream inputStream =
- ClassLoader.getSystemResourceAsStream(
- classParameter.getName().replace('.', '/') + ".class")) {
- classReader = new ClassReader(inputStream);
- } catch (IOException ioe) {
- throw ioe;
- }
-
+ public void testStreamConstructorAndAccessors(
+ final PrecompiledClass classParameter, final Api apiParameter) throws IOException {
+ ClassReader classReader =
+ new ClassReader(
+ ClassLoader.getSystemResourceAsStream(
+ classParameter.getName().replace('.', '/') + ".class"));
assertNotEquals(0, classReader.getAccess());
assertEquals(classParameter.getInternalName(), classReader.getClassName());
if (classParameter.getInternalName().equals("module-info")) {
@@ -231,18 +190,10 @@
assertNotNull(classReader.getInterfaces());
}
- @Test
- public void testStreamConstructor_nullStream() {
- Executable constructor = () -> new ClassReader((InputStream) null);
-
- Exception exception = assertThrows(IOException.class, constructor);
- assertEquals("Class not found", exception.getMessage());
- }
-
/** Tests {@link ClassReader#ClassReader(java.io.InputStream)} with an empty stream. */
@Test
- public void testStreamConstructor_emptyStream() throws IOException {
- try (InputStream inputStream =
+ public void testStreamConstructorWithEmptyStream() throws IOException {
+ InputStream inputStream =
new InputStream() {
@Override
@@ -254,152 +205,127 @@
public int read() throws IOException {
return -1;
}
- }) {
- Executable streamConstructor = () -> new ClassReader(inputStream);
+ };
+ assertTimeoutPreemptively(
+ Duration.ofMillis(100),
+ () -> assertThrows(RuntimeException.class, () -> new ClassReader(inputStream)));
+ }
- assertTimeoutPreemptively(
- Duration.ofMillis(100),
- () -> assertThrows(ArrayIndexOutOfBoundsException.class, streamConstructor));
- } catch (IOException ioe) {
- throw ioe;
- }
+ /** Tests the ClassReader accept method with a default visitor. */
+ @ParameterizedTest
+ @MethodSource(ALL_CLASSES_AND_ALL_APIS)
+ public void testAcceptWithDefaultVisitor(
+ final PrecompiledClass classParameter, final Api apiParameter) {
+ ClassReader classReader = new ClassReader(classParameter.getBytes());
+ ClassVisitor classVisitor = new ClassVisitor(apiParameter.value()) {};
+ boolean hasNestHostOrMembers =
+ classParameter == PrecompiledClass.JDK11_ALL_STRUCTURES
+ || classParameter == PrecompiledClass.JDK11_ALL_STRUCTURES_NESTED;
+ boolean hasModules = classParameter == PrecompiledClass.JDK9_MODULE;
+ boolean hasTypeAnnotations = classParameter == PrecompiledClass.JDK8_ALL_STRUCTURES;
+ assertThat(() -> classReader.accept(classVisitor, 0))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(
+ (hasNestHostOrMembers && apiParameter.value() < ASM7)
+ || (hasModules && apiParameter.value() < ASM6)
+ || (hasTypeAnnotations && apiParameter.value() < ASM5));
}
/** Tests the ClassReader accept method with an empty visitor. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_emptyVisitor(
+ public void testAcceptWithEmptyVisitor(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor = new EmptyClassVisitor(apiParameter.value());
-
- Executable accept = () -> classReader.accept(classVisitor, 0);
-
- if (classParameter.isMoreRecentThan(apiParameter)) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- } else {
- assertDoesNotThrow(accept);
- }
+ assertThat(() -> classReader.accept(classVisitor, 0))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(classParameter.isMoreRecentThan(apiParameter));
}
/** Tests the ClassReader accept method with an empty visitor and SKIP_DEBUG. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_emptyVisitor_skipDebug(
+ public void testAcceptWithEmptyVisitorAndSkipDebug(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor = new EmptyClassVisitor(apiParameter.value());
-
- Executable accept = () -> classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);
-
- // The following jdk8 classes contain MethodParameters attributes which require ASM5. Here we
- // skip these attributes with SKIP_DEBUG, and these classes contain no other features requiring
- // ASM5 or more, so they can be read with ASM4.
- if (classParameter.isMoreRecentThan(apiParameter)
- && classParameter != PrecompiledClass.JDK8_ALL_FRAMES
- && classParameter != PrecompiledClass.JDK8_ALL_STRUCTURES
- && classParameter != PrecompiledClass.JDK8_ANONYMOUS_INNER_CLASS
- && classParameter != PrecompiledClass.JDK8_INNER_CLASS
- && classParameter != PrecompiledClass.JDK8_LARGE_METHOD) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- } else {
- assertDoesNotThrow(accept);
- }
+ assertThat(() -> classReader.accept(classVisitor, ClassReader.SKIP_DEBUG))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(classParameter.isMoreRecentThan(apiParameter));
}
/** Tests the ClassReader accept method with an empty visitor and EXPAND_FRAMES. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_emptyVisitor_expandFrames(
+ public void testAcceptWithEmptyVisitorAndExpandFrames(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor = new EmptyClassVisitor(apiParameter.value());
-
- Executable accept = () -> classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
-
- if (classParameter.isMoreRecentThan(apiParameter)) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- } else {
- assertDoesNotThrow(accept);
- }
+ assertThat(() -> classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(classParameter.isMoreRecentThan(apiParameter));
}
/** Tests the ClassReader accept method with an empty visitor and SKIP_FRAMES. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_emptyVisitor_skipFrames(
+ public void testAcceptWithEmptyVisitorAndSkipFrames(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor = new EmptyClassVisitor(apiParameter.value());
-
- Executable accept = () -> classReader.accept(classVisitor, ClassReader.SKIP_FRAMES);
-
- if (classParameter.isMoreRecentThan(apiParameter)) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- } else {
- assertDoesNotThrow(accept);
- }
+ assertThat(() -> classReader.accept(classVisitor, ClassReader.SKIP_FRAMES))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(classParameter.isMoreRecentThan(apiParameter));
}
/** Tests the ClassReader accept method with an empty visitor and SKIP_CODE. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_emptyVisitor_skipCode(
+ public void testAcceptWithEmptyVisitorAndSkipCode(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor = new EmptyClassVisitor(apiParameter.value());
-
- Executable accept = () -> classReader.accept(classVisitor, ClassReader.SKIP_CODE);
-
// jdk8.ArtificialStructures contains structures which require ASM5, but only inside the method
// code. Here we skip the code, so this class can be read with ASM4. Likewise for
// jdk11.AllInstructions.
- if (classParameter.isMoreRecentThan(apiParameter)
- && classParameter != PrecompiledClass.JDK8_ARTIFICIAL_STRUCTURES
- && classParameter != PrecompiledClass.JDK11_ALL_INSTRUCTIONS) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- } else {
- assertDoesNotThrow(accept);
- }
+ assertThat(() -> classReader.accept(classVisitor, ClassReader.SKIP_CODE))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(
+ classParameter.isMoreRecentThan(apiParameter)
+ && classParameter != PrecompiledClass.JDK8_ARTIFICIAL_STRUCTURES
+ && classParameter != PrecompiledClass.JDK11_ALL_INSTRUCTIONS);
}
/**
- * Tests the ClassReader accept method with a visitor that skips fields, methods, members,
- * modules, nest host, permitted subclasses and record.
+ * Tests the ClassReader accept method with default annotation, field, method and module visitors.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_emptyVisitor_skipFieldMethodAndModuleContent(
+ public void testAcceptWithDefaultAnnotationFieldMethodAndModuleVisitor(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor =
new EmptyClassVisitor(apiParameter.value()) {
+
@Override
- public void visit(
- final int version,
- final int access,
- final String name,
- final String signature,
- final String superName,
- final String[] interfaces) {
- // access may contain ACC_RECORD
+ public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+ return new AnnotationVisitor(api) {};
}
@Override
- public ModuleVisitor visitModule(
- final String name, final int access, final String version) {
- return null;
+ public AnnotationVisitor visitTypeAnnotation(
+ final int typeRef,
+ final TypePath typePath,
+ final String descriptor,
+ final boolean visible) {
+ return new AnnotationVisitor(api) {};
}
@Override
- public RecordComponentVisitor visitRecordComponent(
- final String name, final String descriptor, final String signature) {
- return null;
+ public ModuleVisitor visitModule(
+ final String name, final int access, final String version) {
+ return new ModuleVisitor(api) {};
}
@Override
@@ -409,7 +335,7 @@
final String descriptor,
final String signature,
final Object value) {
- return null;
+ return new FieldVisitor(api) {};
}
@Override
@@ -419,130 +345,30 @@
final String descriptor,
final String signature,
final String[] exceptions) {
- return null;
+ return new MethodVisitor(api) {};
}
-
- @Override
- public void visitNestHost(final String nestHost) {}
-
- @Override
- public void visitNestMember(final String nestMember) {}
-
- @Override
- public void visitPermittedSubclass(final String permittedSubclass) {}
};
-
- Executable accept = () -> classReader.accept(classVisitor, 0);
-
- assertDoesNotThrow(accept);
- }
-
- /** Tests the ClassReader accept method with a class whose content is invalid. */
- @ParameterizedTest
- @EnumSource(InvalidClass.class)
- public void testAccept_emptyVisitor_invalidClass(final InvalidClass invalidClass) {
- assumeFalse(
- invalidClass == InvalidClass.INVALID_CLASS_VERSION
- || invalidClass == InvalidClass.INVALID_CP_INFO_TAG);
- ClassReader classReader = new ClassReader(invalidClass.getBytes());
-
- Executable accept =
- () -> classReader.accept(new EmptyClassVisitor(/* latest */ Opcodes.ASM10_EXPERIMENTAL), 0);
-
- if (invalidClass == InvalidClass.INVALID_CONSTANT_POOL_INDEX
- || invalidClass == InvalidClass.INVALID_CONSTANT_POOL_REFERENCE
- || invalidClass == InvalidClass.INVALID_BYTECODE_OFFSET) {
- Exception exception = assertThrows(ArrayIndexOutOfBoundsException.class, accept);
- Matcher matcher = Pattern.compile("\\d+").matcher(exception.getMessage());
- assertTrue(matcher.find() && Integer.valueOf(matcher.group()) > 0);
- } else {
- assertThrows(IllegalArgumentException.class, accept);
- }
- }
-
- /** Tests the ClassReader accept method with a default visitor. */
- @ParameterizedTest
- @MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_defaultVisitor(
- final PrecompiledClass classParameter, final Api apiParameter) {
- ClassReader classReader = new ClassReader(classParameter.getBytes());
- ClassVisitor classVisitor = new ClassVisitor(apiParameter.value()) {};
-
- Executable accept = () -> classReader.accept(classVisitor, 0);
-
- boolean hasPermittedSubclasses = classParameter == PrecompiledClass.JDK15_ALL_STRUCTURES;
- boolean hasRecord =
- classParameter == PrecompiledClass.JDK14_ALL_STRUCTURES_RECORD
- || classParameter == PrecompiledClass.JDK14_ALL_STRUCTURES_EMPTY_RECORD;
- boolean hasNestHostOrMembers =
- classParameter == PrecompiledClass.JDK11_ALL_STRUCTURES
- || classParameter == PrecompiledClass.JDK11_ALL_STRUCTURES_NESTED;
- boolean hasModules = classParameter == PrecompiledClass.JDK9_MODULE;
- boolean hasTypeAnnotations = classParameter == PrecompiledClass.JDK8_ALL_STRUCTURES;
- if ((hasPermittedSubclasses && apiParameter.value() < ASM9)
- || (hasRecord && apiParameter.value() < ASM8)
- || (hasNestHostOrMembers && apiParameter.value() < ASM7)
- || (hasModules && apiParameter.value() < ASM6)
- || (hasTypeAnnotations && apiParameter.value() < ASM5)) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
- } else {
- assertDoesNotThrow(accept);
- }
+ assertThat(() -> classReader.accept(classVisitor, 0))
+ .succeedsOrThrows(RuntimeException.class)
+ .when(classParameter.isMoreRecentThan(apiParameter));
}
/**
- * Tests the ClassReader accept method with default annotation, field, method and module visitors.
+ * Tests the ClassReader accept method with a visitor that skips fields, methods, modules and nest
+ * host and members.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testAccept_defaultAnnotationFieldMethodAndModuleVisitors(
+ public void testAcceptWithEmptyVisitorAndSkipFieldMethodAndModuleContent(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor =
new EmptyClassVisitor(apiParameter.value()) {
@Override
- public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
- return new AnnotationVisitor(api) {};
- }
-
- @Override
- public AnnotationVisitor visitTypeAnnotation(
- final int typeRef,
- final TypePath typePath,
- final String descriptor,
- final boolean visible) {
- return new AnnotationVisitor(api) {};
- }
-
- @Override
public ModuleVisitor visitModule(
final String name, final int access, final String version) {
- super.visitModule(name, access, version);
- return new ModuleVisitor(api) {};
- }
-
- @Override
- public RecordComponentVisitor visitRecordComponent(
- final String name, final String descriptor, final String signature) {
- super.visitRecordComponent(name, descriptor, signature);
- return new RecordComponentVisitor(api) {
- @Override
- public AnnotationVisitor visitAnnotation(
- final String descriptor, final boolean visible) {
- return new AnnotationVisitor(api) {};
- }
-
- @Override
- public AnnotationVisitor visitTypeAnnotation(
- final int typeRef,
- final TypePath typePath,
- final String descriptor,
- final boolean visible) {
- return new AnnotationVisitor(api) {};
- }
- };
+ return null;
}
@Override
@@ -552,7 +378,7 @@
final String descriptor,
final String signature,
final Object value) {
- return new FieldVisitor(api) {};
+ return null;
}
@Override
@@ -562,28 +388,24 @@
final String descriptor,
final String signature,
final String[] exceptions) {
- return new MethodVisitor(api) {};
+ return null;
}
- };
- Executable accept = () -> classReader.accept(classVisitor, 0);
+ @Override
+ public void visitNestHost(final String nestHost) {}
- if (classParameter.isMoreRecentThan(apiParameter)) {
- Exception exception = assertThrows(UnsupportedOperationException.class, accept);
- if (!exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN)) {
- throw new AssertionError("invalid error message");
- }
- } else {
- assertDoesNotThrow(accept);
- }
+ @Override
+ public void visitNestMember(final String nestMember) {}
+ };
+ classReader.accept(classVisitor, 0);
}
@Test
- public void testAccept_parameterAnnotationIndices() {
+ public void testParameterAnnotationIndices() {
+ final AtomicBoolean success = new AtomicBoolean(false);
ClassReader classReader = new ClassReader(PrecompiledClass.JDK5_LOCAL_CLASS.getBytes());
- AtomicInteger parameterIndex = new AtomicInteger(-1);
- ClassVisitor readParameterIndexVisitor =
- new ClassVisitor(/* latest */ Opcodes.ASM10_EXPERIMENTAL) {
+ classReader.accept(
+ new ClassVisitor(Opcodes.ASM7) {
@Override
public MethodVisitor visitMethod(
final int access,
@@ -596,29 +418,23 @@
public AnnotationVisitor visitParameterAnnotation(
final int parameter, final String descriptor, final boolean visible) {
if (descriptor.equals("Ljava/lang/Deprecated;")) {
- parameterIndex.set(parameter);
+ assertEquals(0, parameter);
+ success.set(true);
}
return null;
}
};
}
- };
-
- classReader.accept(readParameterIndexVisitor, 0);
-
- assertEquals(0, parameterIndex.get());
+ },
+ 0);
+ assertTrue(success.get());
}
@Test
- public void testAccept_previewClass() {
- byte[] classFile = PrecompiledClass.JDK11_ALL_INSTRUCTIONS.getBytes();
- // Set the minor version to 65535.
- classFile[4] = (byte) 0xFF;
- classFile[5] = (byte) 0xFF;
- ClassReader classReader = new ClassReader(classFile);
- AtomicInteger classVersion = new AtomicInteger(0);
- ClassVisitor readVersionVisitor =
- new ClassVisitor(/* latest */ Opcodes.ASM10_EXPERIMENTAL) {
+ public void testPreviewMinorVersion() {
+ ClassReader classReader = new ClassReader(PrecompiledClass.JDK11_ALL_INSTRUCTIONS.getBytes());
+ classReader.accept(
+ new ClassVisitor(Opcodes.ASM7) {
@Override
public void visit(
final int version,
@@ -627,18 +443,38 @@
final String signature,
final String superName,
final String[] interfaces) {
- classVersion.set(version);
+ assertEquals(Opcodes.V_PREVIEW, version & Opcodes.V_PREVIEW);
}
- };
-
- classReader.accept(readVersionVisitor, 0);
+ },
+ 0);
+ }
- assertEquals(Opcodes.V_PREVIEW, classVersion.get() & Opcodes.V_PREVIEW);
+ /** Tests that reading an invalid class throws an exception. */
+ @ParameterizedTest
+ @EnumSource(InvalidClass.class)
+ public void testInvalidClasses(final InvalidClass invalidClass) {
+ if (invalidClass == InvalidClass.INVALID_CLASS_VERSION
+ || invalidClass == InvalidClass.INVALID_CP_INFO_TAG) {
+ assertThrows(IllegalArgumentException.class, () -> new ClassReader(invalidClass.getBytes()));
+ } else {
+ ClassReader classReader = new ClassReader(invalidClass.getBytes());
+ if (invalidClass == InvalidClass.INVALID_CONSTANT_POOL_INDEX
+ || invalidClass == InvalidClass.INVALID_CONSTANT_POOL_REFERENCE
+ || invalidClass == InvalidClass.INVALID_BYTECODE_OFFSET) {
+ assertThrows(
+ ArrayIndexOutOfBoundsException.class,
+ () -> classReader.accept(new EmptyClassVisitor(ASM7), 0));
+ } else {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> classReader.accept(new EmptyClassVisitor(ASM7), 0));
+ }
+ }
}
private static class EmptyClassVisitor extends ClassVisitor {
- final AnnotationVisitor annotationVisitor =
+ AnnotationVisitor annotationVisitor =
new AnnotationVisitor(api) {
@Override
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -27,45 +27,29 @@
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.objectweb.asm.test.AsmTest;
-import org.objectweb.asm.test.ClassFile;
/**
- * Unit tests for {@link ClassVisitor}. Also tests {@link FieldVisitor}, {@ink MethodVisitor},
- * {@link ModuleVisitor} and {@link AnnotationVisitor}.
+ * ClassVisitor tests. Also tests FieldVisitor, MethodVisitor, ModuleVisitor and AnnotationVisitor.
*
* @author Eric Bruneton
*/
public class ClassVisitorTest extends AsmTest {
@Test
- public void testConstructor_validApi() {
- Executable constructor = () -> new ClassVisitor(Opcodes.ASM4) {};
-
- assertDoesNotThrow(constructor);
- }
-
- @Test
- public void testConstructor_invalidApi() {
- Executable constructor = () -> new ClassVisitor(0) {};
-
- Exception exception = assertThrows(IllegalArgumentException.class, constructor);
- assertEquals("Unsupported api 0", exception.getMessage());
+ public void testConstuctor() {
+ assertThrows(IllegalArgumentException.class, () -> new ClassVisitor(0) {});
+ assertThrows(IllegalArgumentException.class, () -> new ClassVisitor(Integer.MAX_VALUE) {});
}
/**
@@ -75,97 +59,81 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_emptyVisitor(
+ public void testReadAndWriteWithEmptyVisitor(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
ClassAdapter classAdapter = new ClassAdapter(apiParameter.value(), classWriter);
-
- Executable transform = () -> classReader.accept(classAdapter, attributes(), 0);
-
if (classParameter.isMoreRecentThan(apiParameter)) {
- Exception exception = assertThrows(UnsupportedOperationException.class, transform);
- assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN));
+ assertThrows(RuntimeException.class, () -> classReader.accept(classAdapter, attributes(), 0));
} else {
- assertDoesNotThrow(transform);
- assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
+ classReader.accept(classAdapter, attributes(), 0);
+ assertThatClass(classWriter.toByteArray()).isEqualTo(classFile);
}
}
/**
- * Tests that a ClassReader -> class adapter -> ClassWriter chain gives the same result with or
- * without the copy pool option, and a class adapter that changes the method exceptions.
+ * Tests that a ClassReader -> class adapter -> ClassWriter chain give the same result with or
+ * without the copy pool option.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_copyPool_changeMethodExceptions(
+ public void testReadAndWriteWithCopyPoolAndExceptionAdapter(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
ClassWriter classWriterWithCopyPool = new ClassWriter(classReader, 0);
-
classReader.accept(new ChangeExceptionAdapter(classWriter), attributes(), 0);
classReader.accept(new ChangeExceptionAdapter(classWriterWithCopyPool), attributes(), 0);
-
- assertEquals(
- new ClassFile(classWriter.toByteArray()),
- new ClassFile(classWriterWithCopyPool.toByteArray()));
+ assertThatClass(classWriterWithCopyPool.toByteArray()).isEqualTo(classWriter.toByteArray());
}
/**
- * Tests that a ClassReader -> class adapter -> ClassWriter chain gives the same result with or
- * without the copy pool option, and a class adapter that changes the deprecated method flags.
+ * Tests that a ClassReader -> class adapter -> ClassWriter chain give the same result with or
+ * without the copy pool option.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_copyPool_changeMethodDeprecatedFlag(
+ public void testReadAndWriteWithCopyPoolAndDeprecatedAdapter(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
ClassWriter classWriterWithCopyPool = new ClassWriter(classReader, 0);
int access = Opcodes.ACC_DEPRECATED;
-
classReader.accept(new ChangeAccessAdapter(classWriter, access), attributes(), 0);
classReader.accept(new ChangeAccessAdapter(classWriterWithCopyPool, access), attributes(), 0);
-
- assertEquals(
- new ClassFile(classWriter.toByteArray()),
- new ClassFile(classWriterWithCopyPool.toByteArray()));
+ assertThatClass(classWriterWithCopyPool.toByteArray()).isEqualTo(classWriter.toByteArray());
}
/**
- * Tests that a ClassReader -> class adapter -> ClassWriter chain gives the same result with or
- * without the copy pool option, and a class adapter that changes the synthetic method flags.
+ * Tests that a ClassReader -> class adapter -> ClassWriter chain give the same result with or
+ * without the copy pool option.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_copyPool_changeMethodSyntheticFlag(
+ public void testReadAndWriteWithCopyPoolAndSyntheticAdapter(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
ClassWriter classWriterWithCopyPool = new ClassWriter(classReader, 0);
int access = Opcodes.ACC_SYNTHETIC;
-
classReader.accept(new ChangeAccessAdapter(classWriter, access), attributes(), 0);
classReader.accept(new ChangeAccessAdapter(classWriterWithCopyPool, access), attributes(), 0);
-
- assertEquals(
- new ClassFile(classWriter.toByteArray()),
- new ClassFile(classWriterWithCopyPool.toByteArray()));
+ assertThatClass(classWriterWithCopyPool.toByteArray()).isEqualTo(classWriter.toByteArray());
}
/**
- * Tests that a ClassReader -> class adapter -> ClassWriter chain gives the same result with or
- * without the copy pool option, and a class adapter that changes the class version (and
- * optionally the method synthetic flags / attributes as well).
+ * Tests that a ClassReader -> class adapter -> ClassWriter chain give the same result with or
+ * without the copy pool option, when the class version is changed (and optionally a synthetic
+ * attribute as well).
*/
@ParameterizedTest
@CsvSource({"true, true", "true, false", "false, true", "false, false"})
- public void testReadAndWrite_copyPool_changeClassVersionAndMethodSyntheticFlag(
+ public void testReadAndWriteWithCopyPoolAndSyntheticAdapter(
final boolean upgradeVersion, final boolean changeSynthetic) {
ClassWriter sourceClassWriter = new ClassWriter(0);
sourceClassWriter.visit(
@@ -179,27 +147,25 @@
.visitMethod(Opcodes.ACC_ABSTRACT | Opcodes.ACC_SYNTHETIC, "m", "()V", null, null)
.visitEnd();
sourceClassWriter.visitEnd();
+
ClassReader classReader = new ClassReader(sourceClassWriter.toByteArray());
ClassWriter classWriter = new ClassWriter(0);
ClassWriter copyPoolClassWriter = new ClassWriter(classReader, 0);
int version = upgradeVersion ? Opcodes.V1_5 : Opcodes.V1_4;
int access = changeSynthetic ? Opcodes.ACC_SYNTHETIC : 0;
-
classReader.accept(
new ChangeVersionAdapter(new ChangeAccessAdapter(classWriter, access), version), 0);
classReader.accept(
new ChangeVersionAdapter(new ChangeAccessAdapter(copyPoolClassWriter, access), version), 0);
-
- assertEquals(
- new ClassFile(classWriter.toByteArray()), new ClassFile(copyPoolClassWriter.toByteArray()));
+ assertThatClass(copyPoolClassWriter.toByteArray()).isEqualTo(classWriter.toByteArray());
}
/**
- * Tests that a ClassReader -> class adapter -> ClassWriter chain gives the same result with or
- * without the copy pool option, and a class adapter that changes the method descriptors.
+ * Tests that a ClassReader -> class adapter -> ClassWriter chain give the same result when the
+ * descriptor of a method is changed.
*/
@Test
- public void testReadAndWrite_copyPool_changeMethodDescriptor() {
+ public void testReadAndWriteWithCopyPoolAndChangeDescriptor() {
ClassWriter sourceClassWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
sourceClassWriter.visit(
Opcodes.V1_7, Opcodes.ACC_ABSTRACT, "C", null, "java/lang/Object", null);
@@ -213,82 +179,28 @@
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
sourceClassWriter.visitEnd();
+
ClassReader classReader = new ClassReader(sourceClassWriter.toByteArray());
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassWriter copyPoolClassWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
-
classReader.accept(new AddParameterAdapter(classWriter), 0);
classReader.accept(new AddParameterAdapter(copyPoolClassWriter), 0);
-
- assertEquals(
- new ClassFile(classWriter.toByteArray()), new ClassFile(copyPoolClassWriter.toByteArray()));
+ assertThatClass(copyPoolClassWriter.toByteArray()).isEqualTo(classWriter.toByteArray());
}
/** Test that classes with only visible or only invisible annotations can be read correctly. */
@ParameterizedTest
@ValueSource(strings = {"true", "false"})
- public void testReadAndWrite_removeAnnotations(final boolean visibilityValue) {
+ public void testReadAndWriteWithRemoveAnnotationAdapter(final boolean visibilityValue) {
ClassWriter classWriter = new ClassWriter(0);
new ClassReader(PrecompiledClass.JDK8_ALL_STRUCTURES.getBytes())
.accept(new RemoveAnnotationAdapter(classWriter, visibilityValue), 0);
byte[] classFile = classWriter.toByteArray();
- ClassWriter newClassWriter = new ClassWriter(0);
+ ClassWriter newClassWriter = new ClassWriter(0);
new ClassReader(classFile)
.accept(new RemoveAnnotationAdapter(newClassWriter, visibilityValue), 0);
-
- assertEquals(new ClassFile(classFile), new ClassFile(newClassWriter.toByteArray()));
- }
-
- /**
- * Tests that optional module data (ModulePackage, ModuleMainClass, etc) can be removed with a
- * ClassReader -> class adapter -> ClassWriter chain.
- */
- @Test
- public void testReadAndWrite_removeOptionalModuleData() {
- byte[] classFile = PrecompiledClass.JDK9_MODULE.getBytes();
- ClassReader classReader = new ClassReader(classFile);
- ClassWriter classWriter = new ClassWriter(0);
- ClassVisitor classVisitor =
- new ClassVisitor(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classWriter) {
-
- @Override
- public ModuleVisitor visitModule(
- final String name, final int access, final String version) {
- return new ModuleVisitor(api, super.visitModule(name, access, version)) {
-
- @Override
- public void visitMainClass(final String mainClass) {}
-
- @Override
- public void visitPackage(final String packaze) {}
-
- @Override
- public void visitRequire(
- final String module, final int access, final String version) {
- super.visitRequire(module, access, null);
- }
-
- @Override
- public void visitExport(
- final String packaze, final int access, final String... modules) {
- super.visitExport(packaze, access, (String[]) null);
- }
-
- @Override
- public void visitOpen(
- final String packaze, final int access, final String... modules) {
- super.visitOpen(packaze, access, (String[]) null);
- }
- };
- }
- };
-
- classReader.accept(classVisitor, null, 0);
-
- String classDump = new ClassFile(classWriter.toByteArray()).toString();
- assertFalse(classDump.contains("ModulePackage"));
- assertFalse(classDump.contains("ModuleMainClass"));
+ assertThatClass(newClassWriter.toByteArray()).isEqualTo(classFile);
}
static Attribute[] attributes() {
@@ -358,7 +270,7 @@
public ModuleVisitor visitModule(final String name, final int access, final String version) {
return new ModuleVisitor(api, super.visitModule(name, access, version)) {};
}
- }
+ };
private static class FieldAdapter extends FieldVisitor {
@@ -454,7 +366,7 @@
private static class ChangeExceptionAdapter extends ClassVisitor {
ChangeExceptionAdapter(final ClassVisitor classVisitor) {
- super(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classVisitor);
+ super(Opcodes.ASM7, classVisitor);
}
@Override
@@ -476,7 +388,7 @@
private final int newVersion;
ChangeVersionAdapter(final ClassVisitor classVisitor, final int newVersion) {
- super(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classVisitor);
+ super(Opcodes.ASM7, classVisitor);
this.newVersion = newVersion;
}
@@ -497,7 +409,7 @@
private final int accessFlags;
ChangeAccessAdapter(final ClassVisitor classVisitor, final int accessFlags) {
- super(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classVisitor);
+ super(Opcodes.ASM7, classVisitor);
this.accessFlags = accessFlags;
}
@@ -518,7 +430,7 @@
private final boolean visibilityValue;
RemoveAnnotationAdapter(final ClassVisitor classVisitor, final boolean visibilityValue) {
- super(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classVisitor);
+ super(Opcodes.ASM7, classVisitor);
this.visibilityValue = visibilityValue;
}
@@ -659,7 +571,7 @@
private static class AddParameterAdapter extends ClassVisitor {
public AddParameterAdapter(final ClassVisitor classVisitor) {
- super(/* latest */ Opcodes.ASM10_EXPERIMENTAL, classVisitor);
+ super(Opcodes.ASM7, classVisitor);
}
@Override
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -27,20 +27,19 @@
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
-import java.util.concurrent.atomic.AtomicReference;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
-import org.objectweb.asm.test.ClassFile;
/**
* ClassWriter unit tests for COMPUTE_MAXS option with JSR instructions.
@@ -56,7 +55,11 @@
private static final int LOCAL4 = 4;
private static final int LOCAL5 = 5;
+ private ClassWriter classWriter;
+ private MethodVisitor methodVisitor;
+
// Labels used to generate test cases.
+ private final Label start = new Label();
private final Label label0 = new Label();
private final Label label1 = new Label();
private final Label label2 = new Label();
@@ -71,6 +74,178 @@
private final Label label11 = new Label();
private final Label label12 = new Label();
+ @BeforeEach
+ public void setUp() throws Exception {
+ init(Opcodes.V1_1);
+ }
+
+ private void init(final int classVersion) {
+ classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ classWriter.visit(classVersion, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null);
+ methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
+ methodVisitor.visitCode();
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "m", "()V", null, null);
+ methodVisitor.visitCode();
+ label(start);
+ }
+
+ private void nop() {
+ methodVisitor.visitInsn(Opcodes.NOP);
+ }
+
+ private void push() {
+ methodVisitor.visitInsn(Opcodes.ICONST_0);
+ }
+
+ private void pop() {
+ methodVisitor.visitInsn(Opcodes.POP);
+ }
+
+ private void iconst_0() {
+ methodVisitor.visitInsn(Opcodes.ICONST_0);
+ }
+
+ private void istore(final int var) {
+ methodVisitor.visitVarInsn(Opcodes.ISTORE, var);
+ }
+
+ private void aload(final int var) {
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, var);
+ }
+
+ private void iload(final int var) {
+ methodVisitor.visitVarInsn(Opcodes.ILOAD, var);
+ }
+
+ private void astore(final int var) {
+ methodVisitor.visitVarInsn(Opcodes.ASTORE, var);
+ }
+
+ private void ret(final int var) {
+ methodVisitor.visitVarInsn(Opcodes.RET, var);
+ }
+
+ private void athrow() {
+ methodVisitor.visitInsn(Opcodes.ATHROW);
+ }
+
+ private void aconst_null() {
+ methodVisitor.visitInsn(Opcodes.ACONST_NULL);
+ }
+
+ private void vreturn() {
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ }
+
+ private void label(final Label label) {
+ methodVisitor.visitLabel(label);
+ }
+
+ private void iinc(final int var, final int increment) {
+ methodVisitor.visitIincInsn(var, increment);
+ }
+
+ private void go(final Label label) {
+ methodVisitor.visitJumpInsn(Opcodes.GOTO, label);
+ }
+
+ private void jsr(final Label label) {
+ methodVisitor.visitJumpInsn(Opcodes.JSR, label);
+ }
+
+ private void ifnonnull(final Label label) {
+ methodVisitor.visitJumpInsn(Opcodes.IFNONNULL, label);
+ }
+
+ private void ifne(final Label label) {
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, label);
+ }
+
+ private void trycatch(final Label start, final Label end, final Label handler) {
+ methodVisitor.visitTryCatchBlock(start, end, handler, null);
+ }
+
+ private void assertMaxs(final int maxStack, final int maxLocals) {
+ methodVisitor.visitMaxs(0, 0);
+ methodVisitor.visitEnd();
+ classWriter.visitEnd();
+ byte[] classFile = classWriter.toByteArray();
+ ClassReader classReader = new ClassReader(classFile);
+ classReader.accept(
+ new ClassVisitor(Opcodes.ASM5) {
+ @Override
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final String[] exceptions) {
+ if (name.equals("m")) {
+ return new MethodVisitor(Opcodes.ASM5) {
+ @Override
+ public void visitMaxs(final int realMaxStack, final int realMaxLocals) {
+ assertEquals(maxStack, realMaxStack, "maxStack");
+ assertEquals(maxLocals, realMaxLocals, "maxLocals");
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+ },
+ 0);
+
+ TestClassLoader loader = new TestClassLoader();
+ try {
+ loader.defineClass("C", classFile).newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ private void assertGraph(final String... nodes) {
+ Map> expected = new HashMap>();
+ for (String node : nodes) {
+ StringTokenizer stringTokenizer = new StringTokenizer(node, "=,");
+ String key = stringTokenizer.nextToken();
+ Set values = new HashSet();
+ while (stringTokenizer.hasMoreTokens()) {
+ values.add(stringTokenizer.nextToken());
+ }
+ expected.put(key, values);
+ }
+
+ Map> actual = new HashMap>();
+ Label currentLabel = start;
+ while (currentLabel != null) {
+ String key = "N" + currentLabel.getOffset();
+ Set value = new HashSet();
+ Edge outgoingEdge = currentLabel.outgoingEdges;
+ if ((currentLabel.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) {
+ // Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual
+ // edges which lead to the instruction just after the jsr, and do not correspond to a
+ // possible execution path (see {@link #visitJumpInsn} and
+ // {@link Label#FLAG_SUBROUTINE_CALLER}).
+ assertNotNull(outgoingEdge);
+ outgoingEdge = outgoingEdge.nextEdge;
+ }
+ while (outgoingEdge != null) {
+ value.add("N" + outgoingEdge.successor.getOffset());
+ outgoingEdge = outgoingEdge.nextEdge;
+ }
+ actual.put(key, value);
+ currentLabel = currentLabel.nextBasicBlock;
+ }
+
+ assertEquals(expected, actual);
+ }
+
/**
* Tests a method which has the most basic try{}finally{} form imaginable. That is,
* repeated one or more times:
@@ -89,65 +264,60 @@
*/
@ParameterizedTest
@ValueSource(ints = {1, 31, 32, 33})
- public void testVisitMaxs_basic(final int numSubroutines) {
- TestCaseBuilder testCase = new TestCaseBuilder();
+ public void testBasic(final int numSubroutines) {
for (int i = 0; i < numSubroutines; ++i) {
+ iconst_0(); // N0
+ istore(1);
+
+ // Body of try block.
Label k0 = new Label();
+ label(k0); // N2
+ iinc(1, 1);
+ Label k3 = new Label();
+ go(k3);
+
+ // Exception handler.
Label k1 = new Label();
+ label(k1); // N8
+ astore(3);
Label k2 = new Label();
- Label k3 = new Label();
+ jsr(k2);
+ aload(3); // N12
+ athrow();
+
+ // Subroutine.
+ label(k2); // N14
+ astore(2);
+ iinc(1, -1);
+ push();
+ push();
+ ret(2);
+
+ // Non-exceptional exit from try block.
+ label(k3); // N22
+ jsr(k2);
+ push(); // N25
+ push();
Label k4 = new Label();
- testCase
- .iconst_0() // N0
- .istore(1)
- // Body of try block.
- .label(k0) // N2
- .iinc(1, 1)
- .go(k3)
- // Exception handler.
- .label(k1) // N8
- .astore(3)
- .jsr(k2)
- .aload(3) // N12
- .athrow()
- // Subroutine.
- .label(k2) // N14
- .astore(2)
- .iinc(1, -1)
- .push()
- .push()
- .ret(2)
- // Non-exceptional exit from try block.
- .label(k3) // N22
- .jsr(k2)
- .push() // N25
- .push()
- .label(k4) // N27
- .vreturn()
- .trycatch(k0, k1, k1)
- .trycatch(k3, k4, k1);
- }
+ label(k4); // N27
+ vreturn();
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
+ trycatch(k0, k1, k1);
+ trycatch(k3, k4, k1);
+ }
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(4, methodInfo.maxStack);
- assertEquals(4, methodInfo.maxLocals);
+ assertMaxs(4, 4);
if (numSubroutines == 1) {
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2",
- "N2=N22,N8",
- "N8=N14",
- "N12=",
- "N14=N12,N25",
- "N22=N14,N8",
- "N25=N27,N8",
- "N27=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
+ assertGraph(
+ "N0=N2",
+ "N2=N22,N8",
+ "N8=N14",
+ "N12=",
+ "N14=N12,N25",
+ "N22=N14,N8",
+ "N25=N27,N8",
+ "N27=");
}
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
}
/**
@@ -169,63 +339,60 @@
*
*/
@Test
- public void testVisitMaxs_ifElseInFinally() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- // Body of try block.
- .label(label0) // N2
- .iinc(1, 1)
- .go(label5)
- // Exception handler.
- .label(label1) // N8
- .astore(3)
- .jsr(label2)
- .push() // N12
- .push()
- .aload(3)
- .athrow()
- // Subroutine.
- .label(label2) // N16
- .astore(2)
- .push()
- .push()
- .iload(1)
- .ifne(label3)
- .iinc(1, 2)
- .go(label4)
- .label(label3) // N29
- .iinc(1, 3)
- .label(label4) // N32, common exit.
- .ret(2)
- // Non-exceptional exit from try block.
- .label(label5) // N34
- .jsr(label2)
- .label(label6) // N37
- .vreturn()
- .trycatch(label0, label1, label1)
- .trycatch(label5, label6, label1);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(5, methodInfo.maxStack);
- assertEquals(4, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2",
- "N2=N34,N8",
- "N8=N16",
- "N12=",
- "N16=N29,N32",
- "N29=N32",
- "N32=N37,N12",
- "N34=N16,N8",
- "N37=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testIfElseInFinally() {
+ iconst_0(); // N0
+ istore(1);
+
+ // Body of try block.
+ label(label0); // N2
+ iinc(1, 1);
+ go(label5);
+
+ // Exception handler.
+ label(label1); // N8
+ astore(3);
+ jsr(label2);
+ push(); // N12
+ push();
+ aload(3);
+ athrow();
+
+ // Subroutine.
+ label(label2); // N16
+ astore(2);
+ push();
+ push();
+ iload(1);
+ ifne(label3);
+ iinc(1, 2);
+ go(label4);
+
+ label(label3); // N29
+ iinc(1, 3);
+
+ label(label4); // N32, common exit.
+ ret(2);
+
+ // Non-exceptional exit from try block.
+ label(label5); // N34
+ jsr(label2);
+ label(label6); // N37
+ vreturn();
+
+ trycatch(label0, label1, label1);
+ trycatch(label5, label6, label1);
+
+ assertMaxs(5, 4);
+ assertGraph(
+ "N0=N2",
+ "N2=N34,N8",
+ "N8=N16",
+ "N12=",
+ "N16=N29,N32",
+ "N29=N32",
+ "N32=N37,N12",
+ "N34=N16,N8",
+ "N37=");
}
/**
@@ -247,70 +414,67 @@
*
*/
@Test
- public void testVisitMaxs_simpleNestedFinally() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- // Body of try block.
- .label(label0) // N2
- .iinc(1, 1)
- .jsr(label2)
- .go(label5) // N8
- // First exception handler.
- .label(label1) // N11
- .astore(4)
- .jsr(label2)
- .aload(4) // N16
- .athrow()
- // First subroutine.
- .label(label2) // N19
- .astore(2)
- .iinc(1, 2)
- .jsr(label4)
- .push() // N26
- .push()
- .ret(2)
- // Second exception handler.
- .label(label3) // N30
- .astore(5)
- .jsr(label4)
- .aload(5) // N35
- .athrow()
- // Second subroutine.
- .label(label4) // N38
- .astore(3)
- .push()
- .push()
- .iinc(1, 3)
- .ret(3)
- // On normal exit, try block jumps here.
- .label(label5) // N46
- .vreturn()
- .trycatch(label0, label1, label1)
- .trycatch(label2, label3, label3);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(5, methodInfo.maxStack);
- assertEquals(6, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2",
- "N2=N11,N19",
- "N8=N11,N46",
- "N11=N19",
- "N16=",
- "N19=N30,N38",
- "N26=N16,N30,N8",
- "N30=N38",
- "N35=",
- "N38=N26,N35",
- "N46=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testSimpleNestedFinally() {
+ iconst_0(); // N0
+ istore(1);
+
+ // Body of try block.
+ label(label0); // N2
+ iinc(1, 1);
+ jsr(label2);
+ go(label5); // N8
+
+ // First exception handler.
+ label(label1); // N11
+ astore(4);
+ jsr(label2);
+ aload(4); // N16
+ athrow();
+
+ // First subroutine.
+ label(label2); // N19
+ astore(2);
+ iinc(1, 2);
+ jsr(label4);
+ push(); // N26
+ push();
+ ret(2);
+
+ // Second exception handler.
+ label(label3); // N30
+ astore(5);
+ jsr(label4);
+ aload(5); // N35
+ athrow();
+
+ // Second subroutine.
+ label(label4); // N38
+ astore(3);
+ push();
+ push();
+ iinc(1, 3);
+ ret(3);
+
+ // On normal exit, try block jumps here.
+ label(label5); // N46
+ vreturn();
+
+ trycatch(label0, label1, label1);
+ trycatch(label2, label3, label3);
+
+ assertMaxs(5, 6);
+ assertGraph(
+ "N0=N2",
+ "N2=N11,N19",
+ "N8=N11,N46",
+ "N11=N19",
+ "N16=",
+ "N19=N30,N38",
+ "N26=N16,N30,N8",
+ "N30=N38",
+ "N35=",
+ "N38=N26,N35",
+ "N46=");
}
/**
@@ -335,47 +499,43 @@
*
*/
@Test
- public void testVisitMaxs_subroutineWithNoRet() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- // While loop header/try block.
- .label(label0) // N2
- .iinc(1, 1)
- .jsr(label2)
- .go(label3) // N8
- // Implicit catch block.
- .label(label1) // N11
- .astore(2)
- .jsr(label2)
- .push() // N15
- .push()
- .aload(2)
- .athrow()
- // Subroutine which does not return.
- .label(label2) // N19
- .astore(3)
- .iinc(1, 2)
- .go(label4)
- // End of the loop, goes back to the top.
- .label(label3) // N26
- .go(label0)
- .label(label4) // N29
- .vreturn()
- .trycatch(label0, label1, label1);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(1, methodInfo.maxStack);
- assertEquals(4, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2", "N2=N11,N19", "N8=N11,N26", "N11=N19", "N15=", "N19=N29", "N26=N2", "N29=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testSubroutineWithNoRet() {
+ iconst_0(); // N0
+ istore(1);
+
+ // While loop header/try block.
+ label(label0); // N2
+ iinc(1, 1);
+ jsr(label2);
+ go(label3); // N8
+
+ // Implicit catch block.
+ label(label1); // N11
+ astore(2);
+ jsr(label2);
+ push(); // N15
+ push();
+ aload(2);
+ athrow();
+
+ // Subroutine which does not return.
+ label(label2); // N19
+ astore(3);
+ iinc(1, 2);
+ go(label4);
+
+ // End of the loop, goes back to the top.
+ label(label3); // N26
+ go(label0);
+
+ label(label4); // N29
+ vreturn();
+
+ trycatch(label0, label1, label1);
+
+ assertMaxs(1, 4);
+ assertGraph(
+ "N0=N2", "N2=N11,N19", "N8=N11,N26", "N11=N19", "N15=", "N19=N29", "N26=N2", "N29=");
}
/**
@@ -391,29 +551,19 @@
*
*/
@Test
- public void testVisitMaxs_subroutineWithNoRet2() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .aconst_null() // N0
- .jsr(label0)
- .nop() // N4
- .label(label0) // N5
- .astore(0)
- .astore(0)
- .vreturn()
- .label(label1) // N8
- .localVariable("i", "I", null, label0, label1, 1);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(2, methodInfo.maxStack);
- assertEquals(2, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph("N0=N5", "N4=N5", "N5=", "N8=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testSubroutineWithNoRet2() {
+ aconst_null(); // N0
+ jsr(label0);
+ nop(); // N4
+ label(label0); // N5
+ astore(0);
+ astore(0);
+ vreturn();
+ label(label1); // N8
+ methodVisitor.visitLocalVariable("i", "I", null, label0, label1, 1);
+
+ assertMaxs(2, 2);
+ assertGraph("N0=N5", "N4=N5", "N5=", "N8=");
}
/**
@@ -439,59 +589,56 @@
*
*/
@Test
- public void testVisitMaxs_implicitExit() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- // While loop header.
- .label(label0) // N2
- .aconst_null()
- .ifnonnull(label5)
- // Try block.
- .label(label1) // N6
- .iinc(1, 1)
- .jsr(label3)
- .go(label4) // N12
- // Implicit catch block.
- .label(label2) // N15
- .astore(2)
- .jsr(label3)
- .aload(2) // N19
- .push()
- .push()
- .athrow()
- // Subroutine which does not return.
- .label(label3) // N23
- .astore(3)
- .iinc(1, 2)
- .go(label5)
- // End of the loop, goes back to the top.
- .label(label4) // N30
- .go(label1)
- .label(label5) // N33
- .vreturn()
- .trycatch(label1, label2, label2);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(1, methodInfo.maxStack);
- assertEquals(4, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2",
- "N2=N6,N33",
- "N6=N23,N15",
- "N12=N30,N15",
- "N15=N23",
- "N19=",
- "N23=N33",
- "N30=N6",
- "N33=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testImplicitExit() {
+ iconst_0(); // N0
+ istore(1);
+
+ // While loop header.
+ label(label0); // N2
+ aconst_null();
+ ifnonnull(label5);
+
+ // Yry block.
+ label(label1); // N6
+ iinc(1, 1);
+ jsr(label3);
+ go(label4); // N12
+
+ // Implicit catch block.
+ label(label2); // N15
+ astore(2);
+ jsr(label3);
+ aload(2); // N19
+ push();
+ push();
+ athrow();
+
+ // Subroutine which does not return.
+ label(label3); // N23
+ astore(3);
+ iinc(1, 2);
+ go(label5);
+
+ // End of the loop, goes back to the top.
+ label(label4); // N30
+ go(label1);
+
+ label(label5); // N33
+ vreturn();
+
+ trycatch(label1, label2, label2);
+
+ assertMaxs(1, 4);
+ assertGraph(
+ "N0=N2",
+ "N2=N6,N33",
+ "N6=N23,N15",
+ "N12=N30,N15",
+ "N15=N23",
+ "N19=",
+ "N23=N33",
+ "N30=N6",
+ "N33=");
}
/**
@@ -521,112 +668,104 @@
* Static and Dynamic Analysis" by Cyrille Artho and Armin Biere.
*/
@Test
- public void testVisitMaxs_implicitExitToAnotherSubroutine() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- // First try.
- .label(label0) // N2
- .jsr(label2)
- .vreturn() // N5
- // Exception handler for first try.
- .label(label1) // N6
- .astore(LOCAL2)
- .jsr(label2)
- .push() // N10
- .push()
- .aload(LOCAL2)
- .athrow()
- // First finally handler.
- .label(label2) // N14
- .astore(LOCAL4)
- .push()
- .push()
- .go(label6)
- // Body of while loop, also second try.
- .label(label3) // N21
- .jsr(label5)
- .vreturn() // N24
- // Exception handler for second try.
- .label(label4) // N25
- .astore(LOCAL3)
- .push()
- .push()
- .jsr(label5)
- .aload(LOCAL3) // N31
- .athrow()
- // Second finally handler.
- .label(label5) // N33
- .astore(LOCAL5)
- .iload(LOCAL1)
- .ifne(label7)
- .ret(LOCAL5)
- // Test for the while loop.
- .label(label6) // N41
- .iload(LOCAL1)
- .ifne(label3) // falls through to X.
- // Exit from finally block.
- .label(label7) // N45
- .ret(LOCAL4)
- .trycatch(label0, label1, label1)
- .trycatch(label3, label4, label4);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(5, methodInfo.maxStack);
- assertEquals(6, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2",
- "N2=N6,N14",
- "N5=N6",
- "N6=N14",
- "N10=",
- "N14=N41",
- "N21=N25,N33",
- "N24=N25",
- "N25=N33",
- "N31=",
- "N33=N31,N45,N24",
- "N41=N45,N21",
- "N45=N5,N10");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testImplicitExitToAnotherSubroutine() {
+ iconst_0(); // N0
+ istore(1);
+
+ // First try.
+ label(label0); // N2
+ jsr(label2);
+ vreturn(); // N5
+
+ // Exception handler for first try.
+ label(label1); // N6
+ astore(LOCAL2);
+ jsr(label2);
+ push(); // N10
+ push();
+ aload(LOCAL2);
+ athrow();
+
+ // First finally handler.
+ label(label2); // N14
+ astore(LOCAL4);
+ push();
+ push();
+ go(label6);
+
+ // Body of while loop, also second try.
+ label(label3); // N21
+ jsr(label5);
+ vreturn(); // N24
+
+ // Exception handler for second try.
+ label(label4); // N25
+ astore(LOCAL3);
+ push();
+ push();
+ jsr(label5);
+ aload(LOCAL3); // N31
+ athrow();
+
+ // Second finally handler.
+ label(label5); // N33
+ astore(LOCAL5);
+ iload(LOCAL1);
+ ifne(label7);
+ ret(LOCAL5);
+
+ // Test for the while loop.
+ label(label6); // N41
+ iload(LOCAL1);
+ ifne(label3); // falls through to X.
+
+ // Exit from finally block.
+ label(label7); // N45
+ ret(LOCAL4);
+
+ trycatch(label0, label1, label1);
+ trycatch(label3, label4, label4);
+
+ assertMaxs(5, 6);
+ assertGraph(
+ "N0=N2",
+ "N2=N6,N14",
+ "N5=N6",
+ "N6=N14",
+ "N10=",
+ "N14=N41",
+ "N21=N25,N33",
+ "N24=N25",
+ "N25=N33",
+ "N31=",
+ "N33=N31,N45,N24",
+ "N41=N45,N21",
+ "N45=N5,N10");
}
@Test
- public void testVisitMaxs_implicitExitToAnotherSubroutine2() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- .jsr(label0)
- .vreturn() // N5
- .label(label0) // N6
- .astore(2)
- .jsr(label1)
- .go(label2) // N10
- .label(label1) // N13
- .astore(3)
- .iload(1)
- .ifne(label2)
- .ret(3)
- .label(label2) // N20
- .ret(2);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(1, methodInfo.maxStack);
- assertEquals(4, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph("N0=N6", "N5=", "N6=N13", "N10=N20", "N13=N20,N10", "N20=N5");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testImplicitExitToAnotherSubroutine2() {
+ iconst_0(); // N0
+ istore(1);
+ jsr(label0);
+ vreturn(); // N5
+
+ label(label0); // N6
+ astore(2);
+ jsr(label1);
+ go(label2); // N10
+
+ label(label1); // N13
+ astore(3);
+ iload(1);
+ ifne(label2);
+ ret(3);
+
+ label(label2); // N20
+ ret(2);
+
+ assertMaxs(1, 4);
+ assertGraph("N0=N6", "N5=", "N6=N13", "N10=N20", "N13=N20,N10", "N20=N5");
}
/**
@@ -636,44 +775,38 @@
*
This would not normally be produced by a Java compiler.
*/
@Test
- public void testVisitMaxs_interleavedCode() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- .jsr(label0)
- .go(label1) // N5
- // Subroutine 1.
- .label(label0) // N8
- .astore(2)
- .iinc(1, 1)
- .go(label2)
- // Second part of main subroutine.
- .label(label1) // N15
- .iinc(1, 2)
- .go(label3)
- // Second part of subroutine 1.
- .label(label2) // N21
- .iinc(1, 4)
- .push()
- .push()
- .ret(2)
- // Third part of main subroutine.
- .label(label3) // N28
- .push()
- .push()
- .vreturn();
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(4, methodInfo.maxStack);
- assertEquals(3, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph("N0=N8", "N5=N15", "N8=N21", "N15=N28", "N21=N5", "N28=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testInterleavedCode() {
+ iconst_0(); // N0
+ istore(1);
+ jsr(label0);
+ go(label1); // N5
+
+ // Subroutine 1.
+ label(label0); // N8
+ astore(2);
+ iinc(1, 1);
+ go(label2);
+
+ // Second part of main subroutine.
+ label(label1); // N15
+ iinc(1, 2);
+ go(label3);
+
+ // Second part of subroutine 1.
+ label(label2); // N21
+ iinc(1, 4);
+ push();
+ push();
+ ret(2);
+
+ // Third part of main subroutine.
+ label(label3); // N28
+ push();
+ push();
+ vreturn();
+
+ assertMaxs(4, 3);
+ assertGraph("N0=N8", "N5=N15", "N8=N21", "N15=N28", "N21=N5", "N28=");
}
/**
@@ -705,84 +838,84 @@
*
*/
@Test
- public void testVisitMaxs_implicitExitInTryCatch() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .iconst_0() // N0
- .istore(1)
- // First try.
- .label(label0) // N2
- .jsr(label2)
- .vreturn() // N5
- // Exception handler for first try.
- .label(label1) // N6
- .astore(LOCAL2)
- .jsr(label2)
- .aload(LOCAL2) // N10
- .athrow()
- // First finally handler.
- .label(label2) // N12
- .astore(LOCAL4)
- .go(label6)
- // Body of while loop, also second try.
- .label(label3) // N17
- .jsr(label5)
- .push() // N20
- .push()
- .vreturn()
- // Exception handler for second try.
- .label(label4) // N23
- .astore(LOCAL3)
- .jsr(label5)
- .aload(LOCAL3) // N27
- .athrow()
- // Second finally handler.
- .label(label5) // N29
- .astore(LOCAL5)
- .iload(LOCAL1)
- .ifne(label7)
- .push()
- .push()
- .ret(LOCAL5)
- // Test for the while loop.
- .label(label6) // N39
- .iload(LOCAL1)
- .ifne(label3) // Falls through.
- // Exit from finally block.
- .label(label7) // N43
- .ret(LOCAL4)
- // Outermost catch.
- .label(label8) // N45
- .iinc(LOCAL1, 3)
- .vreturn()
- .trycatch(label0, label1, label1)
- .trycatch(label3, label4, label4)
- .trycatch(label0, label8, label8);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(4, methodInfo.maxStack);
- assertEquals(6, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N2",
- "N2=N6,N45,N12",
- "N5=N6,N45",
- "N6=N45,N12",
- "N10=N45",
- "N12=N39,N45",
- "N17=N23,N45,N29",
- "N20=N23,N45",
- "N23=N45,N29",
- "N27=N45",
- "N29=N43,N45,N20,N27",
- "N39=N43,N45,N17",
- "N43=N45,N5,N10",
- "N45=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testImplicitExitInTryCatch() {
+ iconst_0(); // N0
+ istore(1);
+
+ // First try.
+ label(label0); // N2
+ jsr(label2);
+ vreturn(); // N5
+
+ // Exception handler for first try.
+ label(label1); // N6
+ astore(LOCAL2);
+ jsr(label2);
+ aload(LOCAL2); // N10
+ athrow();
+
+ // First finally handler.
+ label(label2); // N12
+ astore(LOCAL4);
+ go(label6);
+
+ // Body of while loop, also second try.
+ label(label3); // N17
+ jsr(label5);
+ push(); // N20
+ push();
+ vreturn();
+
+ // Exception handler for second try.
+ label(label4); // N23
+ astore(LOCAL3);
+ jsr(label5);
+ aload(LOCAL3); // N27
+ athrow();
+
+ // Second finally handler.
+ label(label5); // N29
+ astore(LOCAL5);
+ iload(LOCAL1);
+ ifne(label7);
+ push();
+ push();
+ ret(LOCAL5);
+
+ // Test for the while loop.
+ label(label6); // N39
+ iload(LOCAL1);
+ ifne(label3); // Falls through.
+
+ // Exit from finally block.
+ label(label7); // N43
+ ret(LOCAL4);
+
+ // Outermost catch.
+ label(label8); // N45
+ iinc(LOCAL1, 3);
+ vreturn();
+
+ trycatch(label0, label1, label1);
+ trycatch(label3, label4, label4);
+ trycatch(label0, label8, label8);
+
+ assertMaxs(4, 6);
+ assertGraph(
+ "N0=N2",
+ "N2=N6,N45,N12",
+ "N5=N6,N45",
+ "N6=N45,N12",
+ "N10=N45",
+ "N12=N39,N45",
+ "N17=N23,N45,N29",
+ "N20=N23,N45",
+ "N23=N45,N29",
+ "N27=N45",
+ "N29=N43,N45,N20,N27",
+ "N39=N43,N45,N17",
+ "N43=N45,N5,N10",
+ "N45=");
}
/**
@@ -790,80 +923,71 @@
* com/sun/corba/ee/impl/protocol/CorbaClientDelegateImpl from GlassFish 2. See issue #317823.
*/
@Test
- public void testVisitMaxs_glassFish2CorbaClientDelegateImplExample() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .label(label0) // N0
- .jsr(label4)
- .label(label1) // N3
- .go(label5)
- .label(label2) // N6
- .pop()
- .jsr(label4)
- .label(label3) // N10
- .aconst_null()
- .athrow()
- .label(label4) // N12
- .astore(1)
- .ret(1)
- .label(label5) // N15
- .aconst_null()
- .aconst_null()
- .aconst_null()
- .pop()
- .pop()
- .pop()
- .label(label6) // N21
- .go(label8)
- .label(label7) // N24
- .pop()
- .go(label8)
- .aconst_null()
- .athrow()
- .label(label8) // N30
- .iconst_0()
- .ifne(label0)
- .jsr(label12)
- .label(label9) // N37
- .vreturn()
- .label(label10) // N38
- .pop()
- .jsr(label12)
- .label(label11) // N42
- .aconst_null()
- .athrow()
- .label(label12) // N44
- .astore(2)
- .ret(2)
- .trycatch(label0, label1, label2)
- .trycatch(label2, label3, label2)
- .trycatch(label0, label6, label7)
- .trycatch(label0, label9, label10)
- .trycatch(label10, label11, label10);
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(3, methodInfo.maxStack);
- assertEquals(3, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph(
- "N0=N6,N12,N24,N38",
- "N3=N15,N24,N38",
- "N6=N6,N12,N24,N38",
- "N10=N24,N38",
- "N12=N3,N10,N24,N38",
- "N15=N21,N24,N38",
- "N21=N30,N38",
- "N24=N30,N38",
- "N30=N0,N38,N44",
- "N37=",
- "N38=N38,N44",
- "N42=",
- "N44=N37,N42");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testGlassFish2CorbaClientDelegateImplExample() {
+ label(label0); // N0
+ jsr(label4);
+ label(label1); // N3
+ go(label5);
+ label(label2); // N6
+ pop();
+ jsr(label4);
+ label(label3); // N10
+ aconst_null();
+ athrow();
+ label(label4); // N12
+ astore(1);
+ ret(1);
+ label(label5); // N15
+ aconst_null();
+ aconst_null();
+ aconst_null();
+ pop();
+ pop();
+ pop();
+ label(label6); // N21
+ go(label8);
+ label(label7); // N24
+ pop();
+ go(label8);
+ aconst_null();
+ athrow();
+ label(label8); // N30
+ iconst_0();
+ ifne(label0);
+ jsr(label12);
+ label(label9); // N37
+ vreturn();
+ label(label10); // N38
+ pop();
+ jsr(label12);
+ label(label11); // N42
+ aconst_null();
+ athrow();
+ label(label12); // N44
+ astore(2);
+ ret(2);
+
+ trycatch(label0, label1, label2);
+ trycatch(label2, label3, label2);
+ trycatch(label0, label6, label7);
+ trycatch(label0, label9, label10);
+ trycatch(label10, label11, label10);
+
+ assertMaxs(3, 3);
+ assertGraph(
+ "N0=N6,N12,N24,N38",
+ "N3=N15,N24,N38",
+ "N6=N6,N12,N24,N38",
+ "N10=N24,N38",
+ "N12=N3,N10,N24,N38",
+ "N15=N21,N24,N38",
+ "N21=N30,N38",
+ "N24=N30,N38",
+ "N30=N0,N38,N44",
+ "N37=",
+ "N38=N38,N44",
+ "N42=",
+ "N44=N37,N42");
}
/**
@@ -871,41 +995,31 @@
* the second subroutine coming first in the bytecode instructions sequence.
*/
@Test
- public void testVisitMaxs_implicitExitToAnotherSubroutineInverted() {
- TestCaseBuilder testCase =
- new TestCaseBuilder()
- .go(label3) // N0
- // Second subroutine, returns to caller of first subroutine.
- .label(label0) // N3
- .astore(2)
- .label(label1) // N4
- .ret(1)
- // First subroutine.
- .label(label2) // N6
- .astore(1)
- .aload(0)
- .ifnonnull(label1)
- .jsr(label0) // This JSR never returns, the following code is unreachable.
- .aconst_null() // N14
- .aconst_null()
- .aconst_null()
- .vreturn()
- // Main "subroutine".
- .label(label3) // N18
- .jsr(label2)
- .label(label4) // N21
- .vreturn();
-
- Map> controlFlowGraph = testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(1, methodInfo.maxStack);
- assertEquals(3, methodInfo.maxLocals);
- Map> expectedControlFlowGraph =
- controlFlowGraph("N0=N18", "N3=N4", "N4=N21", "N6=N3,N4", "N14=", "N18=N6", "N21=");
- assertEquals(expectedControlFlowGraph, controlFlowGraph);
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
+ public void testImplicitExitToAnotherSubroutineInverted() {
+ go(label3); // N0
+ // Second subroutine, returns to caller of first subroutine.
+ label(label0); // N3
+ astore(2);
+ label(label1); // N4
+ ret(1);
+ // First subroutine.
+ label(label2); // N6
+ astore(1);
+ aload(0);
+ ifnonnull(label1);
+ jsr(label0); // This JSR never returns, the following code is unreachable.
+ aconst_null(); // N14
+ aconst_null();
+ aconst_null();
+ vreturn();
+ // Main "subroutine".
+ label(label3); // N18
+ jsr(label2);
+ label(label4); // N21
+ vreturn();
+
+ assertMaxs(1, 3);
+ assertGraph("N0=N18", "N3=N4", "N4=N21", "N6=N3,N4", "N14=", "N18=N6", "N21=");
}
/**
@@ -913,286 +1027,43 @@
* in between, when dead code is present.
*/
@Test
- public void testVisitMaxs_framesWithDeadCode() {
- TestCaseBuilder testCase =
- new TestCaseBuilder(Opcodes.V1_7)
- .vreturn()
- // With the default compute maxs algorithm, this dead code block is not considered for
- // the maximum stack size, which works fine for classes up to V1_6. Starting with V1_7,
- // stack map frames are mandatory, even for dead code, and the maximum stack size must
- // take dead code into account. Hopefully it can be computed from the stack map frames,
- // and the instructions in between (without any control flow graph construction or
- // algorithm).
- .label(label0)
- .frame(Opcodes.F_SAME, 0, null, 0, null)
- .aconst_null()
- .vreturn();
-
- testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(1, methodInfo.maxStack);
- assertEquals(1, methodInfo.maxLocals);
- }
+ public void testComputeMaxsFromFramesWithDeadCode() {
+ init(Opcodes.V1_7);
- @Test
- public void testVisitMaxs_frameWithLong() {
- TestCaseBuilder testCase =
- new TestCaseBuilder(Opcodes.V1_7)
- .push2()
- .go(label0)
- .label(label0)
- .frame(Opcodes.F_NEW, 0, null, 1, new Object[] {Opcodes.LONG})
- .aconst_null()
- .vreturn();
-
- testCase.visitMaxs();
- byte[] classFile = testCase.build();
-
- MethodInfo methodInfo = readMaxStackAndLocals(classFile);
- assertEquals(3, methodInfo.maxStack);
- assertEquals(1, methodInfo.maxLocals);
- }
+ vreturn();
+ // With the default compute maxs algorithm, this dead code block is not considered for the
+ // maximum stack size, which works fine for classes up to V1_6. Starting with V1_7, stack map
+ // frames are mandatory, even for dead code, and the maximum stack size must take dead code into
+ // account. Hopefully it can be computed from the stack map frames, and the instructions in
+ // between (without any control flow graph construction or algorithm).
+ label(label0);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ aconst_null();
+ vreturn();
- private static MethodInfo readMaxStackAndLocals(final byte[] classFile) {
- AtomicReference methodInfo = new AtomicReference<>();
- ClassReader classReader = new ClassReader(classFile);
- classReader.accept(
- new ClassVisitor(Opcodes.ASM5) {
- @Override
- public MethodVisitor visitMethod(
- final int access,
- final String name,
- final String descriptor,
- final String signature,
- final String[] exceptions) {
- if (name.equals("m")) {
- return new MethodVisitor(Opcodes.ASM5) {
- @Override
- public void visitMaxs(final int maxStack, final int maxLocals) {
- methodInfo.set(new MethodInfo(maxStack, maxLocals));
- }
- };
- } else {
- return null;
- }
- }
- },
- 0);
- return methodInfo.get();
- }
-
- private static Map> controlFlowGraph(final String... nodes) {
- Map> graph = new HashMap<>();
- for (String node : nodes) {
- StringTokenizer stringTokenizer = new StringTokenizer(node, "=,");
- String key = stringTokenizer.nextToken();
- Set values = new HashSet<>();
- while (stringTokenizer.hasMoreTokens()) {
- values.add(stringTokenizer.nextToken());
- }
- graph.put(key, values);
- }
- return graph;
+ assertMaxs(1, 1);
}
- private static class MethodInfo {
+ @Test
+ public void testComputeMaxsFromFrameWithLong() {
+ init(Opcodes.V1_7);
- public final int maxStack;
- public final int maxLocals;
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "ABC", "get", "()J", false);
+ go(label0);
+ label(label0);
+ methodVisitor.visitFrame(Opcodes.F_NEW, 0, null, 1, new Object[] {Opcodes.LONG});
+ aconst_null();
+ vreturn();
- public MethodInfo(final int maxStack, final int maxLocals) {
- this.maxStack = maxStack;
- this.maxLocals = maxLocals;
- }
+ assertMaxs(3, 1);
}
- private static final class TestCaseBuilder {
-
- private final ClassWriter classWriter;
- private final MethodVisitor methodVisitor;
- private final Label start;
-
- TestCaseBuilder() {
- this(Opcodes.V1_1);
- }
-
- TestCaseBuilder(final int classVersion) {
- classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- classWriter.visit(classVersion, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null);
- MethodVisitor constructor =
- classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
- constructor.visitCode();
- constructor.visitVarInsn(Opcodes.ALOAD, 0);
- constructor.visitMethodInsn(
- Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
- constructor.visitInsn(Opcodes.RETURN);
- constructor.visitMaxs(1, 1);
- constructor.visitEnd();
- methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "m", "()V", null, null);
- methodVisitor.visitCode();
- start = new Label();
- label(start);
- }
-
- TestCaseBuilder nop() {
- methodVisitor.visitInsn(Opcodes.NOP);
- return this;
- }
+ private static class TestClassLoader extends ClassLoader {
- TestCaseBuilder push() {
- methodVisitor.visitInsn(Opcodes.ICONST_0);
- return this;
- }
-
- TestCaseBuilder push2() {
- methodVisitor.visitInsn(Opcodes.LCONST_0);
- return this;
- }
-
- TestCaseBuilder pop() {
- methodVisitor.visitInsn(Opcodes.POP);
- return this;
- }
-
- TestCaseBuilder iconst_0() {
- methodVisitor.visitInsn(Opcodes.ICONST_0);
- return this;
- }
-
- TestCaseBuilder istore(final int var) {
- methodVisitor.visitVarInsn(Opcodes.ISTORE, var);
- return this;
- }
-
- TestCaseBuilder aload(final int var) {
- methodVisitor.visitVarInsn(Opcodes.ALOAD, var);
- return this;
- }
-
- TestCaseBuilder iload(final int var) {
- methodVisitor.visitVarInsn(Opcodes.ILOAD, var);
- return this;
- }
-
- TestCaseBuilder astore(final int var) {
- methodVisitor.visitVarInsn(Opcodes.ASTORE, var);
- return this;
- }
-
- TestCaseBuilder ret(final int var) {
- methodVisitor.visitVarInsn(Opcodes.RET, var);
- return this;
- }
-
- TestCaseBuilder athrow() {
- methodVisitor.visitInsn(Opcodes.ATHROW);
- return this;
- }
-
- TestCaseBuilder aconst_null() {
- methodVisitor.visitInsn(Opcodes.ACONST_NULL);
- return this;
- }
-
- TestCaseBuilder vreturn() {
- methodVisitor.visitInsn(Opcodes.RETURN);
- return this;
- }
-
- TestCaseBuilder label(final Label label) {
- methodVisitor.visitLabel(label);
- return this;
- }
-
- TestCaseBuilder iinc(final int var, final int increment) {
- methodVisitor.visitIincInsn(var, increment);
- return this;
- }
-
- TestCaseBuilder frame(
- final int type,
- final int numLocal,
- final Object[] local,
- final int numStack,
- final Object[] stack) {
- methodVisitor.visitFrame(type, numLocal, local, numStack, stack);
- return this;
- }
-
- TestCaseBuilder go(final Label label) {
- methodVisitor.visitJumpInsn(Opcodes.GOTO, label);
- return this;
- }
-
- TestCaseBuilder jsr(final Label label) {
- methodVisitor.visitJumpInsn(Opcodes.JSR, label);
- return this;
- }
-
- TestCaseBuilder ifnonnull(final Label label) {
- methodVisitor.visitJumpInsn(Opcodes.IFNONNULL, label);
- return this;
- }
-
- TestCaseBuilder ifne(final Label label) {
- methodVisitor.visitJumpInsn(Opcodes.IFNE, label);
- return this;
- }
-
- TestCaseBuilder trycatch(final Label start, final Label end, final Label handler) {
- methodVisitor.visitTryCatchBlock(start, end, handler, null);
- return this;
- }
-
- TestCaseBuilder localVariable(
- final String name,
- final String descriptor,
- final String signature,
- final Label start,
- final Label end,
- final int index) {
- methodVisitor.visitLocalVariable(name, descriptor, signature, start, end, index);
- return this;
- }
-
- /**
- * Calls the visitMaxs method and returns the computed control flow graph.
- *
- * @return the computed control flow graph.
- */
- Map> visitMaxs() {
- methodVisitor.visitMaxs(0, 0);
-
- Map> graph = new HashMap<>();
- Label currentLabel = start;
- while (currentLabel != null) {
- String key = "N" + currentLabel.getOffset();
- Set value = new HashSet<>();
- Edge outgoingEdge = currentLabel.outgoingEdges;
- if ((currentLabel.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) {
- // Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual
- // edges which lead to the instruction just after the jsr, and do not correspond to a
- // possible execution path (see {@link #visitJumpInsn} and
- // {@link Label#FLAG_SUBROUTINE_CALLER}).
- assertNotNull(outgoingEdge);
- outgoingEdge = outgoingEdge.nextEdge;
- }
- while (outgoingEdge != null) {
- value.add("N" + outgoingEdge.successor.getOffset());
- outgoingEdge = outgoingEdge.nextEdge;
- }
- graph.put(key, value);
- currentLabel = currentLabel.nextBasicBlock;
- }
- return graph;
- }
+ public TestClassLoader() {}
- byte[] build() {
- methodVisitor.visitEnd();
- classWriter.visitEnd();
- return classWriter.toByteArray();
+ public Class> defineClass(final String name, final byte[] classFile) {
+ return defineClass(name, classFile, 0, classFile.length);
}
}
}
diff -Nru asm-9.1/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java asm-7.0/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java
--- asm-9.1/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java 2021-02-06 12:22:51.000000000 +0000
+++ asm-7.0/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java 2018-10-27 13:28:52.000000000 +0000
@@ -28,35 +28,26 @@
package org.objectweb.asm;
import static java.util.stream.Collectors.toSet;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.junit.jupiter.api.Assumptions.assumeFalse;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import static org.objectweb.asm.test.Assertions.assertThat;
import java.io.IOException;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
-import java.util.Set;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.objectweb.asm.test.AsmTest;
-import org.objectweb.asm.test.ClassFile;
/**
- * Unit tests for {@link ClassWriter}.
+ * ClassWriter tests.
*
* @author Eric Bruneton
*/
@@ -70,13 +61,11 @@
*/
@Test
public void testInstanceFields() {
- Set actualFields =
- Arrays.stream(ClassWriter.class.getDeclaredFields())
- .filter(field -> !Modifier.isStatic(field.getModifiers()))
- .map(Field::getName)
- .collect(toSet());
-
- Set expectedFields =
+ // IMPORTANT: if this fails, update the string list AND update the logic that resets the
+ // ClassWriter fields in ClassWriter.toByteArray(), if needed (this logic is used to do a
+ // ClassReader->ClassWriter round trip to remove the ASM specific instructions due to large
+ // forward jumps).
+ assertEquals(
new HashSet(
Arrays.asList(
"version",
@@ -105,230 +94,67 @@
"nestHostClassIndex",
"numberOfNestMemberClasses",
"nestMemberClasses",
- "numberOfPermittedSubclasses",
- "permittedSubclasses",
- "firstRecordComponent",
- "lastRecordComponent",
"firstAttribute",
- "compute"));
- // IMPORTANT: if this fails, update the string list AND update the logic that resets the
- // ClassWriter fields in ClassWriter.toByteArray(), if needed (this logic is used to do a
- // ClassReader->ClassWriter round trip to remove the ASM specific instructions due to large
- // forward jumps).
- assertEquals(expectedFields, actualFields);
- }
-
- /**
- * Checks that all the ClassVisitor methods are overridden by ClassWriter and are final.
- * ClassWriter does not take an api version as constructor argument. Therefore, backward
- * compatibility of user subclasses overriding some visit methods of ClassWriter would not be
- * possible to ensure. To prevent this, the ClassWriter visit methods must be final.
- */
- @Test
- public void testVisitMethods_final() {
- ArrayList publicClassVisitorMethods = new ArrayList<>();
- for (Method classVisitorMethod : ClassVisitor.class.getDeclaredMethods()) {
- int modifiers = classVisitorMethod.getModifiers();
- if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
- publicClassVisitorMethods.add(classVisitorMethod);
- }
- }
-
- for (Method classVisitorMethod : publicClassVisitorMethods) {
- try {
- Method classWriterMethod =
- ClassWriter.class.getMethod(
- classVisitorMethod.getName(), classVisitorMethod.getParameterTypes());
- assertTrue(
- Modifier.isFinal(classWriterMethod.getModifiers()), classWriterMethod + " is final");
- } catch (NoSuchMethodException e) {
- fail("ClassWriter must override " + classVisitorMethod);
- }
- }
+ "compute")),
+ Arrays.stream(ClassWriter.class.getDeclaredFields())
+ .filter(field -> !Modifier.isStatic(field.getModifiers()))
+ .map(Field::getName)
+ .collect(toSet()));
}
@Test
public void testNewConst() {
- ClassWriter classWriter = newEmptyClassWriter();
-
+ ClassWriter classWriter = new ClassWriter(0);
+ classWriter.newConst(Byte.valueOf((byte) 0));
+ classWriter.newConst(Character.valueOf('0'));
+ classWriter.newConst(Short.valueOf((short) 0));
classWriter.newConst(Boolean.FALSE);
- classWriter.newConst(Byte.valueOf((byte) 1));
- classWriter.newConst(Character.valueOf('2'));
- classWriter.newConst(Short.valueOf((short) 3));
-
- String constantPoolDump = getConstantPoolDump(classWriter);
- assertTrue(constantPoolDump.contains("constant_pool: 0"));
- assertTrue(constantPoolDump.contains("constant_pool: 1"));
- assertTrue(constantPoolDump.contains("constant_pool: 50"));
- assertTrue(constantPoolDump.contains("constant_pool: 3"));
- }
-
- @Test
- public void testNewConst_illegalArgument() {
- ClassWriter classWriter = newEmptyClassWriter();
-
- Executable newConst = () -> classWriter.newConst(new Object());
-
- Exception exception = assertThrows(IllegalArgumentException.class, newConst);
- assertTrue(exception.getMessage().matches("value java\\.lang\\.Object@.*"));
- }
-
- @Test
- public void testNewUtf8() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newUTF8("A");
-
- assertTrue(getConstantPoolDump(classWriter).contains("constant_pool: A"));
- }
-
- @Test
- public void testNewClass() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newClass("A");
-
- assertTrue(getConstantPoolDump(classWriter).contains("constant_pool: ConstantClassInfo A"));
- }
-
- @Test
- public void testNewMethodType() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newMethodType("()V");
-
- assertTrue(
- getConstantPoolDump(classWriter).contains("constant_pool: ConstantMethodTypeInfo ()V"));
- }
-
- @Test
- public void testNewModule() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newModule("A");
-
- assertTrue(getConstantPoolDump(classWriter).contains("constant_pool: ConstantModuleInfo A"));
- }
-
- @Test
- public void testNewPackage() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newPackage("A");
-
- assertTrue(getConstantPoolDump(classWriter).contains("constant_pool: ConstantPackageInfo A"));
- }
-
- @Test
- @SuppressWarnings("deprecation")
- public void testDeprecatedNewHandle() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newHandle(Opcodes.H_GETFIELD, "A", "h", "I");
-
- assertTrue(
- getConstantPoolDump(classWriter)
- .contains("constant_pool: ConstantMethodHandleInfo 1.ConstantFieldRefInfo A.hI"));
- }
-
- @Test
- public void testNewHandle() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newHandle(Opcodes.H_GETFIELD, "A", "h", "I", false);
-
- assertTrue(
- getConstantPoolDump(classWriter)
- .contains("constant_pool: ConstantMethodHandleInfo 1.ConstantFieldRefInfo A.hI"));
- }
-
- @Test
- public void testNewConstantDynamic() {
- ClassWriter classWriter = newEmptyClassWriter();
-
+ classWriter.newInvokeDynamic("m", "()V", new Handle(Opcodes.H_GETFIELD, "A", "h", "I", false));
classWriter.newConstantDynamic(
"m", "Ljava/lang/String;", new Handle(Opcodes.H_INVOKESTATIC, "A", "m", "()V", false));
-
- String constantPoolDump = getConstantPoolDump(classWriter);
- assertTrue(
- constantPoolDump.contains("constant_pool: ConstantDynamicInfo 0.mLjava/lang/String;"));
- assertTrue(
- constantPoolDump.contains(
- "constant_pool: ConstantMethodHandleInfo 6.ConstantMethodRefInfo A.m()V"));
- assertTrue(constantPoolDump.contains("constant_pool: BootstrapMethods"));
- }
-
- @Test
- public void testNewInvokeDynamic() {
- ClassWriter classWriter = newEmptyClassWriter();
-
- classWriter.newInvokeDynamic("m", "()V", new Handle(Opcodes.H_GETFIELD, "A", "h", "I", false));
-
- String constantPoolDump = getConstantPoolDump(classWriter);
- assertTrue(constantPoolDump.contains("ConstantInvokeDynamicInfo 0.m()V"));
- assertTrue(
- constantPoolDump.contains(
- "constant_pool: ConstantMethodHandleInfo 1.ConstantFieldRefInfo A.hI"));
- assertTrue(constantPoolDump.contains("constant_pool: BootstrapMethods"));
- }
-
- @Test
- public void testNewField() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newField("A", "f", "I");
-
- assertTrue(
- getConstantPoolDump(classWriter).contains("constant_pool: ConstantFieldRefInfo A.fI"));
- }
-
- @Test
- public void testNewMethod() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newMethod("A", "m", "()V", false);
-
- assertTrue(
- getConstantPoolDump(classWriter).contains("constant_pool: ConstantMethodRefInfo A.m()V"));
- }
-
- @Test
- public void testNewNameType() {
- ClassWriter classWriter = newEmptyClassWriter();
-
classWriter.newNameType("m", "()V");
- assertTrue(
- getConstantPoolDump(classWriter).contains("constant_pool: ConstantNameAndTypeInfo m()V"));
+ assertThrows(IllegalArgumentException.class, () -> classWriter.newConst(new Object()));
}
@ParameterizedTest
@ValueSource(ints = {65535, 65536})
- public void testToByteArray_constantPoolSizeTooLarge(final int constantPoolCount) {
- ClassWriter classWriter = newEmptyClassWriter();
+ public void testConstantPoolSizeTooLarge(final int constantPoolCount) {
+ ClassWriter classWriter = new ClassWriter(0);
+ String className = "A";
+ classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null);
int initConstantPoolCount = 5;
for (int i = 0; i < constantPoolCount - initConstantPoolCount; ++i) {
classWriter.newConst(Integer.valueOf(i));
}
-
- Executable toByteArray = () -> classWriter.toByteArray();
-
if (constantPoolCount > 65535) {
- ClassTooLargeException exception = assertThrows(ClassTooLargeException.class, toByteArray);
- assertEquals("C", exception.getClassName());
- assertEquals(constantPoolCount, exception.getConstantPoolCount());
- assertEquals("Class too large: C", exception.getMessage());
+ ClassTooLargeException thrown =
+ assertThrows(ClassTooLargeException.class, () -> classWriter.toByteArray());
+ assertEquals(className, thrown.getClassName());
+ assertEquals(constantPoolCount, thrown.getConstantPoolCount());
+ assertEquals("Class too large: A", thrown.getMessage());
} else {
- assertDoesNotThrow(toByteArray);
+ classWriter.toByteArray();
}
}
@ParameterizedTest
@ValueSource(ints = {65535, 65536})
- void testToByteArray_methodCodeSizeTooLarge(final int methodCodeSize) {
- ClassWriter classWriter = newEmptyClassWriter();
+ void testMethodCodeSizeTooLarge(final int methodCodeSize) {
+ ClassWriter classWriter = new ClassWriter(0);
+ String className = "A";
String methodName = "m";
String descriptor = "()V";
+ classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null);
MethodVisitor methodVisitor =
classWriter.visitMethod(Opcodes.ACC_STATIC, methodName, descriptor, null, null);
methodVisitor.visitCode();
@@ -338,53 +164,41 @@
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
-
- Executable toByteArray = () -> classWriter.toByteArray();
-
if (methodCodeSize > 65535) {
- MethodTooLargeException exception = assertThrows(MethodTooLargeException.class, toByteArray);
- assertEquals(methodName, exception.getMethodName());
- assertEquals("C", exception.getClassName());
- assertEquals(descriptor, exception.getDescriptor());
- assertEquals(methodCodeSize, exception.getCodeSize());
- assertEquals("Method too large: C.m ()V", exception.getMessage());
+ MethodTooLargeException thrown =
+ assertThrows(MethodTooLargeException.class, () -> classWriter.toByteArray());
+ assertEquals(methodName, thrown.getMethodName());
+ assertEquals(className, thrown.getClassName());
+ assertEquals(descriptor, thrown.getDescriptor());
+ assertEquals(methodCodeSize, thrown.getCodeSize());
+ assertEquals("Method too large: A.m ()V", thrown.getMessage());
} else {
- assertDoesNotThrow(toByteArray);
+ classWriter.toByteArray();
}
}
@Test
- void testToByteArray_largeSourceDebugExtension() {
- ClassWriter classWriter = newEmptyClassWriter();
+ void testLargeSourceDebugExtension() {
+ ClassWriter classWriter = new ClassWriter(0);
classWriter.visitSource("Test.java", new String(new char[100000]));
-
classWriter.toByteArray();
-
- assertTrue(getDump(classWriter).contains("attribute_name_index: SourceDebugExtension"));
}
- /**
- * Tests that the COMPUTE_MAXS option works correctly on classes with very large or deeply nested
- * subroutines (#307600, #311642).
- *
- * @throws IOException if the input class file can't be read.
- */
- @ParameterizedTest
- @ValueSource(strings = {"Issue307600.class", "Issue311642.class"})
- public void testToByteArray_computeMaxs_largeSubroutines(final String classFileName)
- throws IOException {
- ClassReader classReader =
- new ClassReader(Files.newInputStream(Paths.get("src/test/resources/" + classFileName)));
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- classReader.accept(classWriter, attributes(), 0);
-
- Executable toByteArray = () -> classWriter.toByteArray();
-
- assertDoesNotThrow(toByteArray);
+ @Test
+ public void testIllegalConsecutiveFrames() {
+ MethodVisitor methodVisitor =
+ new ClassWriter(0).visitMethod(Opcodes.ACC_STATIC, "m", "()V", null, null);
+ methodVisitor.visitCode();
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ assertThrows(
+ IllegalStateException.class,
+ () ->
+ methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null));
}
@Test
- public void testToByteArray_computeFrames_mergeLongOrDouble() {
+ public void testComputeFramesMergeLongOrDouble() {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "A", null, "java/lang/Object", null);
// Generate a default constructor, so that we can instantiate the class.
@@ -397,6 +211,7 @@
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
+
// A method with a long local variable using slots 0 and 1, with an int stored in slot 1 in a
// branch. At the end of the method, the stack map frame should contain 'TOP' for slot 0,
// otherwise the class instantiation fails with a verification error.
@@ -412,51 +227,12 @@
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
classWriter.visitEnd();
-
- byte[] classFile = classWriter.toByteArray();
-
- assertDoesNotThrow(() -> new ClassFile(classFile).newInstance());
- }
-
- @Test
- public void testToByteArray_computeFrames_highDimensionArrays() {
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
- classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "A", null, "java/lang/Object", null);
- MethodVisitor methodVisitor =
- classWriter.visitMethod(
- Opcodes.ACC_STATIC,
- "m",
- "(I[[[[[[[[Ljava/lang/Integer;[[[[[[[[Ljava/lang/Long;)Ljava/lang/Object;",
- null,
- null);
- methodVisitor.visitCode();
- methodVisitor.visitVarInsn(Opcodes.ILOAD, 0);
- Label thenLabel = new Label();
- Label endIfLabel = new Label();
- methodVisitor.visitJumpInsn(Opcodes.IFNE, thenLabel);
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
- methodVisitor.visitJumpInsn(Opcodes.GOTO, endIfLabel);
- methodVisitor.visitLabel(thenLabel);
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
- // At this point the stack can contain either an 8 dimensions Integer array or an 8 dimensions
- // Long array. The merged type computed with the COMPUTE_FRAMES option should therefore be an
- // 8 dimensions Number array.
- methodVisitor.visitLabel(endIfLabel);
- methodVisitor.visitInsn(Opcodes.ARETURN);
- methodVisitor.visitMaxs(0, 0);
- methodVisitor.visitEnd();
- classWriter.visitEnd();
-
- byte[] classFile = classWriter.toByteArray();
-
- // Check that the merged frame type is correctly computed.
- assertTrue(new ClassFile(classFile).toString().contains("[[[[[[[[Ljava/lang/Number;"));
+ loadAndInstantiate("A", classWriter.toByteArray());
}
@Test
public void testGetCommonSuperClass() {
ClassWriter classWriter = new ClassWriter(0);
-
assertEquals(
"java/lang/Object",
classWriter.getCommonSuperClass("java/lang/Object", "java/lang/Integer"));
@@ -473,16 +249,12 @@
"java/lang/Throwable",
classWriter.getCommonSuperClass(
"java/lang/IndexOutOfBoundsException", "java/lang/AssertionError"));
- Exception exception =
- assertThrows(
- TypeNotPresentException.class,
- () -> classWriter.getCommonSuperClass("-", "java/lang/Object"));
- assertEquals("Type - not present", exception.getMessage());
- exception =
- assertThrows(
- TypeNotPresentException.class,
- () -> classWriter.getCommonSuperClass("java/lang/Object", "-"));
- assertEquals("Type - not present", exception.getMessage());
+ assertThrows(
+ TypeNotPresentException.class,
+ () -> classWriter.getCommonSuperClass("-", "java/lang/Object"));
+ assertThrows(
+ TypeNotPresentException.class,
+ () -> classWriter.getCommonSuperClass("java/lang/Object", "-"));
}
/** Tests that a ClassReader -> ClassWriter transform leaves classes unchanged. */
@@ -492,10 +264,8 @@
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
-
classReader.accept(classWriter, attributes(), 0);
-
- assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
+ assertThatClass(classWriter.toByteArray()).isEqualTo(classFile);
}
/**
@@ -504,18 +274,13 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_skipCode(
+ public void testReadAndWriteWithSkipCode(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
-
classReader.accept(classWriter, attributes(), ClassReader.SKIP_CODE);
-
- assertTrue(
- new ClassFile(classWriter.toByteArray())
- .toString()
- .contains(classParameter.getInternalName()));
+ assertThatClass(classWriter.toByteArray()).contains(classParameter.getInternalName());
}
/**
@@ -524,15 +289,13 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_copyPool(
+ public void testReadAndWriteWithCopyPool(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(classReader, 0);
-
classReader.accept(classWriter, attributes(), 0);
-
- assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
+ assertThatClass(classWriter.toByteArray()).isEqualTo(classFile);
}
/**
@@ -541,33 +304,46 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_expandFrames(
+ public void testReadAndWriteWithExpandFrames(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
-
classReader.accept(classWriter, attributes(), ClassReader.EXPAND_FRAMES);
-
- assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
+ assertThatClass(classWriter.toByteArray()).isEqualTo(classFile);
}
/**
* Tests that a ClassReader -> ClassWriter transform with the COMPUTE_MAXS option leaves classes
- * unchanged. This is not true in general (the valid max stack and max locals for a given method
- * are not unique), but this should be the case with our precompiled classes.
+ * unchanged. This is not true in general (the valid max stack and max locals for a given method),
+ * but this should be the case with our precompiled classes.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_computeMaxs(
+ public void testReadAndWriteWithComputeMaxs(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-
classReader.accept(classWriter, attributes(), 0);
+ assertThatClass(classWriter.toByteArray()).isEqualTo(classFile);
+ }
- assertEquals(new ClassFile(classFile), new ClassFile(classWriter.toByteArray()));
+ /**
+ * Tests that a ClassReader -> ClassWriter transform with the COMPUTE_MAXS option works correctly
+ * on classes with very large or deeply nested subroutines (#307600, #311642).
+ *
+ * @throws IOException if the input class file can't be read.
+ */
+ @ParameterizedTest
+ @ValueSource(strings = {"Issue307600.class", "Issue311642.class"})
+ public void testReadAndWriteWithComputeMaxsAndLargeSubroutines(final String classFileName)
+ throws IOException {
+ ClassReader classReader =
+ new ClassReader(Files.newInputStream(Paths.get("src/test/resources/" + classFileName)));
+ ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ classReader.accept(classWriter, attributes(), 0);
+ classWriter.toByteArray();
}
/**
@@ -576,20 +352,15 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_computeMaxs_newInstance(
+ public void testReadWriteAndLoadWithComputeMaxs(
final PrecompiledClass classParameter, final Api apiParameter) throws Exception {
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classReader.accept(classWriter, attributes(), 0);
-
- Executable newInstance = () -> new ClassFile(classWriter.toByteArray()).newInstance();
-
- if (classParameter.isNotCompatibleWithCurrentJdk()) {
- assertThrows(UnsupportedClassVersionError.class, newInstance);
- } else {
- assertDoesNotThrow(newInstance);
- }
+ assertThat(() -> loadAndInstantiate(classParameter.getName(), classWriter.toByteArray()))
+ .succeedsOrThrows(UnsupportedClassVersionError.class)
+ .when(classParameter.isMoreRecentThanCurrentJdk());
}
/**
@@ -598,47 +369,31 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_computeFrames(
+ public void testReadAndWriteWithComputeFrames(
final PrecompiledClass classParameter, final Api apiParameter) {
- assumeFalse(hasJsrOrRetInstructions(classParameter));
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+
+ // jdk3.AllInstructions and jdk3.LargeMethod contain JSR/RET instructions,
+ // incompatible with COMPUTE_FRAMES.
+ if (classParameter == PrecompiledClass.JDK3_ALL_INSTRUCTIONS
+ || classParameter == PrecompiledClass.JDK3_LARGE_METHOD) {
+ assertThrows(RuntimeException.class, () -> classReader.accept(classWriter, attributes(), 0));
+ return;
+ }
classReader.accept(classWriter, attributes(), 0);
byte[] newClassFile = classWriter.toByteArray();
-
// The computed stack map frames should be equal to the original ones, if any (classes before
// JDK8 don't have ones). This is not true in general (the valid frames for a given method are
// not unique), but this should be the case with our precompiled classes.
if (classParameter.isMoreRecentThan(Api.ASM4)) {
- assertEquals(new ClassFile(classFile), new ClassFile(newClassFile));
- }
- Executable newInstance = () -> new ClassFile(newClassFile).newInstance();
- if (classParameter.isNotCompatibleWithCurrentJdk()) {
- assertThrows(UnsupportedClassVersionError.class, newInstance);
- } else {
- assertDoesNotThrow(newInstance);
+ assertThatClass(newClassFile).isEqualTo(classFile);
}
- }
-
- /**
- * Tests that classes going through a ClassReader -> ClassWriter transform with the COMPUTE_FRAMES
- * option can be loaded and pass bytecode verification.
- */
- @ParameterizedTest
- @MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_computeFrames_jsrInstructions(
- final PrecompiledClass classParameter, final Api apiParameter) {
- assumeTrue(hasJsrOrRetInstructions(classParameter));
- byte[] classFile = classParameter.getBytes();
- ClassReader classReader = new ClassReader(classFile);
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
-
- Executable accept = () -> classReader.accept(classWriter, attributes(), 0);
-
- Exception exception = assertThrows(IllegalArgumentException.class, accept);
- assertEquals("JSR/RET are not supported with computeFrames option", exception.getMessage());
+ assertThat(() -> loadAndInstantiate(classParameter.getName(), newClassFile))
+ .succeedsOrThrows(UnsupportedClassVersionError.class)
+ .when(classParameter.isMoreRecentThanCurrentJdk());
}
/**
@@ -647,28 +402,33 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_skipAndComputeFrames(
+ public void testReadAndWriteWithSkipAndComputeFrames(
final PrecompiledClass classParameter, final Api apiParameter) {
- assumeFalse(hasJsrOrRetInstructions(classParameter));
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+
+ // jdk3.AllInstructions and jdk3.LargeMethod contain JSR/RET instructions,
+ // incompatible with COMPUTE_FRAMES.
+ if (classParameter == PrecompiledClass.JDK3_ALL_INSTRUCTIONS
+ || classParameter == PrecompiledClass.JDK3_LARGE_METHOD) {
+ assertThrows(
+ RuntimeException.class,
+ () -> classReader.accept(classWriter, attributes(), ClassReader.SKIP_FRAMES));
+ return;
+ }
classReader.accept(classWriter, attributes(), ClassReader.SKIP_FRAMES);
byte[] newClassFile = classWriter.toByteArray();
-
// The computed stack map frames should be equal to the original ones, if any (classes before
// JDK8 don't have ones). This is not true in general (the valid frames for a given method are
// not unique), but this should be the case with our precompiled classes.
if (classParameter.isMoreRecentThan(Api.ASM4)) {
- assertEquals(new ClassFile(classFile), new ClassFile(newClassFile));
- }
- Executable newInstance = () -> new ClassFile(newClassFile).newInstance();
- if (classParameter.isNotCompatibleWithCurrentJdk()) {
- assertThrows(UnsupportedClassVersionError.class, newInstance);
- } else {
- assertDoesNotThrow(newInstance);
+ assertThatClass(newClassFile).isEqualTo(classFile);
}
+ assertThat(() -> loadAndInstantiate(classParameter.getName(), newClassFile))
+ .succeedsOrThrows(UnsupportedClassVersionError.class)
+ .when(classParameter.isMoreRecentThanCurrentJdk());
}
/**
@@ -677,24 +437,28 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_computeFramesAndDeadCode(
+ public void testReadAndWriteWithComputeFramesAndDeadCode(
final PrecompiledClass classParameter, final Api apiParameter) {
- assumeFalse(
- hasJsrOrRetInstructions(classParameter) || classParameter.isMoreRecentThan(apiParameter));
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = new DeadCodeInserter(apiParameter.value(), classWriter);
- classReader.accept(classVisitor, attributes(), ClassReader.SKIP_FRAMES);
-
- byte[] newClassFile = classWriter.toByteArray();
- Executable newInstance = () -> new ClassFile(newClassFile).newInstance();
- if (classParameter.isNotCompatibleWithCurrentJdk()) {
- assertThrows(UnsupportedClassVersionError.class, newInstance);
- } else {
- assertDoesNotThrow(newInstance);
+ // jdk3.AllInstructions and jdk3.LargeMethod contain JSR/RET instructions,
+ // incompatible with COMPUTE_FRAMES.
+ if (classParameter == PrecompiledClass.JDK3_ALL_INSTRUCTIONS
+ || classParameter == PrecompiledClass.JDK3_LARGE_METHOD
+ || classParameter.isMoreRecentThan(apiParameter)) {
+ assertThrows(
+ RuntimeException.class,
+ () -> classReader.accept(classVisitor, attributes(), ClassReader.SKIP_FRAMES));
+ return;
}
+ classReader.accept(classVisitor, attributes(), ClassReader.SKIP_FRAMES);
+
+ assertThat(() -> loadAndInstantiate(classParameter.getName(), classWriter.toByteArray()))
+ .succeedsOrThrows(UnsupportedClassVersionError.class)
+ .when(classParameter.isMoreRecentThanCurrentJdk());
}
/**
@@ -705,15 +469,24 @@
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_ALL_APIS)
- public void testReadAndWrite_largeMethod(
+ public void testReadAndWriteWithResizeMethod(
final PrecompiledClass classParameter, final Api apiParameter) {
byte[] classFile = classParameter.getBytes();
- assumeFalse(
- classFile.length > Short.MAX_VALUE || classParameter.isMoreRecentThan(apiParameter));
+ if (classFile.length > Short.MAX_VALUE) {
+ return;
+ }
+
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriterWithoutGetCommonSuperClass();
ForwardJumpNopInserter forwardJumpNopInserter =
new ForwardJumpNopInserter(apiParameter.value(), classWriter);
+
+ if (classParameter.isMoreRecentThan(apiParameter)) {
+ assertThrows(
+ RuntimeException.class,
+ () -> classReader.accept(forwardJumpNopInserter, attributes(), 0));
+ return;
+ }
classReader.accept(forwardJumpNopInserter, attributes(), 0);
if (!forwardJumpNopInserter.transformed) {
classWriter = new ClassWriterWithoutGetCommonSuperClass();
@@ -722,40 +495,61 @@
}
byte[] transformedClass = classWriter.toByteArray();
+ assertThat(() -> loadAndInstantiate(classParameter.getName(), transformedClass))
+ .succeedsOrThrows(UnsupportedClassVersionError.class)
+ .when(classParameter.isMoreRecentThanCurrentJdk());
- Executable newInstance = () -> new ClassFile(transformedClass).newInstance();
- if (classParameter.isNotCompatibleWithCurrentJdk()) {
- assertThrows(UnsupportedClassVersionError.class, newInstance);
- } else {
- assertDoesNotThrow(newInstance);
- }
// The transformed class should have the same structure as the original one (#317792).
ClassWriter originalClassWithoutCode = new ClassWriter(0);
classReader.accept(originalClassWithoutCode, ClassReader.SKIP_CODE);
ClassWriter transformedClassWithoutCode = new ClassWriter(0);
new ClassReader(transformedClass).accept(transformedClassWithoutCode, ClassReader.SKIP_CODE);
- assertEquals(
- new ClassFile(originalClassWithoutCode.toByteArray()),
- new ClassFile(transformedClassWithoutCode.toByteArray()));
- }
-
- private static boolean hasJsrOrRetInstructions(final PrecompiledClass classParameter) {
- return classParameter == PrecompiledClass.JDK3_ALL_INSTRUCTIONS
- || classParameter == PrecompiledClass.JDK3_LARGE_METHOD;
+ assertThatClass(transformedClassWithoutCode.toByteArray())
+ .isEqualTo(originalClassWithoutCode.toByteArray());
}
- private static ClassWriter newEmptyClassWriter() {
+ /** Tests modules without any optional data (ModulePackage, ModuleMainClass, etc). */
+ @Test
+ public void testReadAndWriteWithBasicModule() {
+ byte[] classFile = PrecompiledClass.JDK9_MODULE.getBytes();
+ ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
- classWriter.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null);
- return classWriter;
- }
+ ClassVisitor classVisitor =
+ new ClassVisitor(Opcodes.ASM7, classWriter) {
- private static String getConstantPoolDump(final ClassWriter classWriter) {
- return new ClassFile(classWriter.toByteArray()).getConstantPoolDump();
- }
-
- private static String getDump(final ClassWriter classWriter) {
- return new ClassFile(classWriter.toByteArray()).toString();
+ @Override
+ public ModuleVisitor visitModule(
+ final String name, final int access, final String version) {
+ return new ModuleVisitor(api, super.visitModule(name, access, version)) {
+
+ @Override
+ public void visitMainClass(final String mainClass) {}
+
+ @Override
+ public void visitPackage(final String packaze) {}
+
+ @Override
+ public void visitRequire(
+ final String module, final int access, final String version) {
+ super.visitRequire(module, access, null);
+ }
+
+ @Override
+ public void visitExport(
+ final String packaze, final int access, final String... modules) {
+ super.visitExport(packaze, access, (String[]) null);
+ }
+
+ @Override
+ public void visitOpen(
+ final String packaze, final int access, final String... modules) {
+ super.visitOpen(packaze, access, (String[]) null);
+ }
+ };
+ }
+ };
+ classReader.accept(classVisitor, null, 0);
+ classWriter.toByteArray();
}
private static Attribute[] attributes() {
@@ -961,7 +755,7 @@
final String[] exceptions) {
return new MethodVisitor(
api, super.visitMethod(access, name, descriptor, signature, exceptions)) {
- private final HashSet