diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java
index 0b6818dca87..b5127eb142e 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java
@@ -74,7 +74,10 @@ public abstract class AnnotationVisitor {
*/
protected final int api;
- /** The annotation visitor to which this visitor must delegate method calls. May be null. */
+ /**
+ * The annotation visitor to which this visitor must delegate method calls. May be {@literal
+ * null}.
+ */
protected AnnotationVisitor av;
/**
@@ -93,11 +96,20 @@ public abstract class AnnotationVisitor {
* @param api the ASM API version implemented by this visitor. Must be one of {@link
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
* @param annotationVisitor the annotation visitor to which this visitor must delegate method
- * calls. May be null.
+ * calls. May be {@literal null}.
*/
+ @SuppressWarnings("deprecation")
public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
- throw new IllegalArgumentException();
+ if (api != Opcodes.ASM8
+ && api != Opcodes.ASM7
+ && api != Opcodes.ASM6
+ && api != Opcodes.ASM5
+ && api != Opcodes.ASM4
+ && api != Opcodes.ASM9_EXPERIMENTAL) {
+ throw new IllegalArgumentException("Unsupported api " + api);
+ }
+ if (api == Opcodes.ASM9_EXPERIMENTAL) {
+ Constants.checkAsmExperimental(this);
}
this.api = api;
this.av = annotationVisitor;
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java
index 35320e47329..9517d6b0255 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java
@@ -122,7 +122,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private AnnotationWriter nextAnnotation;
// -----------------------------------------------------------------------------------------------
- // Constructors
+ // Constructors and factories
// -----------------------------------------------------------------------------------------------
/**
@@ -135,15 +135,15 @@ final class AnnotationWriter extends AnnotationVisitor {
* the visited content must be stored. This ByteVector must already contain all the fields of
* the structure except the last one (the element_value_pairs array).
* @param previousAnnotation the previously visited annotation of the
- * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
- * other cases (e.g. nested or array annotations).
+ * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
+ * {@literal null} in other cases (e.g. nested or array annotations).
*/
AnnotationWriter(
final SymbolTable symbolTable,
final boolean useNamedValues,
final ByteVector annotation,
final AnnotationWriter previousAnnotation) {
- super(Opcodes.ASM7);
+ super(/* latest api = */ Opcodes.ASM8);
this.symbolTable = symbolTable;
this.useNamedValues = useNamedValues;
this.annotation = annotation;
@@ -156,21 +156,61 @@ final class AnnotationWriter extends AnnotationVisitor {
}
/**
- * Constructs a new {@link AnnotationWriter} using named values.
+ * Creates a new {@link AnnotationWriter} using named values.
*
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
- * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
- * the visited content must be stored. This ByteVector must already contain all the fields of
- * the structure except the last one (the element_value_pairs array).
+ * @param descriptor the class descriptor of the annotation class.
* @param previousAnnotation the previously visited annotation of the
- * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
- * other cases (e.g. nested or array annotations).
+ * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
+ * {@literal null} in other cases (e.g. nested or array annotations).
+ * @return a new {@link AnnotationWriter} for the given annotation descriptor.
*/
- AnnotationWriter(
+ static AnnotationWriter create(
final SymbolTable symbolTable,
- final ByteVector annotation,
+ final String descriptor,
final AnnotationWriter previousAnnotation) {
- this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
+ // Create a ByteVector to hold an 'annotation' JVMS structure.
+ // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
+ ByteVector annotation = new ByteVector();
+ // Write type_index and reserve space for num_element_value_pairs.
+ annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
+ return new AnnotationWriter(
+ symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
+ }
+
+ /**
+ * Creates a new {@link AnnotationWriter} using named values.
+ *
+ * @param symbolTable where the constants used in this AnnotationWriter must be stored.
+ * @param typeRef a reference to the annotated type. The sort of this type reference must be
+ * {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
+ * TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
+ * {@link TypeReference}.
+ * @param typePath the path to the annotated type argument, wildcard bound, array element type, or
+ * static inner type within 'typeRef'. May be {@literal null} if the annotation targets
+ * 'typeRef' as a whole.
+ * @param descriptor the class descriptor of the annotation class.
+ * @param previousAnnotation the previously visited annotation of the
+ * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or
+ * {@literal null} in other cases (e.g. nested or array annotations).
+ * @return a new {@link AnnotationWriter} for the given type annotation reference and descriptor.
+ */
+ static AnnotationWriter create(
+ final SymbolTable symbolTable,
+ final int typeRef,
+ final TypePath typePath,
+ final String descriptor,
+ final AnnotationWriter previousAnnotation) {
+ // Create a ByteVector to hold a 'type_annotation' JVMS structure.
+ // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
+ ByteVector typeAnnotation = new ByteVector();
+ // Write target_type, target_info, and target_path.
+ TypeReference.putTarget(typeRef, typeAnnotation);
+ TypePath.put(typePath, typeAnnotation);
+ // Write type_index and reserve space for num_element_value_pairs.
+ typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
+ return new AnnotationWriter(
+ symbolTable, /* useNamedValues = */ true, typeAnnotation, previousAnnotation);
}
// -----------------------------------------------------------------------------------------------
@@ -275,7 +315,7 @@ final class AnnotationWriter extends AnnotationVisitor {
}
// Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
- return new AnnotationWriter(symbolTable, annotation, null);
+ return new AnnotationWriter(symbolTable, /* useNamedValues = */ true, annotation, null);
}
@Override
@@ -315,7 +355,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* 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 null.
+ * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or {@literal null}.
* @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this
* annotation and all its predecessors. This includes the size of the attribute_name_index and
* attribute_length fields.
@@ -334,6 +374,56 @@ final class AnnotationWriter extends AnnotationVisitor {
return attributeSize;
}
+ /**
+ * Returns the size of the Runtime[In]Visible[Type]Annotations attributes containing the given
+ * annotations and all their 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
@@ -366,6 +456,51 @@ final class AnnotationWriter extends AnnotationVisitor {
}
}
+ /**
+ * 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 --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java
index 3193ff8fad7..daadeb263e2 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java
@@ -59,7 +59,7 @@
package jdk.internal.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
@@ -83,7 +83,7 @@ public class Attribute {
/**
* The next attribute in this attribute list (Attribute instances can be linked via this field to
- * store a list of class, field, method or code attributes). May be {@literal null}.
+ * store a list of class, field, method or Code attributes). May be {@literal null}.
*/
Attribute nextAttribute;
@@ -111,9 +111,9 @@ public class Attribute {
}
/**
- * Returns {@literal true} if this type of attribute is a code attribute.
+ * Returns {@literal true} if this type of attribute is a Code attribute.
*
- * @return {@literal true} if this type of attribute is a code attribute.
+ * @return {@literal true} if this type of attribute is a Code attribute.
*/
public boolean isCodeAttribute() {
return false;
@@ -123,7 +123,7 @@ public class Attribute {
* Returns the labels corresponding to this attribute.
*
* @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
- * a code attribute that contains labels.
+ * a Code attribute that contains labels.
*/
protected Label[] getLabels() {
return new Label[0];
@@ -135,18 +135,18 @@ public class Attribute {
* ClassReader.
*
* @param classReader the class that contains the attribute to be read.
- * @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
- * 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
+ * @param offset index of the first byte of the attribute's content in {@link ClassReader}. The 6
+ * attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param length the length of the attribute's content (excluding the 6 attribute header bytes).
* @param charBuffer the buffer to be used to call the ClassReader methods requiring a
* 'charBuffer' parameter.
* @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
- * in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
+ * in {@link ClassReader}, or -1 if the attribute to be read is not a Code attribute. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
- * is not a code attribute.
+ * is not a Code attribute.
* @return a new {@link Attribute} object corresponding to the specified bytes.
*/
protected Attribute read(
@@ -158,7 +158,7 @@ public class Attribute {
final Label[] labels) {
Attribute attribute = new Attribute(type);
attribute.content = new byte[length];
- System.arraycopy(classReader.b, offset, attribute.content, 0, length);
+ System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
return attribute;
}
@@ -169,16 +169,16 @@ public class Attribute {
*
* @param classWriter the class to which this attribute must be added. This parameter can be used
* to add the items that corresponds to this attribute to the constant pool of this class.
- * @param code the bytecode of the method corresponding to this code attribute, or {@literal null}
- * if this attribute is not a code attribute. Corresponds to the 'code' field of the Code
+ * @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
+ * if this attribute is not a Code attribute. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to this code
- * attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length'
+ * attribute, or 0 if this attribute is not a Code attribute. Corresponds to the 'code_length'
* field of the Code attribute.
- * @param maxStack the maximum stack size of the method corresponding to this code attribute, or
- * -1 if this attribute is not a code attribute.
+ * @param maxStack the maximum stack size of the method corresponding to this Code attribute, or
+ * -1 if this attribute is not a Code attribute.
* @param maxLocals the maximum number of local variables of the method corresponding to this code
- * attribute, or -1 if this attribute is not a code attribute.
+ * attribute, or -1 if this attribute is not a Code attribute.
* @return the byte array form of this attribute.
*/
protected ByteVector write(
@@ -228,16 +228,16 @@ public class Attribute {
* attribute_length) per attribute. Also adds the attribute type names to the constant pool.
*
* @param symbolTable where the constants used in the attributes must be stored.
- * @param code the bytecode of the method corresponding to these code attributes, or {@literal
- * null} if they are not code attributes. Corresponds to the 'code' field of the Code
+ * @param code the bytecode of the method corresponding to these Code attributes, or {@literal
+ * null} if they are not Code attributes. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to these code
- * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
+ * attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
* the Code attribute.
- * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
- * -1 if they are not code attributes.
+ * @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
+ * -1 if they are not Code attributes.
* @param maxLocals the maximum number of local variables of the method corresponding to these
- * code attributes, or -1 if they are not code attribute.
+ * Code attributes, or -1 if they are not Code attribute.
* @return the size of all the attributes in this attribute list. This size includes the size of
* the attribute headers.
*/
@@ -258,6 +258,42 @@ public class Attribute {
return size;
}
+ /**
+ * Returns the total size in bytes of all the attributes that correspond to the given field,
+ * method or class access flags and signature. This size includes the 6 header bytes
+ * (attribute_name_index and attribute_length) per attribute. Also adds the attribute type names
+ * to the constant pool.
+ *
+ * @param symbolTable where the constants used in the attributes must be stored.
+ * @param accessFlags some field, method or class access flags.
+ * @param signatureIndex the constant pool index of a field, method of class signature.
+ * @return the size of all the attributes in bytes. This size includes the size of the attribute
+ * headers.
+ */
+ static int computeAttributesSize(
+ final SymbolTable symbolTable, final int accessFlags, final int signatureIndex) {
+ int size = 0;
+ // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
+ if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
+ && symbolTable.getMajorVersion() < Opcodes.V1_5) {
+ // Synthetic attributes always use 6 bytes.
+ symbolTable.addConstantUtf8(Constants.SYNTHETIC);
+ size += 6;
+ }
+ if (signatureIndex != 0) {
+ // Signature attributes always use 8 bytes.
+ symbolTable.addConstantUtf8(Constants.SIGNATURE);
+ size += 8;
+ }
+ // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
+ if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
+ // Deprecated attributes always use 6 bytes.
+ symbolTable.addConstantUtf8(Constants.DEPRECATED);
+ size += 6;
+ }
+ return size;
+ }
+
/**
* Puts all the attributes of the attribute list that begins with this attribute, in the given
* byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
@@ -280,16 +316,16 @@ public class Attribute {
* attribute.
*
* @param symbolTable where the constants used in the attributes must be stored.
- * @param code the bytecode of the method corresponding to these code attributes, or {@literal
- * null} if they are not code attributes. Corresponds to the 'code' field of the Code
+ * @param code the bytecode of the method corresponding to these Code attributes, or {@literal
+ * null} if they are not Code attributes. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to these code
- * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
+ * attributes, or 0 if they are not Code attributes. Corresponds to the 'code_length' field of
* the Code attribute.
- * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
- * -1 if they are not code attributes.
+ * @param maxStack the maximum stack size of the method corresponding to these Code attributes, or
+ * -1 if they are not Code attributes.
* @param maxLocals the maximum number of local variables of the method corresponding to these
- * code attributes, or -1 if they are not code attribute.
+ * Code attributes, or -1 if they are not Code attribute.
* @param output where the attributes must be written.
*/
final void putAttributes(
@@ -311,6 +347,37 @@ public class Attribute {
}
}
+ /**
+ * Puts all the attributes that correspond to the given field, method or class access flags and
+ * signature, in the given byte vector. This includes the 6 header bytes (attribute_name_index and
+ * attribute_length) per attribute.
+ *
+ * @param symbolTable where the constants used in the attributes must be stored.
+ * @param accessFlags some field, method or class access flags.
+ * @param signatureIndex the constant pool index of a field, method of class signature.
+ * @param output where the attributes must be written.
+ */
+ static void putAttributes(
+ final SymbolTable symbolTable,
+ final int accessFlags,
+ final int signatureIndex,
+ final ByteVector output) {
+ // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
+ if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
+ && symbolTable.getMajorVersion() < Opcodes.V1_5) {
+ output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
+ }
+ if (signatureIndex != 0) {
+ output
+ .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
+ .putInt(2)
+ .putShort(signatureIndex);
+ }
+ if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
+ output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
+ }
+ }
+
/** A set of attribute prototypes (attributes with the same type are considered equal). */
static final class Set {
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
index 08ed6ea2cbc..06fd8e17870 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
@@ -81,10 +81,11 @@ public class ClassReader {
public static final int SKIP_CODE = 1;
/**
- * 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).
+ * 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).
*/
public static final int SKIP_DEBUG = 2;
@@ -121,6 +122,16 @@ public class ClassReader {
/** The size of the temporary byte array used to read class input streams chunk by chunk. */
private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
+ /**
+ * A byte array containing the JVMS ClassFile structure to be parsed.
+ *
+ * @deprecated Use {@link #readByte(int)} and the other read methods instead. This field will
+ * eventually be deleted.
+ */
+ @Deprecated
+ // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
+ public final byte[] b;
+
/**
* A byte array containing the JVMS ClassFile structure to be parsed. The content of this array
* must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
@@ -130,13 +141,13 @@ public class ClassReader {
* necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
* ClassFile element offsets within this byte array.
*/
- // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
- public final byte[] b;
+ final byte[] classFileBuffer;
/**
- * The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool
- * array, 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 #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].
*/
private final int[] cpInfoOffsets;
@@ -153,8 +164,8 @@ public class ClassReader {
private final ConstantDynamic[] constantDynamicValues;
/**
- * The start offsets in {@link #b} of each element of the bootstrap_methods array (in the
- * BootstrapMethods attribute).
+ * The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
+ * (in the BootstrapMethods attribute).
*
* @see JVMS
* 4.7.23
@@ -167,7 +178,7 @@ public class ClassReader {
*/
private final int maxStringLength;
- /** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */
+ /** The offset in bytes of the ClassFile's access_flags field. */
public final int header;
// -----------------------------------------------------------------------------------------------
@@ -207,7 +218,8 @@ public class ClassReader {
*/
ClassReader(
final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
- b = classFileBuffer;
+ this.classFileBuffer = classFileBuffer;
+ this.b = classFileBuffer;
// Check the class' major_version. This field is after the magic and minor_version fields, which
// use 4 and 2 bytes respectively.
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V15) {
@@ -226,8 +238,8 @@ public class ClassReader {
int currentCpInfoIndex = 1;
int currentCpInfoOffset = classFileOffset + 10;
int currentMaxStringLength = 0;
+ boolean hasBootstrapMethods = false;
boolean hasConstantDynamic = false;
- boolean hasConstantInvokeDynamic = false;
// The offset of the other entries depend on the total size of all the previous entries.
while (currentCpInfoIndex < constantPoolCount) {
cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
@@ -243,11 +255,12 @@ public class ClassReader {
break;
case Symbol.CONSTANT_DYNAMIC_TAG:
cpInfoSize = 5;
+ hasBootstrapMethods = true;
hasConstantDynamic = true;
break;
case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
cpInfoSize = 5;
- hasConstantInvokeDynamic = true;
+ hasBootstrapMethods = true;
break;
case Symbol.CONSTANT_LONG_TAG:
case Symbol.CONSTANT_DOUBLE_TAG:
@@ -287,9 +300,7 @@ public class ClassReader {
// Read the BootstrapMethods attribute, if any (only get the offset of each method).
bootstrapMethodOffsets =
- (hasConstantDynamic | hasConstantInvokeDynamic)
- ? readBootstrapMethodsAttribute(currentMaxStringLength)
- : null;
+ hasBootstrapMethods ? readBootstrapMethodsAttribute(currentMaxStringLength) : null;
}
/**
@@ -330,8 +341,7 @@ public class ClassReader {
if (inputStream == null) {
throw new IOException("Class not found");
}
- try {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
@@ -436,6 +446,7 @@ public class ClassReader {
* @param parsingOptions the options to use to parse this class. One or more of {@link
* #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
*/
+ @SuppressWarnings("deprecation")
public void accept(
final ClassVisitor classVisitor,
final Attribute[] attributePrototypes,
@@ -488,6 +499,10 @@ public class ClassReader {
String nestHostClass = null;
// - The offset of the NestMembers attribute, or 0.
int nestMembersOffset = 0;
+ // - The offset of the PermittedSubtypes attribute, or 0
+ int permittedSubtypesOffset = 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;
@@ -510,6 +525,8 @@ public class ClassReader {
nestHostClass = readClass(currentAttributeOffset, charBuffer);
} else if (Constants.NEST_MEMBERS.equals(attributeName)) {
nestMembersOffset = currentAttributeOffset;
+ } else if (Constants.PERMITTED_SUBTYPES.equals(attributeName)) {
+ permittedSubtypesOffset = currentAttributeOffset;
} else if (Constants.SIGNATURE.equals(attributeName)) {
signature = readUTF8(currentAttributeOffset, charBuffer);
} else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
@@ -527,6 +544,9 @@ public class ClassReader {
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)) {
@@ -684,6 +704,17 @@ public class ClassReader {
}
}
+ // Visit the PermittedSubtypes attribute.
+ if (permittedSubtypesOffset != 0) {
+ int numberOfPermittedSubtypes = readUnsignedShort(permittedSubtypesOffset);
+ int currentPermittedSubtypeOffset = permittedSubtypesOffset + 2;
+ while (numberOfPermittedSubtypes-- > 0) {
+ classVisitor.visitPermittedSubtypeExperimental(
+ readClass(currentPermittedSubtypeOffset, charBuffer));
+ currentPermittedSubtypeOffset += 2;
+ }
+ }
+
// Visit the InnerClasses attribute.
if (innerClassesOffset != 0) {
int numberOfClasses = readUnsignedShort(innerClassesOffset);
@@ -698,6 +729,15 @@ public class ClassReader {
}
}
+ // 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;
@@ -727,7 +767,8 @@ public class ClassReader {
* attribute_name_index and attribute_length fields).
* @param modulePackagesOffset the offset of the ModulePackages attribute (excluding the
* attribute_info's attribute_name_index and attribute_length fields), or 0.
- * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
+ * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or {@literal
+ * null}.
*/
private void readModuleAttributes(
final ClassVisitor classVisitor,
@@ -844,6 +885,180 @@ public class ClassReader {
moduleVisitor.visitEnd();
}
+ /**
+ * 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.
*
@@ -1159,19 +1374,18 @@ public class ClassReader {
MethodWriter methodWriter = (MethodWriter) methodVisitor;
if (methodWriter.canCopyMethodAttributes(
this,
- methodInfoOffset,
- currentOffset - methodInfoOffset,
synthetic,
(context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0,
readUnsignedShort(methodInfoOffset + 4),
signatureIndex,
exceptionsOffset)) {
+ methodWriter.setMethodAttributesSource(methodInfoOffset, currentOffset - methodInfoOffset);
return currentOffset;
}
}
// Visit the MethodParameters attribute.
- if (methodParametersOffset != 0) {
+ if (methodParametersOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
int parametersCount = readByte(methodParametersOffset);
int currentParameterOffset = methodParametersOffset + 1;
while (parametersCount-- > 0) {
@@ -1320,15 +1534,15 @@ public class ClassReader {
*
* @param methodVisitor the visitor that must visit the Code attribute.
* @param context information about the class being parsed.
- * @param codeOffset the start offset in {@link #b} of the Code attribute, excluding its
- * attribute_name_index and attribute_length fields.
+ * @param codeOffset the start offset in {@link #classFileBuffer} of the Code attribute, excluding
+ * its attribute_name_index and attribute_length fields.
*/
private void readCode(
final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
int currentOffset = codeOffset;
// Read the max_stack, max_locals and code_length fields.
- final byte[] classFileBuffer = b;
+ final byte[] classBuffer = classFileBuffer;
final char[] charBuffer = context.charBuffer;
final int maxStack = readUnsignedShort(currentOffset);
final int maxLocals = readUnsignedShort(currentOffset + 2);
@@ -1341,115 +1555,115 @@ public class ClassReader {
final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
while (currentOffset < bytecodeEndOffset) {
final int bytecodeOffset = currentOffset - bytecodeStartOffset;
- final int opcode = classFileBuffer[currentOffset] & 0xFF;
+ final int opcode = classBuffer[currentOffset] & 0xFF;
switch (opcode) {
- case Constants.NOP:
- case Constants.ACONST_NULL:
- 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 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.ILOAD_0:
case Constants.ILOAD_1:
case Constants.ILOAD_2:
@@ -1492,24 +1706,24 @@ public class ClassReader {
case Constants.ASTORE_3:
currentOffset += 1;
break;
- 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:
+ 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:
createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
currentOffset += 3;
break;
@@ -1541,28 +1755,28 @@ public class ClassReader {
currentOffset += 5;
break;
case Constants.WIDE:
- 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:
+ 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:
currentOffset += 4;
break;
- case Constants.IINC:
+ case Opcodes.IINC:
currentOffset += 6;
break;
default:
throw new IllegalArgumentException();
}
break;
- case Constants.TABLESWITCH:
+ case Opcodes.TABLESWITCH:
// Skip 0 to 3 padding bytes.
currentOffset += 4 - (bytecodeOffset & 3);
// Read the default label and the number of table entries.
@@ -1575,7 +1789,7 @@ public class ClassReader {
currentOffset += 4;
}
break;
- case Constants.LOOKUPSWITCH:
+ case Opcodes.LOOKUPSWITCH:
// Skip 0 to 3 padding bytes.
currentOffset += 4 - (bytecodeOffset & 3);
// Read the default label and the number of switch cases.
@@ -1588,44 +1802,44 @@ public class ClassReader {
currentOffset += 8;
}
break;
- 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:
+ 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:
currentOffset += 2;
break;
- case Constants.SIPUSH:
+ case Opcodes.SIPUSH:
case Constants.LDC_W:
case Constants.LDC2_W:
- 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:
+ 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:
currentOffset += 3;
break;
- case Constants.INVOKEINTERFACE:
- case Constants.INVOKEDYNAMIC:
+ case Opcodes.INVOKEINTERFACE:
+ case Opcodes.INVOKEDYNAMIC:
currentOffset += 5;
break;
- case Constants.MULTIANEWARRAY:
+ case Opcodes.MULTIANEWARRAY:
currentOffset += 4;
break;
default:
@@ -1790,11 +2004,11 @@ public class ClassReader {
// creating a label for each NEW instruction, and faster than fully decoding the whole stack
// map table.
for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
- if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
+ if (classBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
int potentialBytecodeOffset = readUnsignedShort(offset + 1);
if (potentialBytecodeOffset >= 0
&& potentialBytecodeOffset < codeLength
- && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
+ && (classBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
== Opcodes.NEW) {
createLabel(potentialBytecodeOffset, labels);
}
@@ -1890,115 +2104,115 @@ public class ClassReader {
}
// Visit the instruction at this bytecode offset.
- int opcode = classFileBuffer[currentOffset] & 0xFF;
+ int opcode = classBuffer[currentOffset] & 0xFF;
switch (opcode) {
- case Constants.NOP:
- case Constants.ACONST_NULL:
- 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 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:
methodVisitor.visitInsn(opcode);
currentOffset += 1;
break;
@@ -2050,24 +2264,24 @@ public class ClassReader {
methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
currentOffset += 1;
break;
- 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:
+ 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:
methodVisitor.visitJumpInsn(
opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
currentOffset += 3;
@@ -2128,19 +2342,17 @@ public class ClassReader {
break;
}
case Constants.ASM_GOTO_W:
- {
- // Replace ASM_GOTO_W with GOTO_W.
- methodVisitor.visitJumpInsn(
- Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
- // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
- // IFNOTxxx
* ===================================== - * |.DIM|KIND|FLAG|...............VALUE| + * |...DIM|KIND|.F|...............VALUE| * ===================================== ** *
Output frames can contain abstract types of any kind and with a positive or negative array * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or - * UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type - * table contains only internal type names (array type descriptors are forbidden - array dimensions - * must be represented through the DIM field). + * UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases + * the type table contains only internal type names (array type descriptors are forbidden - array + * dimensions must be represented through the DIM field). * *
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 @@ -160,18 +160,25 @@ class Frame { private static final int ITEM_ASM_CHAR = 11; private static final int ITEM_ASM_SHORT = 12; + // The size and offset in bits of each field of an abstract type. + + private static final int DIM_SIZE = 6; + private static final int KIND_SIZE = 4; + private static final int FLAGS_SIZE = 2; + private static final int VALUE_SIZE = 32 - DIM_SIZE - KIND_SIZE - FLAGS_SIZE; + + private static final int DIM_SHIFT = KIND_SIZE + FLAGS_SIZE + VALUE_SIZE; + private static final int KIND_SHIFT = FLAGS_SIZE + VALUE_SIZE; + private static final int FLAGS_SHIFT = VALUE_SIZE; + // Bitmasks to get each field of an abstract type. - private static final int DIM_MASK = 0xF0000000; - private static final int KIND_MASK = 0x0F000000; - private static final int FLAGS_MASK = 0x00F00000; - private static final int VALUE_MASK = 0x000FFFFF; + private static final int DIM_MASK = ((1 << DIM_SIZE) - 1) << DIM_SHIFT; + private static final int KIND_MASK = ((1 << KIND_SIZE) - 1) << KIND_SHIFT; + private static final int VALUE_MASK = (1 << VALUE_SIZE) - 1; // Constants to manipulate the DIM field of an abstract type. - /** The number of right shift bits to use to get the array dimensions of an abstract type. */ - private static final int DIM_SHIFT = 28; - /** The constant to be added to an abstract type to get one with one more array dimension. */ private static final int ARRAY_OF = +1 << DIM_SHIFT; @@ -180,11 +187,11 @@ class Frame { // Possible values for the KIND field of an abstract type. - private static final int CONSTANT_KIND = 0x01000000; - private static final int REFERENCE_KIND = 0x02000000; - private static final int UNINITIALIZED_KIND = 0x03000000; - private static final int LOCAL_KIND = 0x04000000; - private static final int STACK_KIND = 0x05000000; + private static final int CONSTANT_KIND = 1 << KIND_SHIFT; + private static final int REFERENCE_KIND = 2 << KIND_SHIFT; + private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT; + private static final int LOCAL_KIND = 4 << KIND_SHIFT; + private static final int STACK_KIND = 5 << KIND_SHIFT; // Possible flags for the FLAGS field of an abstract type. @@ -193,7 +200,7 @@ class Frame { * concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been * partially overridden with an xSTORE instruction). */ - private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK; + private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 1 << FLAGS_SHIFT; // Useful predefined abstract types (all the possible CONSTANT_KIND types). @@ -571,7 +578,8 @@ class Frame { * @param descriptor a type or method descriptor (in which case its return type is pushed). */ private void push(final SymbolTable symbolTable, final String descriptor) { - int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0; + int typeDescriptorOffset = + descriptor.charAt(0) == '(' ? Type.getReturnTypeOffset(descriptor) : 0; int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset); if (abstractType != 0) { push(abstractType); @@ -1134,6 +1142,42 @@ class Frame { // Frame merging methods, used in the second step of the stack map frame computation algorithm // ----------------------------------------------------------------------------------------------- + /** + * Computes the concrete output type corresponding to a given abstract output type. + * + * @param abstractOutputType an abstract output type. + * @param numStack the size of the input stack, used to resolve abstract output types of + * STACK_KIND kind. + * @return the concrete output type corresponding to 'abstractOutputType'. + */ + private int getConcreteOutputType(final int abstractOutputType, final int numStack) { + int dim = abstractOutputType & DIM_MASK; + int kind = abstractOutputType & KIND_MASK; + if (kind == LOCAL_KIND) { + // By definition, a LOCAL_KIND type designates the concrete type of a local variable at + // the beginning of the basic block corresponding to this frame (which is known when + // this method is called, but was not when the abstract type was computed). + int concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + return concreteOutputType; + } else if (kind == STACK_KIND) { + // By definition, a STACK_KIND type designates the concrete type of a local variable at + // the beginning of the basic block corresponding to this frame (which is known when + // this method is called, but was not when the abstract type was computed). + int concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + return concreteOutputType; + } else { + return abstractOutputType; + } + } + /** * Merges the input frame of the given {@link Frame} with the input and output frames of this * {@link Frame}. Returns {@literal true} if the given frame has been changed by this operation @@ -1168,29 +1212,7 @@ class Frame { // value at the beginning of the block. concreteOutputType = inputLocals[i]; } else { - int dim = abstractOutputType & DIM_MASK; - int kind = abstractOutputType & KIND_MASK; - if (kind == LOCAL_KIND) { - // By definition, a LOCAL_KIND type designates the concrete type of a local variable at - // the beginning of the basic block corresponding to this frame (which is known when - // this method is called, but was not when the abstract type was computed). - concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else if (kind == STACK_KIND) { - // By definition, a STACK_KIND type designates the concrete type of a local variable at - // the beginning of the basic block corresponding to this frame (which is known when - // this method is called, but was not when the abstract type was computed). - concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else { - concreteOutputType = abstractOutputType; - } + concreteOutputType = getConcreteOutputType(abstractOutputType, numStack); } } else { // If the local variable has never been assigned in this basic block, it is equal to its @@ -1244,25 +1266,8 @@ class Frame { // Then, do this for the stack operands that have pushed in the basic block (this code is the // same as the one above for local variables). for (int i = 0; i < outputStackTop; ++i) { - int concreteOutputType; int abstractOutputType = outputStack[i]; - int dim = abstractOutputType & DIM_MASK; - int kind = abstractOutputType & KIND_MASK; - if (kind == LOCAL_KIND) { - concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else if (kind == STACK_KIND) { - concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else { - concreteOutputType = abstractOutputType; - } + int concreteOutputType = getConcreteOutputType(abstractOutputType, numStack); if (initializations != null) { concreteOutputType = getInitializedType(symbolTable, concreteOutputType); } @@ -1279,10 +1284,10 @@ class Frame { * @param symbolTable the type table to use to lookup and store type {@link Symbol}. * @param sourceType the abstract type with which the abstract type array element must be merged. * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link - * #UNINITIALIZED_KIND} kind, with positive or null array dimensions. + * #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions. * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND}, - * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array - * dimensions. + * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal + * null} array dimensions. * @param dstIndex the index of the type that must be merged in dstTypes. * @return {@literal true} if the type array has been modified by this operation. */ diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java index e49462f6cfd..4f1cedaad23 100644 --- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java +++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java @@ -258,7 +258,8 @@ public class Label { /** * The maximum height reached by the output stack, relatively to the top of the input stack, in - * the basic block corresponding to this label. This maximum is always positive or null. + * the basic block corresponding to this label. This maximum is always positive or {@literal + * null}. */ short outputStackMax; @@ -295,12 +296,12 @@ public class Label { Edge outgoingEdges; /** - * The next element in the list of labels to which this label belongs, or null if it does not - * belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in - * order to ensure that this field is null if and only if this label does not belong to a list of - * labels. Note that there can be several lists of labels at the same time, but that a label can - * belong to at most one list at a time (unless some lists share a common tail, but this is not - * used in practice). + * The next element in the list of labels to which this label belongs, or {@literal null} if it + * does not belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} + * sentinel, in order to ensure that this field is null if and only if this label does not belong + * to a list of labels. Note that there can be several lists of labels at the same time, but that + * a label can belong to at most one list at a time (unless some lists share a common tail, but + * this is not used in practice). * *
List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
* MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java
index ccb542658d2..6bc37b1ec7a 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java
@@ -87,7 +87,9 @@ public abstract class MethodVisitor {
*/
protected final int api;
- /** The method visitor to which this visitor must delegate method calls. May be null. */
+ /**
+ * The method visitor to which this visitor must delegate method calls. May be {@literal null}.
+ */
protected MethodVisitor mv;
/**
@@ -108,9 +110,18 @@ public abstract class MethodVisitor {
* @param methodVisitor the method visitor to which this visitor must delegate method calls. May
* be null.
*/
+ @SuppressWarnings("deprecation")
public MethodVisitor(final int api, final MethodVisitor methodVisitor) {
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
- throw new IllegalArgumentException();
+ if (api != Opcodes.ASM8
+ && api != Opcodes.ASM7
+ && api != Opcodes.ASM6
+ && api != Opcodes.ASM5
+ && api != Opcodes.ASM4
+ && api != Opcodes.ASM9_EXPERIMENTAL) {
+ throw new IllegalArgumentException("Unsupported api " + api);
+ }
+ if (api == Opcodes.ASM9_EXPERIMENTAL) {
+ Constants.checkAsmExperimental(this);
}
this.api = api;
this.mv = methodVisitor;
@@ -123,7 +134,7 @@ public abstract class MethodVisitor {
/**
* Visits a parameter of this method.
*
- * @param name parameter name or null if none is provided.
+ * @param name parameter name or {@literal null} if none is provided.
* @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC}
* or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}).
*/
@@ -426,14 +437,8 @@ public abstract class MethodVisitor {
@Deprecated
public void visitMethodInsn(
final int opcode, final String owner, final String name, final String descriptor) {
- if (api >= Opcodes.ASM5) {
- boolean isInterface = opcode == Opcodes.INVOKEINTERFACE;
- visitMethodInsn(opcode, owner, name, descriptor, isInterface);
- return;
- }
- if (mv != null) {
- mv.visitMethodInsn(opcode, owner, name, descriptor);
- }
+ int opcodeAndSource = opcode | (api < Opcodes.ASM5 ? Opcodes.SOURCE_DEPRECATED : 0);
+ visitMethodInsn(opcodeAndSource, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE);
}
/**
@@ -453,15 +458,15 @@ public abstract class MethodVisitor {
final String name,
final String descriptor,
final boolean isInterface) {
- if (api < Opcodes.ASM5) {
+ if (api < Opcodes.ASM5 && (opcode & Opcodes.SOURCE_DEPRECATED) == 0) {
if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
- throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
+ throw new UnsupportedOperationException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
}
visitMethodInsn(opcode, owner, name, descriptor);
return;
}
if (mv != null) {
- mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ mv.visitMethodInsn(opcode & ~Opcodes.SOURCE_MASK, owner, name, descriptor, isInterface);
}
}
@@ -569,7 +574,7 @@ public abstract class MethodVisitor {
|| (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 --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java
index f66634e99ab..648d523ba95 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java
@@ -623,7 +623,7 @@ final class MethodWriter extends MethodVisitor {
final String signature,
final String[] exceptions,
final int compute) {
- super(Opcodes.ASM7);
+ super(/* latest api = */ Opcodes.ASM8);
this.symbolTable = symbolTable;
this.accessFlags = " If 'api' is equal to API_NEW, there are two cases:
+ *
+ * If 'api' is equal to API_OLD, there are two cases:
+ *
+ * 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
+ *
+ * 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
+ *
+ * and there are two cases:
+ *
+ * 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):
+ *
+ * If a user class extends this with 'api' equal to API_NEW, the class hierarchy is equivalent
+ * to
+ *
+ * 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
+ *
+ * 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).
+ *
+ * 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.ASM8);
+ 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 --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java
index 3b4d0a5c21a..d4af66c2101 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java
@@ -62,11 +62,11 @@ package jdk.internal.org.objectweb.asm;
* 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 {
@@ -170,7 +170,7 @@ final class SymbolTable {
this.sourceClassReader = classReader;
// Copy the constant pool binary content.
- byte[] inputBytes = classReader.b;
+ byte[] inputBytes = classReader.classFileBuffer;
int constantPoolOffset = classReader.getItem(1) - 1;
int constantPoolLength = classReader.header - constantPoolOffset;
constantPoolCount = classReader.getItemCount();
@@ -273,7 +273,7 @@ final class SymbolTable {
*/
private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) {
// Find attributOffset of the 'bootstrap_methods' array.
- byte[] inputBytes = classReader.b;
+ byte[] inputBytes = classReader.classFileBuffer;
int currentAttributeOffset = classReader.getFirstAttributeOffset();
for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer);
@@ -1077,8 +1077,10 @@ final class SymbolTable {
// 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.
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
- addConstant(bootstrapMethodArgument);
+ int numBootstrapArguments = bootstrapMethodArguments.length;
+ int[] bootstrapMethodArgumentIndexes = new int[numBootstrapArguments];
+ for (int i = 0; i < numBootstrapArguments; i++) {
+ bootstrapMethodArgumentIndexes[i] = addConstant(bootstrapMethodArguments[i]).index;
}
// Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to
@@ -1093,10 +1095,10 @@ final class SymbolTable {
bootstrapMethodHandle.getDesc(),
bootstrapMethodHandle.isInterface())
.index);
- int numBootstrapArguments = bootstrapMethodArguments.length;
+
bootstrapMethodsAttribute.putShort(numBootstrapArguments);
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
- bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index);
+ for (int i = 0; i < numBootstrapArguments; i++) {
+ bootstrapMethodsAttribute.putShort(bootstrapMethodArgumentIndexes[i]);
}
// Compute the length and the hash code of the bootstrap method.
@@ -1214,8 +1216,10 @@ final class SymbolTable {
* corresponding to the common super class of the given types.
*/
int addMergedType(final int typeTableIndex1, final int typeTableIndex2) {
- // TODO sort the arguments? The merge result should be independent of their order.
- long data = typeTableIndex1 | (((long) typeTableIndex2) << 32);
+ long data =
+ typeTableIndex1 < typeTableIndex2
+ ? typeTableIndex1 | (((long) typeTableIndex2) << 32)
+ : typeTableIndex2 | (((long) typeTableIndex1) << 32);
int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2);
Entry entry = get(hashCode);
while (entry != null) {
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java
index d5c2befb0e7..0302df589e6 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java
@@ -336,7 +336,8 @@ public final class Type {
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
+ int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+ currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
}
++numArgumentTypes;
}
@@ -354,7 +355,8 @@ public final class Type {
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
+ int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+ currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
}
argumentTypes[currentArgumentTypeIndex++] =
getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
@@ -394,19 +396,8 @@ public final class Type {
* @return the {@link Type} corresponding to the return type of the given method descriptor.
*/
public static Type getReturnType(final String methodDescriptor) {
- // Skip the first character, which is always a '('.
- int currentOffset = 1;
- // Skip the argument types, one at a each loop iteration.
- while (methodDescriptor.charAt(currentOffset) != ')') {
- while (methodDescriptor.charAt(currentOffset) == '[') {
- currentOffset++;
- }
- if (methodDescriptor.charAt(currentOffset++) == 'L') {
- // Skip the argument descriptor content.
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
- }
- }
- return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length());
+ return getTypeInternal(
+ methodDescriptor, getReturnTypeOffset(methodDescriptor), methodDescriptor.length());
}
/**
@@ -419,6 +410,29 @@ public final class Type {
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.
+ while (methodDescriptor.charAt(currentOffset) != ')') {
+ while (methodDescriptor.charAt(currentOffset) == '[') {
+ currentOffset++;
+ }
+ if (methodDescriptor.charAt(currentOffset++) == 'L') {
+ // Skip the argument descriptor content.
+ int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+ currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+ }
+ }
+ return currentOffset + 1;
+ }
+
/**
* Returns the {@link Type} corresponding to the given field or method descriptor.
*
@@ -536,11 +550,7 @@ public final class Type {
if (sort == OBJECT) {
return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
} else if (sort == INTERNAL) {
- return new StringBuilder()
- .append('L')
- .append(valueBuffer, valueBegin, valueEnd)
- .append(';')
- .toString();
+ return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
} else {
return valueBuffer.substring(valueBegin, valueEnd);
}
@@ -662,14 +672,7 @@ public final class Type {
}
stringBuilder.append(descriptor);
} else {
- stringBuilder.append('L');
- String name = currentClass.getName();
- int nameLength = name.length();
- for (int i = 0; i < nameLength; ++i) {
- char car = name.charAt(i);
- stringBuilder.append(car == '.' ? '/' : car);
- }
- stringBuilder.append(';');
+ stringBuilder.append('L').append(getInternalName(currentClass)).append(';');
}
}
@@ -768,7 +771,8 @@ public final class Type {
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
+ int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+ currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
}
argumentsSize += 1;
}
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java
index 964d84a9d36..ae588db91a5 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java
@@ -147,7 +147,7 @@ public final class TypePath {
* @return the corresponding TypePath object, or {@literal null} if the path is empty.
*/
public static TypePath fromString(final String typePath) {
- if (typePath == null || typePath.isEmpty()) {
+ if (typePath == null || typePath.length() == 0) {
return null;
}
int typePathLength = typePath.length();
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java
index 5e7639b22df..b34d5cc4bbb 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java
@@ -152,8 +152,8 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
public void visitCode() {
super.visitCode();
if (isConstructor) {
- stackFrame = new ArrayList
+ * 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 ]
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * User subclasses
+ *
+ *
+ * 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 ]
+ * }
+ * }
+ *
+ *
+ *
+ * public class StuffVisitor {
+ * @Deprecated public void visitOldStuff(int arg, ...) {
+ * visitNewStuf(arg | SOURCE_DEPRECATED, ...);
+ * }
+ * public void visitNewStuff(int argAndSource...) {
+ * if ((argAndSource & SOURCE_DEPRECATED) == 0) {
+ * visitOldStuff(argAndSource, ...);
+ * } else {
+ * int arg = argAndSource & ~SOURCE_MASK;
+ * [ do stuff ]
+ * }
+ * }
+ * }
+ * class UserStuffVisitor extends StuffVisitor {
+ * @Override public void visitOldStuff(int arg, ...) {
+ * super.visitOldStuff(int arg, ...); // optional
+ * [ do user stuff ]
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * ASM subclasses
+ *
+ *
+ * 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 ]
+ * }
+ * }
+ *
+ *
+ *
+ * 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 ]
+ * }
+ * }
+ *
+ *
+ *
+ * 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 ]
+ * }
+ * }
+ *
+ *
+ * Notes
+ *
+ *
+ *
+ */
+
+ int SOURCE_DEPRECATED = 0x100;
+ int SOURCE_MASK = SOURCE_DEPRECATED;
+
+ // Java ClassFile versions (the minor version is stored in the 16 most significant bits, and the
// major version in the 16 least significant bits).
int V1_1 = 3 << 16 | 45;
@@ -134,7 +347,7 @@ public interface Opcodes {
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; // parameter, module, module *
+ int ACC_MANDATED = 0x8000; // field, method, parameter, module, module *
int ACC_MODULE = 0x8000; // class
// ASM specific access flags.
@@ -142,6 +355,7 @@ public interface Opcodes {
// 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 --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentVisitor.java
new file mode 100644
index 00000000000..70396a6cfb9
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentVisitor.java
@@ -0,0 +1,182 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 jdk.internal.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 {@link
+ * Opcodes#ASM8}.
+ */
+ 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 {@link Opcodes#ASM8}.
+ */
+ 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.
+ */
+ @SuppressWarnings("deprecation")
+ public RecordComponentVisitor(
+ final int api, final RecordComponentVisitor recordComponentVisitor) {
+ if (api != Opcodes.ASM8
+ && api != Opcodes.ASM7
+ && api != Opcodes.ASM6
+ && api != Opcodes.ASM5
+ && api != Opcodes.ASM4
+ && api != Opcodes.ASM9_EXPERIMENTAL) {
+ throw new IllegalArgumentException("Unsupported api " + api);
+ }
+ if (api == Opcodes.ASM9_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 --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentWriter.java
new file mode 100644
index 00000000000..5ec88b4000a
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentWriter.java
@@ -0,0 +1,256 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 jdk.internal.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}.
+ *
+ *