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 @@ * *
  *   =====================================
- *   |...DIM|KIND|.F|...............VALUE|
+ *   |.DIM|KIND|FLAG|...............VALUE|
  *   =====================================
  * 
* *
    - *
  • 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