");
diff --git a/src/main/java/jd/cli/printer/text/PlainTextPrinter.java b/src/main/java/jd/cli/printer/text/PlainTextPrinter.java
index a2c066b9..070c6221 100644
--- a/src/main/java/jd/cli/printer/text/PlainTextPrinter.java
+++ b/src/main/java/jd/cli/printer/text/PlainTextPrinter.java
@@ -1,7 +1,6 @@
package jd.cli.printer.text;
import java.io.PrintStream;
-
import jd.cli.preferences.CommonPreferences;
import jd.core.model.instruction.bytecode.instruction.Instruction;
import jd.core.printer.Printer;
@@ -340,7 +339,7 @@ public class PlainTextPrinter implements Printer {
char c = s.charAt(i);
if (c == '\t') {
- this.printStream.append(c);
+ this.printStream.append('\t');
} else if (c < 32) {
// Write octal format
this.printStream.append("\\0");
diff --git a/src/main/java/jd/cli/util/ClassFileUtil.java b/src/main/java/jd/cli/util/ClassFileUtil.java
index a954b652..77b8f83d 100644
--- a/src/main/java/jd/cli/util/ClassFileUtil.java
+++ b/src/main/java/jd/cli/util/ClassFileUtil.java
@@ -6,7 +6,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-
import jd.core.CoreConstants;
import jd.core.model.classfile.constant.Constant;
import jd.core.model.classfile.constant.ConstantClass;
@@ -104,34 +103,34 @@ public class ClassFileUtil {
byte tag = dis.readByte();
switch (tag) {
- case ConstantConstant.CONSTANT_Class:
- constants[i] = new ConstantClass(tag, dis.readUnsignedShort());
- break;
- case ConstantConstant.CONSTANT_Utf8:
- constants[i] = new ConstantUtf8(tag, dis.readUTF());
- break;
- case ConstantConstant.CONSTANT_Long:
- case ConstantConstant.CONSTANT_Double:
- dis.read();
- dis.read();
- dis.read();
- dis.read();
- i++;
- case ConstantConstant.CONSTANT_Fieldref:
- case ConstantConstant.CONSTANT_Methodref:
- case ConstantConstant.CONSTANT_InterfaceMethodref:
- case ConstantConstant.CONSTANT_NameAndType:
- case ConstantConstant.CONSTANT_Integer:
- case ConstantConstant.CONSTANT_Float:
- dis.read();
- dis.read();
- case ConstantConstant.CONSTANT_String:
- dis.read();
- dis.read();
- break;
- default:
- //throw new ClassFormatException("Invalid constant pool entry");
- return constants;
+ case ConstantConstant.CONSTANT_Class:
+ constants[i] = new ConstantClass(tag, dis.readUnsignedShort());
+ break;
+ case ConstantConstant.CONSTANT_Utf8:
+ constants[i] = new ConstantUtf8(tag, dis.readUTF());
+ break;
+ case ConstantConstant.CONSTANT_Long:
+ case ConstantConstant.CONSTANT_Double:
+ dis.read();
+ dis.read();
+ dis.read();
+ dis.read();
+ i++;
+ case ConstantConstant.CONSTANT_Fieldref:
+ case ConstantConstant.CONSTANT_Methodref:
+ case ConstantConstant.CONSTANT_InterfaceMethodref:
+ case ConstantConstant.CONSTANT_NameAndType:
+ case ConstantConstant.CONSTANT_Integer:
+ case ConstantConstant.CONSTANT_Float:
+ dis.read();
+ dis.read();
+ case ConstantConstant.CONSTANT_String:
+ dis.read();
+ dis.read();
+ break;
+ default:
+ //throw new ClassFormatException("Invalid constant pool entry");
+ return constants;
}
}
diff --git a/src/main/java/me/konloch/kontainer/io/DiskReader.java b/src/main/java/me/konloch/kontainer/io/DiskReader.java
index 5581f5e1..27786c96 100644
--- a/src/main/java/me/konloch/kontainer/io/DiskReader.java
+++ b/src/main/java/me/konloch/kontainer/io/DiskReader.java
@@ -1,13 +1,12 @@
package me.konloch.kontainer.io;
-import the.bytecode.club.bytecodeviewer.util.EncodeUtils;
-
import java.io.BufferedReader;
-import java.io.FileReader;
import java.io.File;
+import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
+import the.bytecode.club.bytecodeviewer.util.EncodeUtils;
/**
* Used to load from the disk, optional caching
diff --git a/src/main/java/me/konloch/kontainer/io/DiskWriter.java b/src/main/java/me/konloch/kontainer/io/DiskWriter.java
index 0248fec5..c3717361 100644
--- a/src/main/java/me/konloch/kontainer/io/DiskWriter.java
+++ b/src/main/java/me/konloch/kontainer/io/DiskWriter.java
@@ -19,7 +19,7 @@ public class DiskWriter {
* @param fileName The file name
* @param difference Normally an integer
* @return The filename with the difference inserted and the file extension
- * preserved
+ * preserved
*/
public static String insertFileName(String fileName, String difference) {
String[] babe = fileName.split("\\.");
diff --git a/src/main/java/org/objectweb/asm/ClassReader.java b/src/main/java/org/objectweb/asm/ClassReader.java
index a8dc6672..c407c317 100644
--- a/src/main/java/org/objectweb/asm/ClassReader.java
+++ b/src/main/java/org/objectweb/asm/ClassReader.java
@@ -37,3597 +37,3606 @@ import java.io.InputStream;
* appropriate visit methods of a given {@link ClassVisitor} for each field, method and bytecode
* instruction encountered.
*
- * @see JVMS 4
* @author Eric Bruneton
* @author Eugene Kuleshov
+ * @see JVMS 4
*/
public class ClassReader {
- /**
- * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed
- * nor visited.
- */
- public static final int SKIP_CODE = 1;
+ /**
+ * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed
+ * nor visited.
+ */
+ 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).
- */
- public static final int SKIP_DEBUG = 2;
+ /**
+ * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable
+ * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor
+ * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and
+ * {@link MethodVisitor#visitLineNumber} are not called).
+ */
+ public static final int SKIP_DEBUG = 2;
- /**
- * A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes
- * are neither parsed nor visited (i.e. {@link MethodVisitor#visitFrame} is not called). This flag
- * is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is used: it avoids visiting frames
- * that will be ignored and recomputed from scratch.
- */
- public static final int SKIP_FRAMES = 4;
+ /**
+ * A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes
+ * are neither parsed nor visited (i.e. {@link MethodVisitor#visitFrame} is not called). This flag
+ * is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is used: it avoids visiting frames
+ * that will be ignored and recomputed from scratch.
+ */
+ public static final int SKIP_FRAMES = 4;
- /**
- * A flag to expand the stack map frames. By default stack map frames are visited in their
- * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed"
- * for the other classes). If this flag is set, stack map frames are always visited in expanded
- * format (this option adds a decompression/compression step in ClassReader and ClassWriter which
- * degrades performance quite a lot).
- */
- public static final int EXPAND_FRAMES = 8;
+ /**
+ * A flag to expand the stack map frames. By default stack map frames are visited in their
+ * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed"
+ * for the other classes). If this flag is set, stack map frames are always visited in expanded
+ * format (this option adds a decompression/compression step in ClassReader and ClassWriter which
+ * degrades performance quite a lot).
+ */
+ public static final int EXPAND_FRAMES = 8;
- /**
- * A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode
- * instructions. When resolving a forward jump it may happen that the signed 2 bytes offset
- * reserved for it is not sufficient to store the bytecode offset. In this case the jump
- * instruction is replaced with a temporary ASM specific instruction using an unsigned 2 bytes
- * offset (see {@link Label#resolve}). This internal flag is used to re-read classes containing
- * such instructions, in order to replace them with standard instructions. In addition, when this
- * flag is used, goto_w and jsr_w are not converted into goto and jsr, to make sure that
- * infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a
- * goto_w in ClassWriter cannot occur.
- */
- static final int EXPAND_ASM_INSNS = 256;
+ /**
+ * A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode
+ * instructions. When resolving a forward jump it may happen that the signed 2 bytes offset
+ * reserved for it is not sufficient to store the bytecode offset. In this case the jump
+ * instruction is replaced with a temporary ASM specific instruction using an unsigned 2 bytes
+ * offset (see {@link Label#resolve}). This internal flag is used to re-read classes containing
+ * such instructions, in order to replace them with standard instructions. In addition, when this
+ * flag is used, goto_w and jsr_w are not converted into goto and jsr, to make sure that
+ * infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a
+ * goto_w in ClassWriter cannot occur.
+ */
+ static final int EXPAND_ASM_INSNS = 256;
- /** 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;
+ /**
+ * 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.
+ *
+ * @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
- * not needed by class visitors.
- *
- *
NOTE: the ClassFile structure can start at any offset within this array, i.e. it does not
- * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
- * ClassFile element offsets within this byte array.
- */
- final byte[] classFileBuffer;
+ /**
+ * A byte array containing the JVMS ClassFile structure to be parsed. The content of this array
+ * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
+ * not needed by class visitors.
+ *
+ *
NOTE: the ClassFile structure can start at any offset within this array, i.e. it does not
+ * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
+ * ClassFile element offsets within this byte array.
+ */
+ final byte[] classFileBuffer;
- /**
- * 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;
+ /**
+ * 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;
- /**
- * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids
- * multiple parsing of a given CONSTANT_Utf8 constant pool item.
- */
- private final String[] constantUtf8Values;
+ /**
+ * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids
+ * multiple parsing of a given CONSTANT_Utf8 constant pool item.
+ */
+ private final String[] constantUtf8Values;
- /**
- * The ConstantDynamic objects corresponding to the CONSTANT_Dynamic constant pool items. This
- * cache avoids multiple parsing of a given CONSTANT_Dynamic constant pool item.
- */
- private final ConstantDynamic[] constantDynamicValues;
+ /**
+ * The ConstantDynamic objects corresponding to the CONSTANT_Dynamic constant pool items. This
+ * cache avoids multiple parsing of a given CONSTANT_Dynamic constant pool item.
+ */
+ private final ConstantDynamic[] constantDynamicValues;
- /**
- * The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
- * (in the BootstrapMethods attribute).
- *
- * @see JVMS
- * 4.7.23
- */
- private final int[] bootstrapMethodOffsets;
+ /**
+ * The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
+ * (in the BootstrapMethods attribute).
+ *
+ * @see JVMS
+ * 4.7.23
+ */
+ private final int[] bootstrapMethodOffsets;
- /**
- * A conservative estimate of the maximum length of the strings contained in the constant pool of
- * the class.
- */
- private final int maxStringLength;
+ /**
+ * A conservative estimate of the maximum length of the strings contained in the constant pool of
+ * the class.
+ */
+ private final int maxStringLength;
- /** The offset in bytes of the ClassFile's access_flags field. */
- public final int header;
+ /**
+ * The offset in bytes of the ClassFile's access_flags field.
+ */
+ public final int header;
- // -----------------------------------------------------------------------------------------------
- // Constructors
- // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // Constructors
+ // -----------------------------------------------------------------------------------------------
- /**
- * Constructs a new {@link ClassReader} object.
- *
- * @param classFile the JVMS ClassFile structure to be read.
- */
- public ClassReader(final byte[] classFile) {
- this(classFile, 0, classFile.length);
- }
-
- /**
- * Constructs a new {@link ClassReader} object.
- *
- * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
- * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
- * @param classFileLength the length in bytes of the ClassFile to be read.
- */
- public ClassReader(
- final byte[] classFileBuffer,
- final int classFileOffset,
- final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility.
- this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true);
- }
-
- /**
- * Constructs a new {@link ClassReader} object. This internal constructor must not be exposed
- * as a public API.
- *
- * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
- * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
- * @param checkClassVersion whether to check the class version or not.
- */
- ClassReader(
- final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
- 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.V17) {
- throw new IllegalArgumentException(
- "Unsupported class file major version " + readShort(classFileOffset + 6));
- }
- // Create the constant pool arrays. The constant_pool_count field is after the magic,
- // minor_version and major_version fields, which use 4, 2 and 2 bytes respectively.
- int constantPoolCount = readUnsignedShort(classFileOffset + 8);
- cpInfoOffsets = new int[constantPoolCount];
- constantUtf8Values = new String[constantPoolCount];
- // Compute the offset of each constant pool entry, as well as a conservative estimate of the
- // maximum length of the constant pool strings. The first constant pool entry is after the
- // magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2
- // bytes respectively.
- int currentCpInfoIndex = 1;
- int currentCpInfoOffset = classFileOffset + 10;
- int currentMaxStringLength = 0;
- boolean hasBootstrapMethods = false;
- boolean hasConstantDynamic = false;
- // The offset of the other entries depend on the total size of all the previous entries.
- while (currentCpInfoIndex < constantPoolCount) {
- cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
- int cpInfoSize;
- switch (classFileBuffer[currentCpInfoOffset]) {
- case Symbol.CONSTANT_FIELDREF_TAG:
- case Symbol.CONSTANT_METHODREF_TAG:
- case Symbol.CONSTANT_INTERFACE_METHODREF_TAG:
- case Symbol.CONSTANT_INTEGER_TAG:
- case Symbol.CONSTANT_FLOAT_TAG:
- case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
- cpInfoSize = 5;
- break;
- case Symbol.CONSTANT_DYNAMIC_TAG:
- cpInfoSize = 5;
- hasBootstrapMethods = true;
- hasConstantDynamic = true;
- break;
- case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
- cpInfoSize = 5;
- hasBootstrapMethods = true;
- break;
- case Symbol.CONSTANT_LONG_TAG:
- case Symbol.CONSTANT_DOUBLE_TAG:
- cpInfoSize = 9;
- currentCpInfoIndex++;
- break;
- case Symbol.CONSTANT_UTF8_TAG:
- cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1);
- if (cpInfoSize > currentMaxStringLength) {
- // The size in bytes of this CONSTANT_Utf8 structure provides a conservative estimate
- // of the length in characters of the corresponding string, and is much cheaper to
- // compute than this exact length.
- currentMaxStringLength = cpInfoSize;
- }
- break;
- case Symbol.CONSTANT_METHOD_HANDLE_TAG:
- cpInfoSize = 4;
- break;
- case Symbol.CONSTANT_CLASS_TAG:
- case Symbol.CONSTANT_STRING_TAG:
- case Symbol.CONSTANT_METHOD_TYPE_TAG:
- case Symbol.CONSTANT_PACKAGE_TAG:
- case Symbol.CONSTANT_MODULE_TAG:
- cpInfoSize = 3;
- break;
- default:
- throw new IllegalArgumentException();
- }
- currentCpInfoOffset += cpInfoSize;
- }
- maxStringLength = currentMaxStringLength;
- // The Classfile's access_flags field is just after the last constant pool entry.
- header = currentCpInfoOffset;
-
- // Allocate the cache of ConstantDynamic values, if there is at least one.
- constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null;
-
- // Read the BootstrapMethods attribute, if any (only get the offset of each method).
- bootstrapMethodOffsets =
- hasBootstrapMethods ? readBootstrapMethodsAttribute(currentMaxStringLength) : null;
- }
-
- /**
- * Constructs a new {@link ClassReader} object.
- *
- * @param inputStream an input stream of the JVMS ClassFile structure to be read. This input
- * stream must contain nothing more than the ClassFile structure itself. It is read from its
- * current position to its end.
- * @throws IOException if a problem occurs during reading.
- */
- public ClassReader(final InputStream inputStream) throws IOException {
- this(readStream(inputStream, false));
- }
-
- /**
- * Constructs a new {@link ClassReader} object.
- *
- * @param className the fully qualified name of the class to be read. The ClassFile structure is
- * retrieved with the current class loader's {@link ClassLoader#getSystemResourceAsStream}.
- * @throws IOException if an exception occurs during reading.
- */
- public ClassReader(final String className) throws IOException {
- this(
- readStream(
- ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true));
- }
-
- /**
- * Reads the given input stream and returns its content as a byte array.
- *
- * @param inputStream an input stream.
- * @param close true to close the input stream after reading.
- * @return the content of the given input stream.
- * @throws IOException if a problem occurs during reading.
- */
- private static byte[] readStream(final InputStream inputStream, final boolean close)
- throws IOException {
- if (inputStream == null) {
- throw new IOException("Class not found");
- }
- 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) {
- outputStream.write(data, 0, bytesRead);
- }
- outputStream.flush();
- return outputStream.toByteArray();
- } finally {
- if (close) {
- inputStream.close();
- }
- }
- }
-
- // -----------------------------------------------------------------------------------------------
- // Accessors
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Returns the class's access flags (see {@link Opcodes}). This value may not reflect Deprecated
- * and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes.
- *
- * @return the class access flags.
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
- */
- public int getAccess() {
- return readUnsignedShort(header);
- }
-
- /**
- * Returns the internal name of the class (see {@link Type#getInternalName()}).
- *
- * @return the internal class name.
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
- */
- public String getClassName() {
- // this_class is just after the access_flags field (using 2 bytes).
- return readClass(header + 2, new char[maxStringLength]);
- }
-
- /**
- * Returns the internal of name of the super class (see {@link Type#getInternalName()}). For
- * interfaces, the super class is {@link Object}.
- *
- * @return the internal name of the super class, or {@literal null} for {@link Object} class.
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
- */
- public String getSuperName() {
- // super_class is after the access_flags and this_class fields (2 bytes each).
- return readClass(header + 4, new char[maxStringLength]);
- }
-
- /**
- * Returns the internal names of the implemented interfaces (see {@link Type#getInternalName()}).
- *
- * @return the internal names of the directly implemented interfaces. Inherited implemented
- * interfaces are not returned.
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
- */
- public String[] getInterfaces() {
- // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each).
- int currentOffset = header + 6;
- int interfacesCount = readUnsignedShort(currentOffset);
- String[] interfaces = new String[interfacesCount];
- if (interfacesCount > 0) {
- char[] charBuffer = new char[maxStringLength];
- for (int i = 0; i < interfacesCount; ++i) {
- currentOffset += 2;
- interfaces[i] = readClass(currentOffset, charBuffer);
- }
- }
- return interfaces;
- }
-
- // -----------------------------------------------------------------------------------------------
- // Public methods
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
- * {@link ClassReader}.
- *
- * @param classVisitor the visitor that must visit this class.
- * @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}.
- */
- public void accept(final ClassVisitor classVisitor, final int parsingOptions) {
- accept(classVisitor, new Attribute[0], parsingOptions);
- }
-
- /**
- * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
- * {@link ClassReader}.
- *
- * @param classVisitor the visitor that must visit this class.
- * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
- * the class. Any attribute whose type is not equal to the type of one the prototypes will not
- * be parsed: its byte array value will be passed unchanged to the ClassWriter. This may
- * corrupt it if this value contains references to the constant pool, or has syntactic or
- * semantic links with a class element that has been transformed by a class adapter between
- * the reader and the writer.
- * @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}.
- */
- public void accept(
- final ClassVisitor classVisitor,
- final Attribute[] attributePrototypes,
- final int parsingOptions) {
- Context context = new Context();
- context.attributePrototypes = attributePrototypes;
- context.parsingOptions = parsingOptions;
- context.charBuffer = new char[maxStringLength];
-
- // Read the access_flags, this_class, super_class, interface_count and interfaces fields.
- char[] charBuffer = context.charBuffer;
- int currentOffset = header;
- int accessFlags = readUnsignedShort(currentOffset);
- String thisClass = readClass(currentOffset + 2, charBuffer);
- String superClass = readClass(currentOffset + 4, charBuffer);
- String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
- currentOffset += 8;
- for (int i = 0; i < interfaces.length; ++i) {
- interfaces[i] = readClass(currentOffset, charBuffer);
- currentOffset += 2;
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param classFile the JVMS ClassFile structure to be read.
+ */
+ public ClassReader(final byte[] classFile) {
+ this(classFile, 0, classFile.length);
}
- // Read the class 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 offset of the InnerClasses attribute, or 0.
- int innerClassesOffset = 0;
- // - The offset of the EnclosingMethod attribute, or 0.
- int enclosingMethodOffset = 0;
- // - The string corresponding to the Signature attribute, or null.
- String signature = null;
- // - The string corresponding to the SourceFile attribute, or null.
- String sourceFile = null;
- // - The string corresponding to the SourceDebugExtension attribute, or null.
- String sourceDebugExtension = 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 offset of the Module attribute, or 0.
- int moduleOffset = 0;
- // - The offset of the ModulePackages attribute, or 0.
- int modulePackagesOffset = 0;
- // - The string corresponding to the ModuleMainClass attribute, or null.
- String moduleMainClass = null;
- // - The string corresponding to the NestHost attribute, or null.
- String nestHostClass = null;
- // - The offset of the NestMembers attribute, or 0.
- int nestMembersOffset = 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 currentAttributeOffset = getFirstAttributeOffset();
- for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
- // Read the attribute_info's attribute_name and attribute_length fields.
- String attributeName = readUTF8(currentAttributeOffset, charBuffer);
- int attributeLength = readInt(currentAttributeOffset + 2);
- currentAttributeOffset += 6;
- // The tests are sorted in decreasing frequency order (based on frequencies observed on
- // typical classes).
- if (Constants.SOURCE_FILE.equals(attributeName)) {
- sourceFile = readUTF8(currentAttributeOffset, charBuffer);
- } else if (Constants.INNER_CLASSES.equals(attributeName)) {
- innerClassesOffset = currentAttributeOffset;
- } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) {
- enclosingMethodOffset = currentAttributeOffset;
- } else if (Constants.NEST_HOST.equals(attributeName)) {
- nestHostClass = readClass(currentAttributeOffset, charBuffer);
- } else if (Constants.NEST_MEMBERS.equals(attributeName)) {
- nestMembersOffset = currentAttributeOffset;
- } else if (Constants.SIGNATURE.equals(attributeName)) {
- signature = readUTF8(currentAttributeOffset, charBuffer);
- } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleAnnotationsOffset = currentAttributeOffset;
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset;
- } else if (Constants.DEPRECATED.equals(attributeName)) {
- accessFlags |= Opcodes.ACC_DEPRECATED;
- } else if (Constants.SYNTHETIC.equals(attributeName)) {
- accessFlags |= Opcodes.ACC_SYNTHETIC;
- } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
- sourceDebugExtension =
- readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
- } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
- } else if (Constants.MODULE.equals(attributeName)) {
- moduleOffset = currentAttributeOffset;
- } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
- moduleMainClass = readClass(currentAttributeOffset, charBuffer);
- } else if (Constants.MODULE_PACKAGES.equals(attributeName)) {
- modulePackagesOffset = currentAttributeOffset;
- } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
- // The BootstrapMethods attribute is read in the constructor.
- Attribute attribute =
- readAttribute(
- attributePrototypes,
- attributeName,
- currentAttributeOffset,
- attributeLength,
- charBuffer,
- -1,
- null);
- attribute.nextAttribute = attributes;
- attributes = attribute;
- }
- currentAttributeOffset += attributeLength;
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
+ * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
+ * @param classFileLength the length in bytes of the ClassFile to be read.
+ */
+ public ClassReader(
+ final byte[] classFileBuffer,
+ final int classFileOffset,
+ final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility.
+ this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true);
}
- // Visit the class declaration. The minor_version and major_version fields start 6 bytes before
- // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
- classVisitor.visit(
- readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);
-
- // Visit the SourceFile and SourceDebugExtenstion attributes.
- if ((parsingOptions & SKIP_DEBUG) == 0
- && (sourceFile != null || sourceDebugExtension != null)) {
- classVisitor.visitSource(sourceFile, sourceDebugExtension);
- }
-
- // Visit the Module, ModulePackages and ModuleMainClass attributes.
- if (moduleOffset != 0) {
- readModuleAttributes(
- classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
- }
-
- // Visit the NestHost attribute.
- if (nestHostClass != null) {
- classVisitor.visitNestHost(nestHostClass);
- }
-
- // Visit the EnclosingMethod attribute.
- if (enclosingMethodOffset != 0) {
- String className = readClass(enclosingMethodOffset, charBuffer);
- int methodIndex = readUnsignedShort(enclosingMethodOffset + 2);
- String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer);
- String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer);
- classVisitor.visitOuterClass(className, name, type);
- }
-
- // 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(
- classVisitor.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(
- classVisitor.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(
- classVisitor.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(
- classVisitor.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 ClassWriter.
- Attribute nextAttribute = attributes.nextAttribute;
- attributes.nextAttribute = null;
- classVisitor.visitAttribute(attributes);
- attributes = nextAttribute;
- }
-
- // Visit the NestedMembers attribute.
- if (nestMembersOffset != 0) {
- int numberOfNestMembers = readUnsignedShort(nestMembersOffset);
- int currentNestMemberOffset = nestMembersOffset + 2;
- while (numberOfNestMembers-- > 0) {
- classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer));
- currentNestMemberOffset += 2;
- }
- }
-
- // Visit the InnerClasses attribute.
- if (innerClassesOffset != 0) {
- int numberOfClasses = readUnsignedShort(innerClassesOffset);
- int currentClassesOffset = innerClassesOffset + 2;
- while (numberOfClasses-- > 0) {
- classVisitor.visitInnerClass(
- readClass(currentClassesOffset, charBuffer),
- readClass(currentClassesOffset + 2, charBuffer),
- readUTF8(currentClassesOffset + 4, charBuffer),
- readUnsignedShort(currentClassesOffset + 6));
- currentClassesOffset += 8;
- }
- }
-
- // Visit the fields and methods.
- int fieldsCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (fieldsCount-- > 0) {
- currentOffset = readField(classVisitor, context, currentOffset);
- }
- int methodsCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (methodsCount-- > 0) {
- currentOffset = readMethod(classVisitor, context, currentOffset);
- }
-
- // Visit the end of the class.
- classVisitor.visitEnd();
- }
-
- // ----------------------------------------------------------------------------------------------
- // Methods to parse modules, fields and methods
- // ----------------------------------------------------------------------------------------------
-
- /**
- * Reads the Module, ModulePackages and ModuleMainClass attributes and visit them.
- *
- * @param classVisitor the current class visitor
- * @param context information about the class being parsed.
- * @param moduleOffset the offset of the Module attribute (excluding the attribute_info's
- * 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.
- */
- private void readModuleAttributes(
- final ClassVisitor classVisitor,
- final Context context,
- final int moduleOffset,
- final int modulePackagesOffset,
- final String moduleMainClass) {
- char[] buffer = context.charBuffer;
-
- // Read the module_name_index, module_flags and module_version_index fields and visit them.
- int currentOffset = moduleOffset;
- String moduleName = readModule(currentOffset, buffer);
- int moduleFlags = readUnsignedShort(currentOffset + 2);
- String moduleVersion = readUTF8(currentOffset + 4, buffer);
- currentOffset += 6;
- ModuleVisitor moduleVisitor = classVisitor.visitModule(moduleName, moduleFlags, moduleVersion);
- if (moduleVisitor == null) {
- return;
- }
-
- // Visit the ModuleMainClass attribute.
- if (moduleMainClass != null) {
- moduleVisitor.visitMainClass(moduleMainClass);
- }
-
- // Visit the ModulePackages attribute.
- if (modulePackagesOffset != 0) {
- int packageCount = readUnsignedShort(modulePackagesOffset);
- int currentPackageOffset = modulePackagesOffset + 2;
- while (packageCount-- > 0) {
- moduleVisitor.visitPackage(readPackage(currentPackageOffset, buffer));
- currentPackageOffset += 2;
- }
- }
-
- // Read the 'requires_count' and 'requires' fields.
- int requiresCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (requiresCount-- > 0) {
- // Read the requires_index, requires_flags and requires_version fields and visit them.
- String requires = readModule(currentOffset, buffer);
- int requiresFlags = readUnsignedShort(currentOffset + 2);
- String requiresVersion = readUTF8(currentOffset + 4, buffer);
- currentOffset += 6;
- moduleVisitor.visitRequire(requires, requiresFlags, requiresVersion);
- }
-
- // Read the 'exports_count' and 'exports' fields.
- int exportsCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (exportsCount-- > 0) {
- // Read the exports_index, exports_flags, exports_to_count and exports_to_index fields
- // and visit them.
- String exports = readPackage(currentOffset, buffer);
- int exportsFlags = readUnsignedShort(currentOffset + 2);
- int exportsToCount = readUnsignedShort(currentOffset + 4);
- currentOffset += 6;
- String[] exportsTo = null;
- if (exportsToCount != 0) {
- exportsTo = new String[exportsToCount];
- for (int i = 0; i < exportsToCount; ++i) {
- exportsTo[i] = readModule(currentOffset, buffer);
- currentOffset += 2;
+ /**
+ * Constructs a new {@link ClassReader} object. This internal constructor must not be exposed
+ * as a public API.
+ *
+ * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
+ * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
+ * @param checkClassVersion whether to check the class version or not.
+ */
+ ClassReader(
+ final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
+ 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.V17) {
+ throw new IllegalArgumentException(
+ "Unsupported class file major version " + readShort(classFileOffset + 6));
}
- }
- moduleVisitor.visitExport(exports, exportsFlags, exportsTo);
- }
-
- // Reads the 'opens_count' and 'opens' fields.
- int opensCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (opensCount-- > 0) {
- // Read the opens_index, opens_flags, opens_to_count and opens_to_index fields and visit them.
- String opens = readPackage(currentOffset, buffer);
- int opensFlags = readUnsignedShort(currentOffset + 2);
- int opensToCount = readUnsignedShort(currentOffset + 4);
- currentOffset += 6;
- String[] opensTo = null;
- if (opensToCount != 0) {
- opensTo = new String[opensToCount];
- for (int i = 0; i < opensToCount; ++i) {
- opensTo[i] = readModule(currentOffset, buffer);
- currentOffset += 2;
- }
- }
- moduleVisitor.visitOpen(opens, opensFlags, opensTo);
- }
-
- // Read the 'uses_count' and 'uses' fields.
- int usesCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (usesCount-- > 0) {
- moduleVisitor.visitUse(readClass(currentOffset, buffer));
- currentOffset += 2;
- }
-
- // Read the 'provides_count' and 'provides' fields.
- int providesCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (providesCount-- > 0) {
- // Read the provides_index, provides_with_count and provides_with_index fields and visit them.
- String provides = readClass(currentOffset, buffer);
- int providesWithCount = readUnsignedShort(currentOffset + 2);
- currentOffset += 4;
- String[] providesWith = new String[providesWithCount];
- for (int i = 0; i < providesWithCount; ++i) {
- providesWith[i] = readClass(currentOffset, buffer);
- currentOffset += 2;
- }
- moduleVisitor.visitProvide(provides, providesWith);
- }
-
- // Visit the end of the module attributes.
- moduleVisitor.visitEnd();
- }
-
- /**
- * Reads a JVMS field_info structure and makes the given visitor visit it.
- *
- * @param classVisitor the visitor that must visit the field.
- * @param context information about the class being parsed.
- * @param fieldInfoOffset the start offset of the field_info structure.
- * @return the offset of the first byte following the field_info structure.
- */
- private int readField(
- final ClassVisitor classVisitor, final Context context, final int fieldInfoOffset) {
- char[] charBuffer = context.charBuffer;
-
- // Read the access_flags, name_index and descriptor_index fields.
- int currentOffset = fieldInfoOffset;
- int accessFlags = readUnsignedShort(currentOffset);
- String name = readUTF8(currentOffset + 2, charBuffer);
- String descriptor = readUTF8(currentOffset + 4, charBuffer);
- currentOffset += 6;
-
- // Read the field 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 value corresponding to the ConstantValue attribute, or null.
- Object constantValue = null;
- // - 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.CONSTANT_VALUE.equals(attributeName)) {
- int constantvalueIndex = readUnsignedShort(currentOffset);
- constantValue = constantvalueIndex == 0 ? null : readConst(constantvalueIndex, charBuffer);
- } else if (Constants.SIGNATURE.equals(attributeName)) {
- signature = readUTF8(currentOffset, charBuffer);
- } else if (Constants.DEPRECATED.equals(attributeName)) {
- accessFlags |= Opcodes.ACC_DEPRECATED;
- } else if (Constants.SYNTHETIC.equals(attributeName)) {
- accessFlags |= Opcodes.ACC_SYNTHETIC;
- } 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;
- }
-
- // Visit the field declaration.
- FieldVisitor fieldVisitor =
- classVisitor.visitField(accessFlags, name, descriptor, signature, constantValue);
- if (fieldVisitor == 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(
- fieldVisitor.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(
- fieldVisitor.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(
- fieldVisitor.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(
- fieldVisitor.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;
- fieldVisitor.visitAttribute(attributes);
- attributes = nextAttribute;
- }
-
- // Visit the end of the field.
- fieldVisitor.visitEnd();
- return currentOffset;
- }
-
- /**
- * Reads a JVMS method_info structure and makes the given visitor visit it.
- *
- * @param classVisitor the visitor that must visit the method.
- * @param context information about the class being parsed.
- * @param methodInfoOffset the start offset of the method_info structure.
- * @return the offset of the first byte following the method_info structure.
- */
- private int readMethod(
- final ClassVisitor classVisitor, final Context context, final int methodInfoOffset) {
- char[] charBuffer = context.charBuffer;
-
- // Read the access_flags, name_index and descriptor_index fields.
- int currentOffset = methodInfoOffset;
- context.currentMethodAccessFlags = readUnsignedShort(currentOffset);
- context.currentMethodName = readUTF8(currentOffset + 2, charBuffer);
- context.currentMethodDescriptor = readUTF8(currentOffset + 4, charBuffer);
- currentOffset += 6;
-
- // Read the method 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 offset of the Code attribute, or 0.
- int codeOffset = 0;
- // - The offset of the Exceptions attribute, or 0.
- int exceptionsOffset = 0;
- // - The strings corresponding to the Exceptions attribute, or null.
- String[] exceptions = null;
- // - Whether the method has a Synthetic attribute.
- boolean synthetic = false;
- // - The constant pool index contained in the Signature attribute, or 0.
- int signatureIndex = 0;
- // - 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 RuntimeVisibleParameterAnnotations attribute, or 0.
- int runtimeVisibleParameterAnnotationsOffset = 0;
- // - The offset of the RuntimeInvisibleParameterAnnotations attribute, or 0.
- int runtimeInvisibleParameterAnnotationsOffset = 0;
- // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
- int runtimeVisibleTypeAnnotationsOffset = 0;
- // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
- int runtimeInvisibleTypeAnnotationsOffset = 0;
- // - The offset of the AnnotationDefault attribute, or 0.
- int annotationDefaultOffset = 0;
- // - The offset of the MethodParameters attribute, or 0.
- int methodParametersOffset = 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.CODE.equals(attributeName)) {
- if ((context.parsingOptions & SKIP_CODE) == 0) {
- codeOffset = currentOffset;
- }
- } else if (Constants.EXCEPTIONS.equals(attributeName)) {
- exceptionsOffset = currentOffset;
- exceptions = new String[readUnsignedShort(exceptionsOffset)];
- int currentExceptionOffset = exceptionsOffset + 2;
- for (int i = 0; i < exceptions.length; ++i) {
- exceptions[i] = readClass(currentExceptionOffset, charBuffer);
- currentExceptionOffset += 2;
- }
- } else if (Constants.SIGNATURE.equals(attributeName)) {
- signatureIndex = readUnsignedShort(currentOffset);
- } else if (Constants.DEPRECATED.equals(attributeName)) {
- context.currentMethodAccessFlags |= Opcodes.ACC_DEPRECATED;
- } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleTypeAnnotationsOffset = currentOffset;
- } else if (Constants.ANNOTATION_DEFAULT.equals(attributeName)) {
- annotationDefaultOffset = currentOffset;
- } else if (Constants.SYNTHETIC.equals(attributeName)) {
- synthetic = true;
- context.currentMethodAccessFlags |= Opcodes.ACC_SYNTHETIC;
- } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleTypeAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
- runtimeVisibleParameterAnnotationsOffset = currentOffset;
- } else if (Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
- runtimeInvisibleParameterAnnotationsOffset = currentOffset;
- } else if (Constants.METHOD_PARAMETERS.equals(attributeName)) {
- methodParametersOffset = currentOffset;
- } else {
- Attribute attribute =
- readAttribute(
- context.attributePrototypes,
- attributeName,
- currentOffset,
- attributeLength,
- charBuffer,
- -1,
- null);
- attribute.nextAttribute = attributes;
- attributes = attribute;
- }
- currentOffset += attributeLength;
- }
-
- // Visit the method declaration.
- MethodVisitor methodVisitor =
- classVisitor.visitMethod(
- context.currentMethodAccessFlags,
- context.currentMethodName,
- context.currentMethodDescriptor,
- signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer),
- exceptions);
- if (methodVisitor == null) {
- return currentOffset;
- }
-
- // If the returned MethodVisitor is in fact a MethodWriter, it means there is no method
- // adapter between the reader and the writer. In this case, it might be possible to copy
- // the method attributes directly into the writer. If so, return early without visiting
- // the content of these attributes.
- if (methodVisitor instanceof MethodWriter) {
- MethodWriter methodWriter = (MethodWriter) methodVisitor;
- if (methodWriter.canCopyMethodAttributes(
- this,
- 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) {
- int parametersCount = readByte(methodParametersOffset);
- int currentParameterOffset = methodParametersOffset + 1;
- while (parametersCount-- > 0) {
- // Read the name_index and access_flags fields and visit them.
- methodVisitor.visitParameter(
- readUTF8(currentParameterOffset, charBuffer),
- readUnsignedShort(currentParameterOffset + 2));
- currentParameterOffset += 4;
- }
- }
-
- // Visit the AnnotationDefault attribute.
- if (annotationDefaultOffset != 0) {
- AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault();
- readElementValue(annotationVisitor, annotationDefaultOffset, null, charBuffer);
- if (annotationVisitor != null) {
- annotationVisitor.visitEnd();
- }
- }
-
- // 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(
- methodVisitor.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(
- methodVisitor.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(
- methodVisitor.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(
- methodVisitor.visitTypeAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- annotationDescriptor,
- /* visible = */ false),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- }
-
- // Visit the RuntimeVisibleParameterAnnotations attribute.
- if (runtimeVisibleParameterAnnotationsOffset != 0) {
- readParameterAnnotations(
- methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true);
- }
-
- // Visit the RuntimeInvisibleParameterAnnotations attribute.
- if (runtimeInvisibleParameterAnnotationsOffset != 0) {
- readParameterAnnotations(
- methodVisitor,
- context,
- runtimeInvisibleParameterAnnotationsOffset,
- /* visible = */ false);
- }
-
- // Visit the non standard attributes.
- while (attributes != null) {
- // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
- Attribute nextAttribute = attributes.nextAttribute;
- attributes.nextAttribute = null;
- methodVisitor.visitAttribute(attributes);
- attributes = nextAttribute;
- }
-
- // Visit the Code attribute.
- if (codeOffset != 0) {
- methodVisitor.visitCode();
- readCode(methodVisitor, context, codeOffset);
- }
-
- // Visit the end of the method.
- methodVisitor.visitEnd();
- return currentOffset;
- }
-
- // ----------------------------------------------------------------------------------------------
- // Methods to parse a Code attribute
- // ----------------------------------------------------------------------------------------------
-
- /**
- * Reads a JVMS 'Code' attribute and makes the given visitor visit it.
- *
- * @param methodVisitor the visitor that must visit the Code attribute.
- * @param context information about the class being parsed.
- * @param codeOffset the start offset in {@link #classFileBuffer} of the Code attribute, excluding
- * its attribute_name_index and attribute_length fields.
- */
- private void readCode(
- final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
- int currentOffset = codeOffset;
-
- // Read the max_stack, max_locals and code_length fields.
- final byte[] classBuffer = classFileBuffer;
- final char[] charBuffer = context.charBuffer;
- final int majorVersion = readUnsignedShort(6);
- final int minorVersion = readUnsignedShort(4);
-
- final int maxStack;
- final int maxLocals;
- final int codeLength;
-
- if (majorVersion == 45 && minorVersion <= 2) {
- maxStack = readByte(currentOffset);
- maxLocals = readByte(currentOffset + 1);
- codeLength = readUnsignedShort(currentOffset + 2);
- currentOffset += 4;
- } else {
- maxStack = readUnsignedShort(currentOffset);
- maxLocals = readUnsignedShort(currentOffset + 2);
- codeLength = readInt(currentOffset + 4);
- currentOffset += 8;
- }
-
- // Read the bytecode 'code' array to create a label for each referenced instruction.
- final int bytecodeStartOffset = currentOffset;
- final int bytecodeEndOffset = currentOffset + codeLength;
- final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
- while (currentOffset < bytecodeEndOffset) {
- final int bytecodeOffset = currentOffset - bytecodeStartOffset;
- final int opcode = classBuffer[currentOffset] & 0xFF;
- switch (opcode) {
- case Opcodes.NOP:
- case Opcodes.ACONST_NULL:
- case Opcodes.ICONST_M1:
- case Opcodes.ICONST_0:
- case Opcodes.ICONST_1:
- case Opcodes.ICONST_2:
- case Opcodes.ICONST_3:
- case Opcodes.ICONST_4:
- case Opcodes.ICONST_5:
- case Opcodes.LCONST_0:
- case Opcodes.LCONST_1:
- case Opcodes.FCONST_0:
- case Opcodes.FCONST_1:
- case Opcodes.FCONST_2:
- case Opcodes.DCONST_0:
- case Opcodes.DCONST_1:
- case Opcodes.IALOAD:
- case Opcodes.LALOAD:
- case Opcodes.FALOAD:
- case Opcodes.DALOAD:
- case Opcodes.AALOAD:
- case Opcodes.BALOAD:
- case Opcodes.CALOAD:
- case Opcodes.SALOAD:
- case Opcodes.IASTORE:
- case Opcodes.LASTORE:
- case Opcodes.FASTORE:
- case Opcodes.DASTORE:
- case Opcodes.AASTORE:
- case Opcodes.BASTORE:
- case Opcodes.CASTORE:
- case Opcodes.SASTORE:
- case Opcodes.POP:
- case Opcodes.POP2:
- case Opcodes.DUP:
- case Opcodes.DUP_X1:
- case Opcodes.DUP_X2:
- case Opcodes.DUP2:
- case Opcodes.DUP2_X1:
- case Opcodes.DUP2_X2:
- case Opcodes.SWAP:
- case Opcodes.IADD:
- case Opcodes.LADD:
- case Opcodes.FADD:
- case Opcodes.DADD:
- case Opcodes.ISUB:
- case Opcodes.LSUB:
- case Opcodes.FSUB:
- case Opcodes.DSUB:
- case Opcodes.IMUL:
- case Opcodes.LMUL:
- case Opcodes.FMUL:
- case Opcodes.DMUL:
- case Opcodes.IDIV:
- case Opcodes.LDIV:
- case Opcodes.FDIV:
- case Opcodes.DDIV:
- case Opcodes.IREM:
- case Opcodes.LREM:
- case Opcodes.FREM:
- case Opcodes.DREM:
- case Opcodes.INEG:
- case Opcodes.LNEG:
- case Opcodes.FNEG:
- case Opcodes.DNEG:
- case Opcodes.ISHL:
- case Opcodes.LSHL:
- case Opcodes.ISHR:
- case Opcodes.LSHR:
- case Opcodes.IUSHR:
- case Opcodes.LUSHR:
- case Opcodes.IAND:
- case Opcodes.LAND:
- case Opcodes.IOR:
- case Opcodes.LOR:
- case Opcodes.IXOR:
- case Opcodes.LXOR:
- case Opcodes.I2L:
- case Opcodes.I2F:
- case Opcodes.I2D:
- case Opcodes.L2I:
- case Opcodes.L2F:
- case Opcodes.L2D:
- case Opcodes.F2I:
- case Opcodes.F2L:
- case Opcodes.F2D:
- case Opcodes.D2I:
- case Opcodes.D2L:
- case Opcodes.D2F:
- case Opcodes.I2B:
- case Opcodes.I2C:
- case Opcodes.I2S:
- case Opcodes.LCMP:
- case Opcodes.FCMPL:
- case Opcodes.FCMPG:
- case Opcodes.DCMPL:
- case Opcodes.DCMPG:
- case Opcodes.IRETURN:
- case Opcodes.LRETURN:
- case Opcodes.FRETURN:
- case Opcodes.DRETURN:
- case Opcodes.ARETURN:
- case Opcodes.RETURN:
- case Opcodes.ARRAYLENGTH:
- case Opcodes.ATHROW:
- case Opcodes.MONITORENTER:
- case Opcodes.MONITOREXIT:
- case Constants.ILOAD_0:
- case Constants.ILOAD_1:
- case Constants.ILOAD_2:
- case Constants.ILOAD_3:
- case Constants.LLOAD_0:
- case Constants.LLOAD_1:
- case Constants.LLOAD_2:
- case Constants.LLOAD_3:
- case Constants.FLOAD_0:
- case Constants.FLOAD_1:
- case Constants.FLOAD_2:
- case Constants.FLOAD_3:
- case Constants.DLOAD_0:
- case Constants.DLOAD_1:
- case Constants.DLOAD_2:
- case Constants.DLOAD_3:
- case Constants.ALOAD_0:
- case Constants.ALOAD_1:
- case Constants.ALOAD_2:
- case Constants.ALOAD_3:
- case Constants.ISTORE_0:
- case Constants.ISTORE_1:
- case Constants.ISTORE_2:
- case Constants.ISTORE_3:
- case Constants.LSTORE_0:
- case Constants.LSTORE_1:
- case Constants.LSTORE_2:
- case Constants.LSTORE_3:
- case Constants.FSTORE_0:
- case Constants.FSTORE_1:
- case Constants.FSTORE_2:
- case Constants.FSTORE_3:
- case Constants.DSTORE_0:
- case Constants.DSTORE_1:
- case Constants.DSTORE_2:
- case Constants.DSTORE_3:
- case Constants.ASTORE_0:
- case Constants.ASTORE_1:
- case Constants.ASTORE_2:
- case Constants.ASTORE_3:
- currentOffset += 1;
- break;
- case Opcodes.IFEQ:
- case Opcodes.IFNE:
- case Opcodes.IFLT:
- case Opcodes.IFGE:
- case Opcodes.IFGT:
- case Opcodes.IFLE:
- case Opcodes.IF_ICMPEQ:
- case Opcodes.IF_ICMPNE:
- case Opcodes.IF_ICMPLT:
- case Opcodes.IF_ICMPGE:
- case Opcodes.IF_ICMPGT:
- case Opcodes.IF_ICMPLE:
- case Opcodes.IF_ACMPEQ:
- case Opcodes.IF_ACMPNE:
- case Opcodes.GOTO:
- case Opcodes.JSR:
- case Opcodes.IFNULL:
- case Opcodes.IFNONNULL:
- createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
- currentOffset += 3;
- break;
- case Constants.ASM_IFEQ:
- case Constants.ASM_IFNE:
- case Constants.ASM_IFLT:
- case Constants.ASM_IFGE:
- case Constants.ASM_IFGT:
- case Constants.ASM_IFLE:
- case Constants.ASM_IF_ICMPEQ:
- case Constants.ASM_IF_ICMPNE:
- case Constants.ASM_IF_ICMPLT:
- case Constants.ASM_IF_ICMPGE:
- case Constants.ASM_IF_ICMPGT:
- case Constants.ASM_IF_ICMPLE:
- case Constants.ASM_IF_ACMPEQ:
- case Constants.ASM_IF_ACMPNE:
- case Constants.ASM_GOTO:
- case Constants.ASM_JSR:
- case Constants.ASM_IFNULL:
- case Constants.ASM_IFNONNULL:
- createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels);
- currentOffset += 3;
- break;
- case Constants.GOTO_W:
- case Constants.JSR_W:
- case Constants.ASM_GOTO_W:
- createLabel(bytecodeOffset + readInt(currentOffset + 1), labels);
- currentOffset += 5;
- break;
- case Constants.WIDE:
- switch (classBuffer[currentOffset + 1] & 0xFF) {
- case Opcodes.ILOAD:
- case Opcodes.FLOAD:
- case Opcodes.ALOAD:
- case Opcodes.LLOAD:
- case Opcodes.DLOAD:
- case Opcodes.ISTORE:
- case Opcodes.FSTORE:
- case Opcodes.ASTORE:
- case Opcodes.LSTORE:
- case Opcodes.DSTORE:
- case Opcodes.RET:
- currentOffset += 4;
- break;
- case Opcodes.IINC:
- currentOffset += 6;
- break;
+ // Create the constant pool arrays. The constant_pool_count field is after the magic,
+ // minor_version and major_version fields, which use 4, 2 and 2 bytes respectively.
+ int constantPoolCount = readUnsignedShort(classFileOffset + 8);
+ cpInfoOffsets = new int[constantPoolCount];
+ constantUtf8Values = new String[constantPoolCount];
+ // Compute the offset of each constant pool entry, as well as a conservative estimate of the
+ // maximum length of the constant pool strings. The first constant pool entry is after the
+ // magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2
+ // bytes respectively.
+ int currentCpInfoIndex = 1;
+ int currentCpInfoOffset = classFileOffset + 10;
+ int currentMaxStringLength = 0;
+ boolean hasBootstrapMethods = false;
+ boolean hasConstantDynamic = false;
+ // The offset of the other entries depend on the total size of all the previous entries.
+ while (currentCpInfoIndex < constantPoolCount) {
+ cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
+ int cpInfoSize;
+ switch (classFileBuffer[currentCpInfoOffset]) {
+ case Symbol.CONSTANT_FIELDREF_TAG:
+ case Symbol.CONSTANT_METHODREF_TAG:
+ case Symbol.CONSTANT_INTERFACE_METHODREF_TAG:
+ case Symbol.CONSTANT_INTEGER_TAG:
+ case Symbol.CONSTANT_FLOAT_TAG:
+ case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
+ cpInfoSize = 5;
+ break;
+ case Symbol.CONSTANT_DYNAMIC_TAG:
+ cpInfoSize = 5;
+ hasBootstrapMethods = true;
+ hasConstantDynamic = true;
+ break;
+ case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
+ cpInfoSize = 5;
+ hasBootstrapMethods = true;
+ break;
+ case Symbol.CONSTANT_LONG_TAG:
+ case Symbol.CONSTANT_DOUBLE_TAG:
+ cpInfoSize = 9;
+ currentCpInfoIndex++;
+ break;
+ case Symbol.CONSTANT_UTF8_TAG:
+ cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1);
+ if (cpInfoSize > currentMaxStringLength) {
+ // The size in bytes of this CONSTANT_Utf8 structure provides a conservative estimate
+ // of the length in characters of the corresponding string, and is much cheaper to
+ // compute than this exact length.
+ currentMaxStringLength = cpInfoSize;
+ }
+ break;
+ case Symbol.CONSTANT_METHOD_HANDLE_TAG:
+ cpInfoSize = 4;
+ break;
+ case Symbol.CONSTANT_CLASS_TAG:
+ case Symbol.CONSTANT_STRING_TAG:
+ case Symbol.CONSTANT_METHOD_TYPE_TAG:
+ case Symbol.CONSTANT_PACKAGE_TAG:
+ case Symbol.CONSTANT_MODULE_TAG:
+ cpInfoSize = 3;
+ break;
default:
- throw new IllegalArgumentException();
- }
- break;
- case Opcodes.TABLESWITCH:
- // Skip 0 to 3 padding bytes.
- currentOffset += 4 - (bytecodeOffset & 3);
- // Read the default label and the number of table entries.
- createLabel(bytecodeOffset + readInt(currentOffset), labels);
- int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1;
- currentOffset += 12;
- // Read the table labels.
- while (numTableEntries-- > 0) {
- createLabel(bytecodeOffset + readInt(currentOffset), labels);
- currentOffset += 4;
- }
- break;
- case Opcodes.LOOKUPSWITCH:
- // Skip 0 to 3 padding bytes.
- currentOffset += 4 - (bytecodeOffset & 3);
- // Read the default label and the number of switch cases.
- createLabel(bytecodeOffset + readInt(currentOffset), labels);
- int numSwitchCases = readInt(currentOffset + 4);
- currentOffset += 8;
- // Read the switch labels.
- while (numSwitchCases-- > 0) {
- createLabel(bytecodeOffset + readInt(currentOffset + 4), labels);
- currentOffset += 8;
- }
- break;
- case Opcodes.ILOAD:
- case Opcodes.LLOAD:
- case Opcodes.FLOAD:
- case Opcodes.DLOAD:
- case Opcodes.ALOAD:
- case Opcodes.ISTORE:
- case Opcodes.LSTORE:
- case Opcodes.FSTORE:
- case Opcodes.DSTORE:
- case Opcodes.ASTORE:
- case Opcodes.RET:
- case Opcodes.BIPUSH:
- case Opcodes.NEWARRAY:
- case Opcodes.LDC:
- currentOffset += 2;
- break;
- case Opcodes.SIPUSH:
- case Constants.LDC_W:
- case Constants.LDC2_W:
- case Opcodes.GETSTATIC:
- case Opcodes.PUTSTATIC:
- case Opcodes.GETFIELD:
- case Opcodes.PUTFIELD:
- case Opcodes.INVOKEVIRTUAL:
- case Opcodes.INVOKESPECIAL:
- case Opcodes.INVOKESTATIC:
- case Opcodes.NEW:
- case Opcodes.ANEWARRAY:
- case Opcodes.CHECKCAST:
- case Opcodes.INSTANCEOF:
- case Opcodes.IINC:
- currentOffset += 3;
- break;
- case Opcodes.INVOKEINTERFACE:
- case Opcodes.INVOKEDYNAMIC:
- currentOffset += 5;
- break;
- case Opcodes.MULTIANEWARRAY:
- currentOffset += 4;
- break;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- // Read the 'exception_table_length' and 'exception_table' field to create a label for each
- // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
- int exceptionTableLength = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (exceptionTableLength-- > 0) {
- Label start = createLabel(readUnsignedShort(currentOffset), labels);
- Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
- Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
- String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
- currentOffset += 8;
- methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
- }
-
- // Read the Code attributes to create a label for each referenced instruction (the variables
- // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the
- // attribute_name_index and attribute_length fields.
- // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0.
- // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is
- // updated after each stack_map_frame is read.
- int stackMapFrameOffset = 0;
- // - The end offset of the StackMap[Table] attribute, or 0.
- int stackMapTableEndOffset = 0;
- // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not.
- boolean compressedFrames = true;
- // - The offset of the LocalVariableTable attribute, or 0.
- int localVariableTableOffset = 0;
- // - The offset of the LocalVariableTypeTable attribute, or 0.
- int localVariableTypeTableOffset = 0;
- // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations
- // attribute, or null.
- int[] visibleTypeAnnotationOffsets = null;
- // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations
- // attribute, or null.
- int[] invisibleTypeAnnotationOffsets = null;
- // - 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;
- if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) {
- if ((context.parsingOptions & SKIP_DEBUG) == 0) {
- localVariableTableOffset = currentOffset;
- // Parse the attribute to find the corresponding (debug only) labels.
- int currentLocalVariableTableOffset = currentOffset;
- int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset);
- currentLocalVariableTableOffset += 2;
- while (localVariableTableLength-- > 0) {
- int startPc = readUnsignedShort(currentLocalVariableTableOffset);
- createDebugLabel(startPc, labels);
- int length = readUnsignedShort(currentLocalVariableTableOffset + 2);
- createDebugLabel(startPc + length, labels);
- // Skip the name_index, descriptor_index and index fields (2 bytes each).
- currentLocalVariableTableOffset += 10;
- }
- }
- } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) {
- localVariableTypeTableOffset = currentOffset;
- // Here we do not extract the labels corresponding to the attribute content. We assume they
- // are the same or a subset of those of the LocalVariableTable attribute.
- } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) {
- if ((context.parsingOptions & SKIP_DEBUG) == 0) {
- // Parse the attribute to find the corresponding (debug only) labels.
- int currentLineNumberTableOffset = currentOffset;
- int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset);
- currentLineNumberTableOffset += 2;
- while (lineNumberTableLength-- > 0) {
- int startPc = readUnsignedShort(currentLineNumberTableOffset);
- int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2);
- currentLineNumberTableOffset += 4;
- createDebugLabel(startPc, labels);
- labels[startPc].addLineNumber(lineNumber);
- }
- }
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- visibleTypeAnnotationOffsets =
- readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
- // Here we do not extract the labels corresponding to the attribute content. This would
- // require a full parsing of the attribute, which would need to be repeated when parsing
- // the bytecode instructions (see below). Instead, the content of the attribute is read one
- // type annotation at a time (i.e. after a type annotation has been visited, the next type
- // annotation is read), and the labels it contains are also extracted one annotation at a
- // time. This assumes that type annotations are ordered by increasing bytecode offset.
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
- invisibleTypeAnnotationOffsets =
- readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
- // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
- } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
- if ((context.parsingOptions & SKIP_FRAMES) == 0) {
- stackMapFrameOffset = currentOffset + 2;
- stackMapTableEndOffset = currentOffset + attributeLength;
- }
- // Here we do not extract the labels corresponding to the attribute content. This would
- // require a full parsing of the attribute, which would need to be repeated when parsing
- // the bytecode instructions (see below). Instead, the content of the attribute is read one
- // frame at a time (i.e. after a frame has been visited, the next frame is read), and the
- // labels it contains are also extracted one frame at a time. Thanks to the ordering of
- // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to
- // see an offset smaller than the offset of the current instruction and for which no Label
- // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map
- // table without a full decoding (see below).
- } else if ("StackMap".equals(attributeName)) {
- if ((context.parsingOptions & SKIP_FRAMES) == 0) {
- stackMapFrameOffset = currentOffset + 2;
- stackMapTableEndOffset = currentOffset + attributeLength;
- compressedFrames = false;
- }
- // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute,
- // although this is not guaranteed by the attribute format. This allows an incremental
- // extraction of the labels corresponding to this attribute (see the comment above for the
- // StackMapTable attribute).
- } else {
- Attribute attribute =
- readAttribute(
- context.attributePrototypes,
- attributeName,
- currentOffset,
- attributeLength,
- charBuffer,
- codeOffset,
- labels);
- attribute.nextAttribute = attributes;
- attributes = attribute;
- }
- currentOffset += attributeLength;
- }
-
- // Initialize the context fields related to stack map frames, and generate the first
- // (implicit) stack map frame, if needed.
- final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0;
- if (stackMapFrameOffset != 0) {
- // The bytecode offset of the first explicit frame is not offset_delta + 1 but only
- // offset_delta. Setting the implicit frame offset to -1 allows us to use of the
- // "offset_delta + 1" rule in all cases.
- context.currentFrameOffset = -1;
- context.currentFrameType = 0;
- context.currentFrameLocalCount = 0;
- context.currentFrameLocalCountDelta = 0;
- context.currentFrameLocalTypes = new Object[maxLocals];
- context.currentFrameStackCount = 0;
- context.currentFrameStackTypes = new Object[maxStack];
- if (expandFrames) {
- computeImplicitFrame(context);
- }
- // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the
- // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type
- // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset).
- // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare,
- // and the only consequence will be the creation of an unneeded label. This is better than
- // creating a label for each NEW instruction, and faster than fully decoding the whole stack
- // map table.
- for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
- if (classBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
- int potentialBytecodeOffset = readUnsignedShort(offset + 1);
- if (potentialBytecodeOffset >= 0
- && potentialBytecodeOffset < codeLength
- && (classBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
- == Opcodes.NEW) {
- createLabel(potentialBytecodeOffset, labels);
- }
- }
- }
- }
- if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) {
- // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method
- // does not currently have any frame. These inserted frames must be computed by simulating the
- // effect of the bytecode instructions, one by one, starting from the implicit first frame.
- // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To
- // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is
- // computed in MethodWriter).
- methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
- }
-
- // Visit the bytecode instructions. First, introduce state variables for the incremental parsing
- // of the type annotations.
-
- // Index of the next runtime visible type annotation to read (in the
- // visibleTypeAnnotationOffsets array).
- int currentVisibleTypeAnnotationIndex = 0;
- // The bytecode offset of the next runtime visible type annotation to read, or -1.
- int currentVisibleTypeAnnotationBytecodeOffset =
- getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0);
- // Index of the next runtime invisible type annotation to read (in the
- // invisibleTypeAnnotationOffsets array).
- int currentInvisibleTypeAnnotationIndex = 0;
- // The bytecode offset of the next runtime invisible type annotation to read, or -1.
- int currentInvisibleTypeAnnotationBytecodeOffset =
- getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0);
-
- // Whether a F_INSERT stack map frame must be inserted before the current instruction.
- boolean insertFrame = false;
-
- // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr
- // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific
- // instructions).
- final int wideJumpOpcodeDelta =
- (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0;
-
- currentOffset = bytecodeStartOffset;
- while (currentOffset < bytecodeEndOffset) {
- final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;
-
- // Visit the label and the line number(s) for this bytecode offset, if any.
- Label currentLabel = labels[currentBytecodeOffset];
- if (currentLabel != null) {
- currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0);
- }
-
- // Visit the stack map frame for this bytecode offset, if any.
- while (stackMapFrameOffset != 0
- && (context.currentFrameOffset == currentBytecodeOffset
- || context.currentFrameOffset == -1)) {
- // If there is a stack map frame for this offset, make methodVisitor visit it, and read the
- // next stack map frame if there is one.
- if (context.currentFrameOffset != -1) {
- if (!compressedFrames || expandFrames) {
- methodVisitor.visitFrame(
- Opcodes.F_NEW,
- context.currentFrameLocalCount,
- context.currentFrameLocalTypes,
- context.currentFrameStackCount,
- context.currentFrameStackTypes);
- } else {
- methodVisitor.visitFrame(
- context.currentFrameType,
- context.currentFrameLocalCountDelta,
- context.currentFrameLocalTypes,
- context.currentFrameStackCount,
- context.currentFrameStackTypes);
- }
- // Since there is already a stack map frame for this bytecode offset, there is no need to
- // insert a new one.
- insertFrame = false;
- }
- if (stackMapFrameOffset < stackMapTableEndOffset) {
- stackMapFrameOffset =
- readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context);
- } else {
- stackMapFrameOffset = 0;
- }
- }
-
- // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to
- // true during the previous iteration. The actual frame content is computed in MethodWriter.
- if (insertFrame) {
- if ((context.parsingOptions & EXPAND_FRAMES) != 0) {
- methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null);
- }
- insertFrame = false;
- }
-
- // Visit the instruction at this bytecode offset.
- int opcode = classBuffer[currentOffset] & 0xFF;
- switch (opcode) {
- case Opcodes.NOP:
- case Opcodes.ACONST_NULL:
- case Opcodes.ICONST_M1:
- case Opcodes.ICONST_0:
- case Opcodes.ICONST_1:
- case Opcodes.ICONST_2:
- case Opcodes.ICONST_3:
- case Opcodes.ICONST_4:
- case Opcodes.ICONST_5:
- case Opcodes.LCONST_0:
- case Opcodes.LCONST_1:
- case Opcodes.FCONST_0:
- case Opcodes.FCONST_1:
- case Opcodes.FCONST_2:
- case Opcodes.DCONST_0:
- case Opcodes.DCONST_1:
- case Opcodes.IALOAD:
- case Opcodes.LALOAD:
- case Opcodes.FALOAD:
- case Opcodes.DALOAD:
- case Opcodes.AALOAD:
- case Opcodes.BALOAD:
- case Opcodes.CALOAD:
- case Opcodes.SALOAD:
- case Opcodes.IASTORE:
- case Opcodes.LASTORE:
- case Opcodes.FASTORE:
- case Opcodes.DASTORE:
- case Opcodes.AASTORE:
- case Opcodes.BASTORE:
- case Opcodes.CASTORE:
- case Opcodes.SASTORE:
- case Opcodes.POP:
- case Opcodes.POP2:
- case Opcodes.DUP:
- case Opcodes.DUP_X1:
- case Opcodes.DUP_X2:
- case Opcodes.DUP2:
- case Opcodes.DUP2_X1:
- case Opcodes.DUP2_X2:
- case Opcodes.SWAP:
- case Opcodes.IADD:
- case Opcodes.LADD:
- case Opcodes.FADD:
- case Opcodes.DADD:
- case Opcodes.ISUB:
- case Opcodes.LSUB:
- case Opcodes.FSUB:
- case Opcodes.DSUB:
- case Opcodes.IMUL:
- case Opcodes.LMUL:
- case Opcodes.FMUL:
- case Opcodes.DMUL:
- case Opcodes.IDIV:
- case Opcodes.LDIV:
- case Opcodes.FDIV:
- case Opcodes.DDIV:
- case Opcodes.IREM:
- case Opcodes.LREM:
- case Opcodes.FREM:
- case Opcodes.DREM:
- case Opcodes.INEG:
- case Opcodes.LNEG:
- case Opcodes.FNEG:
- case Opcodes.DNEG:
- case Opcodes.ISHL:
- case Opcodes.LSHL:
- case Opcodes.ISHR:
- case Opcodes.LSHR:
- case Opcodes.IUSHR:
- case Opcodes.LUSHR:
- case Opcodes.IAND:
- case Opcodes.LAND:
- case Opcodes.IOR:
- case Opcodes.LOR:
- case Opcodes.IXOR:
- case Opcodes.LXOR:
- case Opcodes.I2L:
- case Opcodes.I2F:
- case Opcodes.I2D:
- case Opcodes.L2I:
- case Opcodes.L2F:
- case Opcodes.L2D:
- case Opcodes.F2I:
- case Opcodes.F2L:
- case Opcodes.F2D:
- case Opcodes.D2I:
- case Opcodes.D2L:
- case Opcodes.D2F:
- case Opcodes.I2B:
- case Opcodes.I2C:
- case Opcodes.I2S:
- case Opcodes.LCMP:
- case Opcodes.FCMPL:
- case Opcodes.FCMPG:
- case Opcodes.DCMPL:
- case Opcodes.DCMPG:
- case Opcodes.IRETURN:
- case Opcodes.LRETURN:
- case Opcodes.FRETURN:
- case Opcodes.DRETURN:
- case Opcodes.ARETURN:
- case Opcodes.RETURN:
- case Opcodes.ARRAYLENGTH:
- case Opcodes.ATHROW:
- case Opcodes.MONITORENTER:
- case Opcodes.MONITOREXIT:
- methodVisitor.visitInsn(opcode);
- currentOffset += 1;
- break;
- case Constants.ILOAD_0:
- case Constants.ILOAD_1:
- case Constants.ILOAD_2:
- case Constants.ILOAD_3:
- case Constants.LLOAD_0:
- case Constants.LLOAD_1:
- case Constants.LLOAD_2:
- case Constants.LLOAD_3:
- case Constants.FLOAD_0:
- case Constants.FLOAD_1:
- case Constants.FLOAD_2:
- case Constants.FLOAD_3:
- case Constants.DLOAD_0:
- case Constants.DLOAD_1:
- case Constants.DLOAD_2:
- case Constants.DLOAD_3:
- case Constants.ALOAD_0:
- case Constants.ALOAD_1:
- case Constants.ALOAD_2:
- case Constants.ALOAD_3:
- opcode -= Constants.ILOAD_0;
- methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
- currentOffset += 1;
- break;
- case Constants.ISTORE_0:
- case Constants.ISTORE_1:
- case Constants.ISTORE_2:
- case Constants.ISTORE_3:
- case Constants.LSTORE_0:
- case Constants.LSTORE_1:
- case Constants.LSTORE_2:
- case Constants.LSTORE_3:
- case Constants.FSTORE_0:
- case Constants.FSTORE_1:
- case Constants.FSTORE_2:
- case Constants.FSTORE_3:
- case Constants.DSTORE_0:
- case Constants.DSTORE_1:
- case Constants.DSTORE_2:
- case Constants.DSTORE_3:
- case Constants.ASTORE_0:
- case Constants.ASTORE_1:
- case Constants.ASTORE_2:
- case Constants.ASTORE_3:
- opcode -= Constants.ISTORE_0;
- methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
- currentOffset += 1;
- break;
- case Opcodes.IFEQ:
- case Opcodes.IFNE:
- case Opcodes.IFLT:
- case Opcodes.IFGE:
- case Opcodes.IFGT:
- case Opcodes.IFLE:
- case Opcodes.IF_ICMPEQ:
- case Opcodes.IF_ICMPNE:
- case Opcodes.IF_ICMPLT:
- case Opcodes.IF_ICMPGE:
- case Opcodes.IF_ICMPGT:
- case Opcodes.IF_ICMPLE:
- case Opcodes.IF_ACMPEQ:
- case Opcodes.IF_ACMPNE:
- case Opcodes.GOTO:
- case Opcodes.JSR:
- case Opcodes.IFNULL:
- case Opcodes.IFNONNULL:
- methodVisitor.visitJumpInsn(
- opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
- currentOffset += 3;
- break;
- case Constants.GOTO_W:
- case Constants.JSR_W:
- methodVisitor.visitJumpInsn(
- opcode - wideJumpOpcodeDelta,
- labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
- currentOffset += 5;
- break;
- case Constants.ASM_IFEQ:
- case Constants.ASM_IFNE:
- case Constants.ASM_IFLT:
- case Constants.ASM_IFGE:
- case Constants.ASM_IFGT:
- case Constants.ASM_IFLE:
- case Constants.ASM_IF_ICMPEQ:
- case Constants.ASM_IF_ICMPNE:
- case Constants.ASM_IF_ICMPLT:
- case Constants.ASM_IF_ICMPGE:
- case Constants.ASM_IF_ICMPGT:
- case Constants.ASM_IF_ICMPLE:
- case Constants.ASM_IF_ACMPEQ:
- case Constants.ASM_IF_ACMPNE:
- case Constants.ASM_GOTO:
- case Constants.ASM_JSR:
- case Constants.ASM_IFNULL:
- case Constants.ASM_IFNONNULL:
- {
- // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO
- // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx with IFNOTxxx GOTO_W L:...,
- // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and
- // where designates the instruction just after the GOTO_W.
- // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and
- // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL.
- opcode =
- opcode < Constants.ASM_IFNULL
- ? opcode - Constants.ASM_OPCODE_DELTA
- : opcode - Constants.ASM_IFNULL_OPCODE_DELTA;
- Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)];
- if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
- // Replace GOTO with GOTO_W and JSR with JSR_W.
- methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target);
- } else {
- // Compute the "opposite" of opcode. This can be done by flipping the least
- // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ
- // (with a pre and post offset by 1).
- opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1;
- Label endif = createLabel(currentBytecodeOffset + 3, labels);
- methodVisitor.visitJumpInsn(opcode, endif);
- methodVisitor.visitJumpInsn(Constants.GOTO_W, target);
- // endif designates the instruction just after GOTO_W, and is visited as part of the
- // next instruction. Since it is a jump target, we need to insert a frame here.
- insertFrame = true;
- }
- currentOffset += 3;
- break;
- }
- case Constants.ASM_GOTO_W:
- // Replace ASM_GOTO_W with GOTO_W.
- methodVisitor.visitJumpInsn(
- Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
- // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
- // IFNOTxxx ASM_GOTO_W L:..., see MethodWriter), so we need to insert a frame
- // here.
- insertFrame = true;
- currentOffset += 5;
- break;
- case Constants.WIDE:
- opcode = classBuffer[currentOffset + 1] & 0xFF;
- if (opcode == Opcodes.IINC) {
- methodVisitor.visitIincInsn(
- readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
- currentOffset += 6;
- } else {
- methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2));
- currentOffset += 4;
- }
- break;
- case Opcodes.TABLESWITCH:
- {
- // Skip 0 to 3 padding bytes.
- currentOffset += 4 - (currentBytecodeOffset & 3);
- // Read the instruction.
- Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
- int low = readInt(currentOffset + 4);
- int high = readInt(currentOffset + 8);
- currentOffset += 12;
- Label[] table = new Label[high - low + 1];
- for (int i = 0; i < table.length; ++i) {
- table[i] = labels[currentBytecodeOffset + readInt(currentOffset)];
- currentOffset += 4;
- }
- methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
- break;
- }
- case Opcodes.LOOKUPSWITCH:
- {
- // Skip 0 to 3 padding bytes.
- currentOffset += 4 - (currentBytecodeOffset & 3);
- // Read the instruction.
- Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
- int numPairs = readInt(currentOffset + 4);
- currentOffset += 8;
- int[] keys = new int[numPairs];
- Label[] values = new Label[numPairs];
- for (int i = 0; i < numPairs; ++i) {
- keys[i] = readInt(currentOffset);
- values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
- currentOffset += 8;
- }
- methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
- break;
- }
- case Opcodes.ILOAD:
- case Opcodes.LLOAD:
- case Opcodes.FLOAD:
- case Opcodes.DLOAD:
- case Opcodes.ALOAD:
- case Opcodes.ISTORE:
- case Opcodes.LSTORE:
- case Opcodes.FSTORE:
- case Opcodes.DSTORE:
- case Opcodes.ASTORE:
- case Opcodes.RET:
- methodVisitor.visitVarInsn(opcode, classBuffer[currentOffset + 1] & 0xFF);
- currentOffset += 2;
- break;
- case Opcodes.BIPUSH:
- case Opcodes.NEWARRAY:
- methodVisitor.visitIntInsn(opcode, classBuffer[currentOffset + 1]);
- currentOffset += 2;
- break;
- case Opcodes.SIPUSH:
- methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
- currentOffset += 3;
- break;
- case Opcodes.LDC:
- methodVisitor.visitLdcInsn(readConst(classBuffer[currentOffset + 1] & 0xFF, charBuffer));
- currentOffset += 2;
- break;
- case Constants.LDC_W:
- case Constants.LDC2_W:
- methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
- currentOffset += 3;
- break;
- case Opcodes.GETSTATIC:
- case Opcodes.PUTSTATIC:
- case Opcodes.GETFIELD:
- case Opcodes.PUTFIELD:
- case Opcodes.INVOKEVIRTUAL:
- case Opcodes.INVOKESPECIAL:
- case Opcodes.INVOKESTATIC:
- case Opcodes.INVOKEINTERFACE:
- {
- int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
- String owner = readClass(cpInfoOffset, charBuffer);
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
- if (opcode < Opcodes.INVOKEVIRTUAL) {
- methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
- } else {
- boolean isInterface =
- classBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
- methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
- }
- if (opcode == Opcodes.INVOKEINTERFACE) {
- currentOffset += 5;
- } else {
- currentOffset += 3;
- }
- break;
- }
- case Opcodes.INVOKEDYNAMIC:
- {
- int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
- int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
- Handle handle =
- (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
- Object[] bootstrapMethodArguments =
- new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
- bootstrapMethodOffset += 4;
- for (int i = 0; i < bootstrapMethodArguments.length; i++) {
- bootstrapMethodArguments[i] =
- readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
- bootstrapMethodOffset += 2;
- }
- methodVisitor.visitInvokeDynamicInsn(
- name, descriptor, handle, bootstrapMethodArguments);
- currentOffset += 5;
- break;
- }
- case Opcodes.NEW:
- case Opcodes.ANEWARRAY:
- case Opcodes.CHECKCAST:
- case Opcodes.INSTANCEOF:
- methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
- currentOffset += 3;
- break;
- case Opcodes.IINC:
- methodVisitor.visitIincInsn(
- classBuffer[currentOffset + 1] & 0xFF, classBuffer[currentOffset + 2]);
- currentOffset += 3;
- break;
- case Opcodes.MULTIANEWARRAY:
- methodVisitor.visitMultiANewArrayInsn(
- readClass(currentOffset + 1, charBuffer), classBuffer[currentOffset + 3] & 0xFF);
- currentOffset += 4;
- break;
- default:
- throw new AssertionError();
- }
-
- // Visit the runtime visible instruction annotations, if any.
- while (visibleTypeAnnotationOffsets != null
- && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length
- && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
- if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
- // Parse the target_type, target_info and target_path fields.
- int currentAnnotationOffset =
- readTypeAnnotationTarget(
- context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]);
- // 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.
- readElementValues(
- methodVisitor.visitInsnAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- annotationDescriptor,
- /* visible = */ true),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- currentVisibleTypeAnnotationBytecodeOffset =
- getTypeAnnotationBytecodeOffset(
- visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex);
- }
-
- // Visit the runtime invisible instruction annotations, if any.
- while (invisibleTypeAnnotationOffsets != null
- && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length
- && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
- if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
- // Parse the target_type, target_info and target_path fields.
- int currentAnnotationOffset =
- readTypeAnnotationTarget(
- context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]);
- // 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.
- readElementValues(
- methodVisitor.visitInsnAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- annotationDescriptor,
- /* visible = */ false),
- currentAnnotationOffset,
- /* named = */ true,
- charBuffer);
- }
- currentInvisibleTypeAnnotationBytecodeOffset =
- getTypeAnnotationBytecodeOffset(
- invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex);
- }
- }
- if (labels[codeLength] != null) {
- methodVisitor.visitLabel(labels[codeLength]);
- }
-
- // Visit LocalVariableTable and LocalVariableTypeTable attributes.
- if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
- // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable.
- int[] typeTable = null;
- if (localVariableTypeTableOffset != 0) {
- typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3];
- currentOffset = localVariableTypeTableOffset + 2;
- int typeTableIndex = typeTable.length;
- while (typeTableIndex > 0) {
- // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'.
- typeTable[--typeTableIndex] = currentOffset + 6;
- typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8);
- typeTable[--typeTableIndex] = readUnsignedShort(currentOffset);
- currentOffset += 10;
- }
- }
- int localVariableTableLength = readUnsignedShort(localVariableTableOffset);
- currentOffset = localVariableTableOffset + 2;
- while (localVariableTableLength-- > 0) {
- int startPc = readUnsignedShort(currentOffset);
- int length = readUnsignedShort(currentOffset + 2);
- String name = readUTF8(currentOffset + 4, charBuffer);
- String descriptor = readUTF8(currentOffset + 6, charBuffer);
- int index = readUnsignedShort(currentOffset + 8);
- currentOffset += 10;
- String signature = null;
- if (typeTable != null) {
- for (int i = 0; i < typeTable.length; i += 3) {
- if (typeTable[i] == startPc && typeTable[i + 1] == index) {
- signature = readUTF8(typeTable[i + 2], charBuffer);
- break;
+ throw new IllegalArgumentException();
}
- }
+ currentCpInfoOffset += cpInfoSize;
}
- methodVisitor.visitLocalVariable(
- name, descriptor, signature, labels[startPc], labels[startPc + length], index);
- }
+ maxStringLength = currentMaxStringLength;
+ // The Classfile's access_flags field is just after the last constant pool entry.
+ header = currentCpInfoOffset;
+
+ // Allocate the cache of ConstantDynamic values, if there is at least one.
+ constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null;
+
+ // Read the BootstrapMethods attribute, if any (only get the offset of each method).
+ bootstrapMethodOffsets =
+ hasBootstrapMethods ? readBootstrapMethodsAttribute(currentMaxStringLength) : null;
}
- // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute.
- if (visibleTypeAnnotationOffsets != null) {
- for (int typeAnnotationOffset : visibleTypeAnnotationOffsets) {
- int targetType = readByte(typeAnnotationOffset);
- if (targetType == TypeReference.LOCAL_VARIABLE
- || targetType == TypeReference.RESOURCE_VARIABLE) {
- // Parse the target_type, target_info and target_path fields.
- currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
- currentOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- readElementValues(
- methodVisitor.visitLocalVariableAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- context.currentLocalVariableAnnotationRangeStarts,
- context.currentLocalVariableAnnotationRangeEnds,
- context.currentLocalVariableAnnotationRangeIndices,
- annotationDescriptor,
- /* visible = */ true),
- currentOffset,
- /* named = */ true,
- charBuffer);
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param inputStream an input stream of the JVMS ClassFile structure to be read. This input
+ * stream must contain nothing more than the ClassFile structure itself. It is read from its
+ * current position to its end.
+ * @throws IOException if a problem occurs during reading.
+ */
+ public ClassReader(final InputStream inputStream) throws IOException {
+ this(readStream(inputStream, false));
+ }
+
+ /**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param className the fully qualified name of the class to be read. The ClassFile structure is
+ * retrieved with the current class loader's {@link ClassLoader#getSystemResourceAsStream}.
+ * @throws IOException if an exception occurs during reading.
+ */
+ public ClassReader(final String className) throws IOException {
+ this(
+ readStream(
+ ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true));
+ }
+
+ /**
+ * Reads the given input stream and returns its content as a byte array.
+ *
+ * @param inputStream an input stream.
+ * @param close true to close the input stream after reading.
+ * @return the content of the given input stream.
+ * @throws IOException if a problem occurs during reading.
+ */
+ private static byte[] readStream(final InputStream inputStream, final boolean close)
+ throws IOException {
+ if (inputStream == null) {
+ throw new IOException("Class not found");
}
- }
- }
-
- // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute.
- if (invisibleTypeAnnotationOffsets != null) {
- for (int typeAnnotationOffset : invisibleTypeAnnotationOffsets) {
- int targetType = readByte(typeAnnotationOffset);
- if (targetType == TypeReference.LOCAL_VARIABLE
- || targetType == TypeReference.RESOURCE_VARIABLE) {
- // Parse the target_type, target_info and target_path fields.
- currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
- currentOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- readElementValues(
- methodVisitor.visitLocalVariableAnnotation(
- context.currentTypeAnnotationTarget,
- context.currentTypeAnnotationTargetPath,
- context.currentLocalVariableAnnotationRangeStarts,
- context.currentLocalVariableAnnotationRangeEnds,
- context.currentLocalVariableAnnotationRangeIndices,
- annotationDescriptor,
- /* visible = */ false),
- currentOffset,
- /* named = */ true,
- charBuffer);
+ 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) {
+ outputStream.write(data, 0, bytesRead);
+ }
+ outputStream.flush();
+ return outputStream.toByteArray();
+ } finally {
+ if (close) {
+ inputStream.close();
+ }
}
- }
}
- // Visit the non standard attributes.
- while (attributes != null) {
- // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
- Attribute nextAttribute = attributes.nextAttribute;
- attributes.nextAttribute = null;
- methodVisitor.visitAttribute(attributes);
- attributes = nextAttribute;
+ // -----------------------------------------------------------------------------------------------
+ // Accessors
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the class's access flags (see {@link Opcodes}). This value may not reflect Deprecated
+ * and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes.
+ *
+ * @return the class access flags.
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public int getAccess() {
+ return readUnsignedShort(header);
}
- // Visit the max stack and max locals values.
- methodVisitor.visitMaxs(maxStack, maxLocals);
- }
-
- /**
- * Returns the label corresponding to the given bytecode offset. The default implementation of
- * this method creates a label for the given offset if it has not been already created.
- *
- * @param bytecodeOffset a bytecode offset in a method.
- * @param labels the already created labels, indexed by their offset. If a label already exists
- * for bytecodeOffset this method must not create a new one. Otherwise it must store the new
- * label in this array.
- * @return a non null Label, which must be equal to labels[bytecodeOffset].
- */
- protected Label readLabel(final int bytecodeOffset, final Label[] labels) {
- if (labels[bytecodeOffset] == null) {
- labels[bytecodeOffset] = new Label();
+ /**
+ * Returns the internal name of the class (see {@link Type#getInternalName()}).
+ *
+ * @return the internal class name.
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public String getClassName() {
+ // this_class is just after the access_flags field (using 2 bytes).
+ return readClass(header + 2, new char[maxStringLength]);
}
- return labels[bytecodeOffset];
- }
- /**
- * Creates a label without the {@link Label#FLAG_DEBUG_ONLY} flag set, for the given bytecode
- * offset. The label is created with a call to {@link #readLabel} and its {@link
- * Label#FLAG_DEBUG_ONLY} flag is cleared.
- *
- * @param bytecodeOffset a bytecode offset in a method.
- * @param labels the already created labels, indexed by their offset.
- * @return a Label without the {@link Label#FLAG_DEBUG_ONLY} flag set.
- */
- private Label createLabel(final int bytecodeOffset, final Label[] labels) {
- Label label = readLabel(bytecodeOffset, labels);
- label.flags &= ~Label.FLAG_DEBUG_ONLY;
- return label;
- }
-
- /**
- * Creates a label with the {@link Label#FLAG_DEBUG_ONLY} flag set, if there is no already
- * existing label for the given bytecode offset (otherwise does nothing). The label is created
- * with a call to {@link #readLabel}.
- *
- * @param bytecodeOffset a bytecode offset in a method.
- * @param labels the already created labels, indexed by their offset.
- */
- private void createDebugLabel(final int bytecodeOffset, final Label[] labels) {
- if (labels[bytecodeOffset] == null) {
- readLabel(bytecodeOffset, labels).flags |= Label.FLAG_DEBUG_ONLY;
+ /**
+ * Returns the internal of name of the super class (see {@link Type#getInternalName()}). For
+ * interfaces, the super class is {@link Object}.
+ *
+ * @return the internal name of the super class, or {@literal null} for {@link Object} class.
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public String getSuperName() {
+ // super_class is after the access_flags and this_class fields (2 bytes each).
+ return readClass(header + 4, new char[maxStringLength]);
}
- }
- // ----------------------------------------------------------------------------------------------
- // Methods to parse annotations, type annotations and parameter annotations
- // ----------------------------------------------------------------------------------------------
+ /**
+ * Returns the internal names of the implemented interfaces (see {@link Type#getInternalName()}).
+ *
+ * @return the internal names of the directly implemented interfaces. Inherited implemented
+ * interfaces are not returned.
+ * @see ClassVisitor#visit(int, int, String, String, String, String[])
+ */
+ public String[] getInterfaces() {
+ // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each).
+ int currentOffset = header + 6;
+ int interfacesCount = readUnsignedShort(currentOffset);
+ String[] interfaces = new String[interfacesCount];
+ if (interfacesCount > 0) {
+ char[] charBuffer = new char[maxStringLength];
+ for (int i = 0; i < interfacesCount; ++i) {
+ currentOffset += 2;
+ interfaces[i] = readClass(currentOffset, charBuffer);
+ }
+ }
+ return interfaces;
+ }
- /**
- * Parses a Runtime[In]VisibleTypeAnnotations attribute to find the offset of each type_annotation
- * entry it contains, to find the corresponding labels, and to visit the try catch block
- * annotations.
- *
- * @param methodVisitor the method visitor to be used to visit the try catch block annotations.
- * @param context information about the class being parsed.
- * @param runtimeTypeAnnotationsOffset the start offset of a Runtime[In]VisibleTypeAnnotations
- * attribute, excluding the attribute_info's attribute_name_index and attribute_length fields.
- * @param visible true if the attribute to parse is a RuntimeVisibleTypeAnnotations attribute,
- * false it is a RuntimeInvisibleTypeAnnotations attribute.
- * @return the start offset of each entry of the Runtime[In]VisibleTypeAnnotations_attribute's
- * 'annotations' array field.
- */
- private int[] readTypeAnnotations(
- final MethodVisitor methodVisitor,
- final Context context,
- final int runtimeTypeAnnotationsOffset,
- final boolean visible) {
- char[] charBuffer = context.charBuffer;
- int currentOffset = runtimeTypeAnnotationsOffset;
- // Read the num_annotations field and create an array to store the type_annotation offsets.
- int[] typeAnnotationsOffsets = new int[readUnsignedShort(currentOffset)];
- currentOffset += 2;
- // Parse the 'annotations' array field.
- for (int i = 0; i < typeAnnotationsOffsets.length; ++i) {
- typeAnnotationsOffsets[i] = currentOffset;
- // Parse the type_annotation's target_type and the target_info fields. The size of the
- // target_info field depends on the value of target_type.
- int targetType = readInt(currentOffset);
- switch (targetType >>> 24) {
+ // -----------------------------------------------------------------------------------------------
+ // Public methods
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
+ * {@link ClassReader}.
+ *
+ * @param classVisitor the visitor that must visit this class.
+ * @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}.
+ */
+ public void accept(final ClassVisitor classVisitor, final int parsingOptions) {
+ accept(classVisitor, new Attribute[0], parsingOptions);
+ }
+
+ /**
+ * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
+ * {@link ClassReader}.
+ *
+ * @param classVisitor the visitor that must visit this class.
+ * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
+ * the class. Any attribute whose type is not equal to the type of one the prototypes
+ * will not
+ * be parsed: its byte array value will be passed unchanged to the ClassWriter.
+ * This may
+ * corrupt it if this value contains references to the constant pool, or has syntactic or
+ * semantic links with a class element that has been transformed by a class adapter
+ * between
+ * the reader and the writer.
+ * @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}.
+ */
+ public void accept(
+ final ClassVisitor classVisitor,
+ final Attribute[] attributePrototypes,
+ final int parsingOptions) {
+ Context context = new Context();
+ context.attributePrototypes = attributePrototypes;
+ context.parsingOptions = parsingOptions;
+ context.charBuffer = new char[maxStringLength];
+
+ // Read the access_flags, this_class, super_class, interface_count and interfaces fields.
+ char[] charBuffer = context.charBuffer;
+ int currentOffset = header;
+ int accessFlags = readUnsignedShort(currentOffset);
+ String thisClass = readClass(currentOffset + 2, charBuffer);
+ String superClass = readClass(currentOffset + 4, charBuffer);
+ String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
+ currentOffset += 8;
+ for (int i = 0; i < interfaces.length; ++i) {
+ interfaces[i] = readClass(currentOffset, charBuffer);
+ currentOffset += 2;
+ }
+
+ // Read the class 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 offset of the InnerClasses attribute, or 0.
+ int innerClassesOffset = 0;
+ // - The offset of the EnclosingMethod attribute, or 0.
+ int enclosingMethodOffset = 0;
+ // - The string corresponding to the Signature attribute, or null.
+ String signature = null;
+ // - The string corresponding to the SourceFile attribute, or null.
+ String sourceFile = null;
+ // - The string corresponding to the SourceDebugExtension attribute, or null.
+ String sourceDebugExtension = 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 offset of the Module attribute, or 0.
+ int moduleOffset = 0;
+ // - The offset of the ModulePackages attribute, or 0.
+ int modulePackagesOffset = 0;
+ // - The string corresponding to the ModuleMainClass attribute, or null.
+ String moduleMainClass = null;
+ // - The string corresponding to the NestHost attribute, or null.
+ String nestHostClass = null;
+ // - The offset of the NestMembers attribute, or 0.
+ int nestMembersOffset = 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 currentAttributeOffset = getFirstAttributeOffset();
+ for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
+ // Read the attribute_info's attribute_name and attribute_length fields.
+ String attributeName = readUTF8(currentAttributeOffset, charBuffer);
+ int attributeLength = readInt(currentAttributeOffset + 2);
+ currentAttributeOffset += 6;
+ // The tests are sorted in decreasing frequency order (based on frequencies observed on
+ // typical classes).
+ if (Constants.SOURCE_FILE.equals(attributeName)) {
+ sourceFile = readUTF8(currentAttributeOffset, charBuffer);
+ } else if (Constants.INNER_CLASSES.equals(attributeName)) {
+ innerClassesOffset = currentAttributeOffset;
+ } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) {
+ enclosingMethodOffset = currentAttributeOffset;
+ } else if (Constants.NEST_HOST.equals(attributeName)) {
+ nestHostClass = readClass(currentAttributeOffset, charBuffer);
+ } else if (Constants.NEST_MEMBERS.equals(attributeName)) {
+ nestMembersOffset = currentAttributeOffset;
+ } else if (Constants.SIGNATURE.equals(attributeName)) {
+ signature = readUTF8(currentAttributeOffset, charBuffer);
+ } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
+ runtimeVisibleAnnotationsOffset = currentAttributeOffset;
+ } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+ runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset;
+ } else if (Constants.DEPRECATED.equals(attributeName)) {
+ accessFlags |= Opcodes.ACC_DEPRECATED;
+ } else if (Constants.SYNTHETIC.equals(attributeName)) {
+ accessFlags |= Opcodes.ACC_SYNTHETIC;
+ } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
+ sourceDebugExtension =
+ readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
+ } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
+ runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
+ } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+ runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
+ } else if (Constants.MODULE.equals(attributeName)) {
+ moduleOffset = currentAttributeOffset;
+ } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
+ moduleMainClass = readClass(currentAttributeOffset, charBuffer);
+ } else if (Constants.MODULE_PACKAGES.equals(attributeName)) {
+ modulePackagesOffset = currentAttributeOffset;
+ } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
+ // The BootstrapMethods attribute is read in the constructor.
+ Attribute attribute =
+ readAttribute(
+ attributePrototypes,
+ attributeName,
+ currentAttributeOffset,
+ attributeLength,
+ charBuffer,
+ -1,
+ null);
+ attribute.nextAttribute = attributes;
+ attributes = attribute;
+ }
+ currentAttributeOffset += attributeLength;
+ }
+
+ // Visit the class declaration. The minor_version and major_version fields start 6 bytes before
+ // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
+ classVisitor.visit(
+ readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);
+
+ // Visit the SourceFile and SourceDebugExtenstion attributes.
+ if ((parsingOptions & SKIP_DEBUG) == 0
+ && (sourceFile != null || sourceDebugExtension != null)) {
+ classVisitor.visitSource(sourceFile, sourceDebugExtension);
+ }
+
+ // Visit the Module, ModulePackages and ModuleMainClass attributes.
+ if (moduleOffset != 0) {
+ readModuleAttributes(
+ classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
+ }
+
+ // Visit the NestHost attribute.
+ if (nestHostClass != null) {
+ classVisitor.visitNestHost(nestHostClass);
+ }
+
+ // Visit the EnclosingMethod attribute.
+ if (enclosingMethodOffset != 0) {
+ String className = readClass(enclosingMethodOffset, charBuffer);
+ int methodIndex = readUnsignedShort(enclosingMethodOffset + 2);
+ String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer);
+ String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer);
+ classVisitor.visitOuterClass(className, name, type);
+ }
+
+ // 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(
+ classVisitor.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(
+ classVisitor.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(
+ classVisitor.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(
+ classVisitor.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 ClassWriter.
+ Attribute nextAttribute = attributes.nextAttribute;
+ attributes.nextAttribute = null;
+ classVisitor.visitAttribute(attributes);
+ attributes = nextAttribute;
+ }
+
+ // Visit the NestedMembers attribute.
+ if (nestMembersOffset != 0) {
+ int numberOfNestMembers = readUnsignedShort(nestMembersOffset);
+ int currentNestMemberOffset = nestMembersOffset + 2;
+ while (numberOfNestMembers-- > 0) {
+ classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer));
+ currentNestMemberOffset += 2;
+ }
+ }
+
+ // Visit the InnerClasses attribute.
+ if (innerClassesOffset != 0) {
+ int numberOfClasses = readUnsignedShort(innerClassesOffset);
+ int currentClassesOffset = innerClassesOffset + 2;
+ while (numberOfClasses-- > 0) {
+ classVisitor.visitInnerClass(
+ readClass(currentClassesOffset, charBuffer),
+ readClass(currentClassesOffset + 2, charBuffer),
+ readUTF8(currentClassesOffset + 4, charBuffer),
+ readUnsignedShort(currentClassesOffset + 6));
+ currentClassesOffset += 8;
+ }
+ }
+
+ // Visit the fields and methods.
+ int fieldsCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (fieldsCount-- > 0) {
+ currentOffset = readField(classVisitor, context, currentOffset);
+ }
+ int methodsCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (methodsCount-- > 0) {
+ currentOffset = readMethod(classVisitor, context, currentOffset);
+ }
+
+ // Visit the end of the class.
+ classVisitor.visitEnd();
+ }
+
+ // ----------------------------------------------------------------------------------------------
+ // Methods to parse modules, fields and methods
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * Reads the Module, ModulePackages and ModuleMainClass attributes and visit them.
+ *
+ * @param classVisitor the current class visitor
+ * @param context information about the class being parsed.
+ * @param moduleOffset the offset of the Module attribute (excluding the attribute_info's
+ * 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.
+ */
+ private void readModuleAttributes(
+ final ClassVisitor classVisitor,
+ final Context context,
+ final int moduleOffset,
+ final int modulePackagesOffset,
+ final String moduleMainClass) {
+ char[] buffer = context.charBuffer;
+
+ // Read the module_name_index, module_flags and module_version_index fields and visit them.
+ int currentOffset = moduleOffset;
+ String moduleName = readModule(currentOffset, buffer);
+ int moduleFlags = readUnsignedShort(currentOffset + 2);
+ String moduleVersion = readUTF8(currentOffset + 4, buffer);
+ currentOffset += 6;
+ ModuleVisitor moduleVisitor = classVisitor.visitModule(moduleName, moduleFlags, moduleVersion);
+ if (moduleVisitor == null) {
+ return;
+ }
+
+ // Visit the ModuleMainClass attribute.
+ if (moduleMainClass != null) {
+ moduleVisitor.visitMainClass(moduleMainClass);
+ }
+
+ // Visit the ModulePackages attribute.
+ if (modulePackagesOffset != 0) {
+ int packageCount = readUnsignedShort(modulePackagesOffset);
+ int currentPackageOffset = modulePackagesOffset + 2;
+ while (packageCount-- > 0) {
+ moduleVisitor.visitPackage(readPackage(currentPackageOffset, buffer));
+ currentPackageOffset += 2;
+ }
+ }
+
+ // Read the 'requires_count' and 'requires' fields.
+ int requiresCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (requiresCount-- > 0) {
+ // Read the requires_index, requires_flags and requires_version fields and visit them.
+ String requires = readModule(currentOffset, buffer);
+ int requiresFlags = readUnsignedShort(currentOffset + 2);
+ String requiresVersion = readUTF8(currentOffset + 4, buffer);
+ currentOffset += 6;
+ moduleVisitor.visitRequire(requires, requiresFlags, requiresVersion);
+ }
+
+ // Read the 'exports_count' and 'exports' fields.
+ int exportsCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (exportsCount-- > 0) {
+ // Read the exports_index, exports_flags, exports_to_count and exports_to_index fields
+ // and visit them.
+ String exports = readPackage(currentOffset, buffer);
+ int exportsFlags = readUnsignedShort(currentOffset + 2);
+ int exportsToCount = readUnsignedShort(currentOffset + 4);
+ currentOffset += 6;
+ String[] exportsTo = null;
+ if (exportsToCount != 0) {
+ exportsTo = new String[exportsToCount];
+ for (int i = 0; i < exportsToCount; ++i) {
+ exportsTo[i] = readModule(currentOffset, buffer);
+ currentOffset += 2;
+ }
+ }
+ moduleVisitor.visitExport(exports, exportsFlags, exportsTo);
+ }
+
+ // Reads the 'opens_count' and 'opens' fields.
+ int opensCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (opensCount-- > 0) {
+ // Read the opens_index, opens_flags, opens_to_count and opens_to_index fields and visit them.
+ String opens = readPackage(currentOffset, buffer);
+ int opensFlags = readUnsignedShort(currentOffset + 2);
+ int opensToCount = readUnsignedShort(currentOffset + 4);
+ currentOffset += 6;
+ String[] opensTo = null;
+ if (opensToCount != 0) {
+ opensTo = new String[opensToCount];
+ for (int i = 0; i < opensToCount; ++i) {
+ opensTo[i] = readModule(currentOffset, buffer);
+ currentOffset += 2;
+ }
+ }
+ moduleVisitor.visitOpen(opens, opensFlags, opensTo);
+ }
+
+ // Read the 'uses_count' and 'uses' fields.
+ int usesCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (usesCount-- > 0) {
+ moduleVisitor.visitUse(readClass(currentOffset, buffer));
+ currentOffset += 2;
+ }
+
+ // Read the 'provides_count' and 'provides' fields.
+ int providesCount = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (providesCount-- > 0) {
+ // Read the provides_index, provides_with_count and provides_with_index fields and visit them.
+ String provides = readClass(currentOffset, buffer);
+ int providesWithCount = readUnsignedShort(currentOffset + 2);
+ currentOffset += 4;
+ String[] providesWith = new String[providesWithCount];
+ for (int i = 0; i < providesWithCount; ++i) {
+ providesWith[i] = readClass(currentOffset, buffer);
+ currentOffset += 2;
+ }
+ moduleVisitor.visitProvide(provides, providesWith);
+ }
+
+ // Visit the end of the module attributes.
+ moduleVisitor.visitEnd();
+ }
+
+ /**
+ * Reads a JVMS field_info structure and makes the given visitor visit it.
+ *
+ * @param classVisitor the visitor that must visit the field.
+ * @param context information about the class being parsed.
+ * @param fieldInfoOffset the start offset of the field_info structure.
+ * @return the offset of the first byte following the field_info structure.
+ */
+ private int readField(
+ final ClassVisitor classVisitor, final Context context, final int fieldInfoOffset) {
+ char[] charBuffer = context.charBuffer;
+
+ // Read the access_flags, name_index and descriptor_index fields.
+ int currentOffset = fieldInfoOffset;
+ int accessFlags = readUnsignedShort(currentOffset);
+ String name = readUTF8(currentOffset + 2, charBuffer);
+ String descriptor = readUTF8(currentOffset + 4, charBuffer);
+ currentOffset += 6;
+
+ // Read the field 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 value corresponding to the ConstantValue attribute, or null.
+ Object constantValue = null;
+ // - 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.CONSTANT_VALUE.equals(attributeName)) {
+ int constantvalueIndex = readUnsignedShort(currentOffset);
+ constantValue = constantvalueIndex == 0 ? null : readConst(constantvalueIndex, charBuffer);
+ } else if (Constants.SIGNATURE.equals(attributeName)) {
+ signature = readUTF8(currentOffset, charBuffer);
+ } else if (Constants.DEPRECATED.equals(attributeName)) {
+ accessFlags |= Opcodes.ACC_DEPRECATED;
+ } else if (Constants.SYNTHETIC.equals(attributeName)) {
+ accessFlags |= Opcodes.ACC_SYNTHETIC;
+ } 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;
+ }
+
+ // Visit the field declaration.
+ FieldVisitor fieldVisitor =
+ classVisitor.visitField(accessFlags, name, descriptor, signature, constantValue);
+ if (fieldVisitor == 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(
+ fieldVisitor.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(
+ fieldVisitor.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(
+ fieldVisitor.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(
+ fieldVisitor.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;
+ fieldVisitor.visitAttribute(attributes);
+ attributes = nextAttribute;
+ }
+
+ // Visit the end of the field.
+ fieldVisitor.visitEnd();
+ return currentOffset;
+ }
+
+ /**
+ * Reads a JVMS method_info structure and makes the given visitor visit it.
+ *
+ * @param classVisitor the visitor that must visit the method.
+ * @param context information about the class being parsed.
+ * @param methodInfoOffset the start offset of the method_info structure.
+ * @return the offset of the first byte following the method_info structure.
+ */
+ private int readMethod(
+ final ClassVisitor classVisitor, final Context context, final int methodInfoOffset) {
+ char[] charBuffer = context.charBuffer;
+
+ // Read the access_flags, name_index and descriptor_index fields.
+ int currentOffset = methodInfoOffset;
+ context.currentMethodAccessFlags = readUnsignedShort(currentOffset);
+ context.currentMethodName = readUTF8(currentOffset + 2, charBuffer);
+ context.currentMethodDescriptor = readUTF8(currentOffset + 4, charBuffer);
+ currentOffset += 6;
+
+ // Read the method 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 offset of the Code attribute, or 0.
+ int codeOffset = 0;
+ // - The offset of the Exceptions attribute, or 0.
+ int exceptionsOffset = 0;
+ // - The strings corresponding to the Exceptions attribute, or null.
+ String[] exceptions = null;
+ // - Whether the method has a Synthetic attribute.
+ boolean synthetic = false;
+ // - The constant pool index contained in the Signature attribute, or 0.
+ int signatureIndex = 0;
+ // - 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 RuntimeVisibleParameterAnnotations attribute, or 0.
+ int runtimeVisibleParameterAnnotationsOffset = 0;
+ // - The offset of the RuntimeInvisibleParameterAnnotations attribute, or 0.
+ int runtimeInvisibleParameterAnnotationsOffset = 0;
+ // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
+ int runtimeVisibleTypeAnnotationsOffset = 0;
+ // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
+ int runtimeInvisibleTypeAnnotationsOffset = 0;
+ // - The offset of the AnnotationDefault attribute, or 0.
+ int annotationDefaultOffset = 0;
+ // - The offset of the MethodParameters attribute, or 0.
+ int methodParametersOffset = 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.CODE.equals(attributeName)) {
+ if ((context.parsingOptions & SKIP_CODE) == 0) {
+ codeOffset = currentOffset;
+ }
+ } else if (Constants.EXCEPTIONS.equals(attributeName)) {
+ exceptionsOffset = currentOffset;
+ exceptions = new String[readUnsignedShort(exceptionsOffset)];
+ int currentExceptionOffset = exceptionsOffset + 2;
+ for (int i = 0; i < exceptions.length; ++i) {
+ exceptions[i] = readClass(currentExceptionOffset, charBuffer);
+ currentExceptionOffset += 2;
+ }
+ } else if (Constants.SIGNATURE.equals(attributeName)) {
+ signatureIndex = readUnsignedShort(currentOffset);
+ } else if (Constants.DEPRECATED.equals(attributeName)) {
+ context.currentMethodAccessFlags |= Opcodes.ACC_DEPRECATED;
+ } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
+ runtimeVisibleAnnotationsOffset = currentOffset;
+ } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+ runtimeVisibleTypeAnnotationsOffset = currentOffset;
+ } else if (Constants.ANNOTATION_DEFAULT.equals(attributeName)) {
+ annotationDefaultOffset = currentOffset;
+ } else if (Constants.SYNTHETIC.equals(attributeName)) {
+ synthetic = true;
+ context.currentMethodAccessFlags |= Opcodes.ACC_SYNTHETIC;
+ } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
+ runtimeInvisibleAnnotationsOffset = currentOffset;
+ } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+ runtimeInvisibleTypeAnnotationsOffset = currentOffset;
+ } else if (Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
+ runtimeVisibleParameterAnnotationsOffset = currentOffset;
+ } else if (Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
+ runtimeInvisibleParameterAnnotationsOffset = currentOffset;
+ } else if (Constants.METHOD_PARAMETERS.equals(attributeName)) {
+ methodParametersOffset = currentOffset;
+ } else {
+ Attribute attribute =
+ readAttribute(
+ context.attributePrototypes,
+ attributeName,
+ currentOffset,
+ attributeLength,
+ charBuffer,
+ -1,
+ null);
+ attribute.nextAttribute = attributes;
+ attributes = attribute;
+ }
+ currentOffset += attributeLength;
+ }
+
+ // Visit the method declaration.
+ MethodVisitor methodVisitor =
+ classVisitor.visitMethod(
+ context.currentMethodAccessFlags,
+ context.currentMethodName,
+ context.currentMethodDescriptor,
+ signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer),
+ exceptions);
+ if (methodVisitor == null) {
+ return currentOffset;
+ }
+
+ // If the returned MethodVisitor is in fact a MethodWriter, it means there is no method
+ // adapter between the reader and the writer. In this case, it might be possible to copy
+ // the method attributes directly into the writer. If so, return early without visiting
+ // the content of these attributes.
+ if (methodVisitor instanceof MethodWriter) {
+ MethodWriter methodWriter = (MethodWriter) methodVisitor;
+ if (methodWriter.canCopyMethodAttributes(
+ this,
+ 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) {
+ int parametersCount = readByte(methodParametersOffset);
+ int currentParameterOffset = methodParametersOffset + 1;
+ while (parametersCount-- > 0) {
+ // Read the name_index and access_flags fields and visit them.
+ methodVisitor.visitParameter(
+ readUTF8(currentParameterOffset, charBuffer),
+ readUnsignedShort(currentParameterOffset + 2));
+ currentParameterOffset += 4;
+ }
+ }
+
+ // Visit the AnnotationDefault attribute.
+ if (annotationDefaultOffset != 0) {
+ AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault();
+ readElementValue(annotationVisitor, annotationDefaultOffset, null, charBuffer);
+ if (annotationVisitor != null) {
+ annotationVisitor.visitEnd();
+ }
+ }
+
+ // 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(
+ methodVisitor.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(
+ methodVisitor.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(
+ methodVisitor.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(
+ methodVisitor.visitTypeAnnotation(
+ context.currentTypeAnnotationTarget,
+ context.currentTypeAnnotationTargetPath,
+ annotationDescriptor,
+ /* visible = */ false),
+ currentAnnotationOffset,
+ /* named = */ true,
+ charBuffer);
+ }
+ }
+
+ // Visit the RuntimeVisibleParameterAnnotations attribute.
+ if (runtimeVisibleParameterAnnotationsOffset != 0) {
+ readParameterAnnotations(
+ methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true);
+ }
+
+ // Visit the RuntimeInvisibleParameterAnnotations attribute.
+ if (runtimeInvisibleParameterAnnotationsOffset != 0) {
+ readParameterAnnotations(
+ methodVisitor,
+ context,
+ runtimeInvisibleParameterAnnotationsOffset,
+ /* visible = */ false);
+ }
+
+ // Visit the non standard attributes.
+ while (attributes != null) {
+ // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
+ Attribute nextAttribute = attributes.nextAttribute;
+ attributes.nextAttribute = null;
+ methodVisitor.visitAttribute(attributes);
+ attributes = nextAttribute;
+ }
+
+ // Visit the Code attribute.
+ if (codeOffset != 0) {
+ methodVisitor.visitCode();
+ readCode(methodVisitor, context, codeOffset);
+ }
+
+ // Visit the end of the method.
+ methodVisitor.visitEnd();
+ return currentOffset;
+ }
+
+ // ----------------------------------------------------------------------------------------------
+ // Methods to parse a Code attribute
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * Reads a JVMS 'Code' attribute and makes the given visitor visit it.
+ *
+ * @param methodVisitor the visitor that must visit the Code attribute.
+ * @param context information about the class being parsed.
+ * @param codeOffset the start offset in {@link #classFileBuffer} of the Code attribute, excluding
+ * its attribute_name_index and attribute_length fields.
+ */
+ private void readCode(
+ final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
+ int currentOffset = codeOffset;
+
+ // Read the max_stack, max_locals and code_length fields.
+ final byte[] classBuffer = classFileBuffer;
+ final char[] charBuffer = context.charBuffer;
+ final int majorVersion = readUnsignedShort(6);
+ final int minorVersion = readUnsignedShort(4);
+
+ final int maxStack;
+ final int maxLocals;
+ final int codeLength;
+
+ if (majorVersion == 45 && minorVersion <= 2) {
+ maxStack = readByte(currentOffset);
+ maxLocals = readByte(currentOffset + 1);
+ codeLength = readUnsignedShort(currentOffset + 2);
+ currentOffset += 4;
+ } else {
+ maxStack = readUnsignedShort(currentOffset);
+ maxLocals = readUnsignedShort(currentOffset + 2);
+ codeLength = readInt(currentOffset + 4);
+ currentOffset += 8;
+ }
+
+ // Read the bytecode 'code' array to create a label for each referenced instruction.
+ final int bytecodeStartOffset = currentOffset;
+ final int bytecodeEndOffset = currentOffset + codeLength;
+ final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
+ while (currentOffset < bytecodeEndOffset) {
+ final int bytecodeOffset = currentOffset - bytecodeStartOffset;
+ final int opcode = classBuffer[currentOffset] & 0xFF;
+ switch (opcode) {
+ case Opcodes.NOP:
+ case Opcodes.ACONST_NULL:
+ case Opcodes.ICONST_M1:
+ case Opcodes.ICONST_0:
+ case Opcodes.ICONST_1:
+ case Opcodes.ICONST_2:
+ case Opcodes.ICONST_3:
+ case Opcodes.ICONST_4:
+ case Opcodes.ICONST_5:
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ case Opcodes.IALOAD:
+ case Opcodes.LALOAD:
+ case Opcodes.FALOAD:
+ case Opcodes.DALOAD:
+ case Opcodes.AALOAD:
+ case Opcodes.BALOAD:
+ case Opcodes.CALOAD:
+ case Opcodes.SALOAD:
+ case Opcodes.IASTORE:
+ case Opcodes.LASTORE:
+ case Opcodes.FASTORE:
+ case Opcodes.DASTORE:
+ case Opcodes.AASTORE:
+ case Opcodes.BASTORE:
+ case Opcodes.CASTORE:
+ case Opcodes.SASTORE:
+ case Opcodes.POP:
+ case Opcodes.POP2:
+ case Opcodes.DUP:
+ case Opcodes.DUP_X1:
+ case Opcodes.DUP_X2:
+ case Opcodes.DUP2:
+ case Opcodes.DUP2_X1:
+ case Opcodes.DUP2_X2:
+ case Opcodes.SWAP:
+ case Opcodes.IADD:
+ case Opcodes.LADD:
+ case Opcodes.FADD:
+ case Opcodes.DADD:
+ case Opcodes.ISUB:
+ case Opcodes.LSUB:
+ case Opcodes.FSUB:
+ case Opcodes.DSUB:
+ case Opcodes.IMUL:
+ case Opcodes.LMUL:
+ case Opcodes.FMUL:
+ case Opcodes.DMUL:
+ case Opcodes.IDIV:
+ case Opcodes.LDIV:
+ case Opcodes.FDIV:
+ case Opcodes.DDIV:
+ case Opcodes.IREM:
+ case Opcodes.LREM:
+ case Opcodes.FREM:
+ case Opcodes.DREM:
+ case Opcodes.INEG:
+ case Opcodes.LNEG:
+ case Opcodes.FNEG:
+ case Opcodes.DNEG:
+ case Opcodes.ISHL:
+ case Opcodes.LSHL:
+ case Opcodes.ISHR:
+ case Opcodes.LSHR:
+ case Opcodes.IUSHR:
+ case Opcodes.LUSHR:
+ case Opcodes.IAND:
+ case Opcodes.LAND:
+ case Opcodes.IOR:
+ case Opcodes.LOR:
+ case Opcodes.IXOR:
+ case Opcodes.LXOR:
+ case Opcodes.I2L:
+ case Opcodes.I2F:
+ case Opcodes.I2D:
+ case Opcodes.L2I:
+ case Opcodes.L2F:
+ case Opcodes.L2D:
+ case Opcodes.F2I:
+ case Opcodes.F2L:
+ case Opcodes.F2D:
+ case Opcodes.D2I:
+ case Opcodes.D2L:
+ case Opcodes.D2F:
+ case Opcodes.I2B:
+ case Opcodes.I2C:
+ case Opcodes.I2S:
+ case Opcodes.LCMP:
+ case Opcodes.FCMPL:
+ case Opcodes.FCMPG:
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG:
+ case Opcodes.IRETURN:
+ case Opcodes.LRETURN:
+ case Opcodes.FRETURN:
+ case Opcodes.DRETURN:
+ case Opcodes.ARETURN:
+ case Opcodes.RETURN:
+ case Opcodes.ARRAYLENGTH:
+ case Opcodes.ATHROW:
+ case Opcodes.MONITORENTER:
+ case Opcodes.MONITOREXIT:
+ case Constants.ILOAD_0:
+ case Constants.ILOAD_1:
+ case Constants.ILOAD_2:
+ case Constants.ILOAD_3:
+ case Constants.LLOAD_0:
+ case Constants.LLOAD_1:
+ case Constants.LLOAD_2:
+ case Constants.LLOAD_3:
+ case Constants.FLOAD_0:
+ case Constants.FLOAD_1:
+ case Constants.FLOAD_2:
+ case Constants.FLOAD_3:
+ case Constants.DLOAD_0:
+ case Constants.DLOAD_1:
+ case Constants.DLOAD_2:
+ case Constants.DLOAD_3:
+ case Constants.ALOAD_0:
+ case Constants.ALOAD_1:
+ case Constants.ALOAD_2:
+ case Constants.ALOAD_3:
+ case Constants.ISTORE_0:
+ case Constants.ISTORE_1:
+ case Constants.ISTORE_2:
+ case Constants.ISTORE_3:
+ case Constants.LSTORE_0:
+ case Constants.LSTORE_1:
+ case Constants.LSTORE_2:
+ case Constants.LSTORE_3:
+ case Constants.FSTORE_0:
+ case Constants.FSTORE_1:
+ case Constants.FSTORE_2:
+ case Constants.FSTORE_3:
+ case Constants.DSTORE_0:
+ case Constants.DSTORE_1:
+ case Constants.DSTORE_2:
+ case Constants.DSTORE_3:
+ case Constants.ASTORE_0:
+ case Constants.ASTORE_1:
+ case Constants.ASTORE_2:
+ case Constants.ASTORE_3:
+ currentOffset += 1;
+ break;
+ case Opcodes.IFEQ:
+ case Opcodes.IFNE:
+ case Opcodes.IFLT:
+ case Opcodes.IFGE:
+ case Opcodes.IFGT:
+ case Opcodes.IFLE:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ACMPEQ:
+ case Opcodes.IF_ACMPNE:
+ case Opcodes.GOTO:
+ case Opcodes.JSR:
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL:
+ createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
+ currentOffset += 3;
+ break;
+ case Constants.ASM_IFEQ:
+ case Constants.ASM_IFNE:
+ case Constants.ASM_IFLT:
+ case Constants.ASM_IFGE:
+ case Constants.ASM_IFGT:
+ case Constants.ASM_IFLE:
+ case Constants.ASM_IF_ICMPEQ:
+ case Constants.ASM_IF_ICMPNE:
+ case Constants.ASM_IF_ICMPLT:
+ case Constants.ASM_IF_ICMPGE:
+ case Constants.ASM_IF_ICMPGT:
+ case Constants.ASM_IF_ICMPLE:
+ case Constants.ASM_IF_ACMPEQ:
+ case Constants.ASM_IF_ACMPNE:
+ case Constants.ASM_GOTO:
+ case Constants.ASM_JSR:
+ case Constants.ASM_IFNULL:
+ case Constants.ASM_IFNONNULL:
+ createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels);
+ currentOffset += 3;
+ break;
+ case Constants.GOTO_W:
+ case Constants.JSR_W:
+ case Constants.ASM_GOTO_W:
+ createLabel(bytecodeOffset + readInt(currentOffset + 1), labels);
+ currentOffset += 5;
+ break;
+ case Constants.WIDE:
+ switch (classBuffer[currentOffset + 1] & 0xFF) {
+ case Opcodes.ILOAD:
+ case Opcodes.FLOAD:
+ case Opcodes.ALOAD:
+ case Opcodes.LLOAD:
+ case Opcodes.DLOAD:
+ case Opcodes.ISTORE:
+ case Opcodes.FSTORE:
+ case Opcodes.ASTORE:
+ case Opcodes.LSTORE:
+ case Opcodes.DSTORE:
+ case Opcodes.RET:
+ currentOffset += 4;
+ break;
+ case Opcodes.IINC:
+ currentOffset += 6;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ break;
+ case Opcodes.TABLESWITCH:
+ // Skip 0 to 3 padding bytes.
+ currentOffset += 4 - (bytecodeOffset & 3);
+ // Read the default label and the number of table entries.
+ createLabel(bytecodeOffset + readInt(currentOffset), labels);
+ int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1;
+ currentOffset += 12;
+ // Read the table labels.
+ while (numTableEntries-- > 0) {
+ createLabel(bytecodeOffset + readInt(currentOffset), labels);
+ currentOffset += 4;
+ }
+ break;
+ case Opcodes.LOOKUPSWITCH:
+ // Skip 0 to 3 padding bytes.
+ currentOffset += 4 - (bytecodeOffset & 3);
+ // Read the default label and the number of switch cases.
+ createLabel(bytecodeOffset + readInt(currentOffset), labels);
+ int numSwitchCases = readInt(currentOffset + 4);
+ currentOffset += 8;
+ // Read the switch labels.
+ while (numSwitchCases-- > 0) {
+ createLabel(bytecodeOffset + readInt(currentOffset + 4), labels);
+ currentOffset += 8;
+ }
+ break;
+ case Opcodes.ILOAD:
+ case Opcodes.LLOAD:
+ case Opcodes.FLOAD:
+ case Opcodes.DLOAD:
+ case Opcodes.ALOAD:
+ case Opcodes.ISTORE:
+ case Opcodes.LSTORE:
+ case Opcodes.FSTORE:
+ case Opcodes.DSTORE:
+ case Opcodes.ASTORE:
+ case Opcodes.RET:
+ case Opcodes.BIPUSH:
+ case Opcodes.NEWARRAY:
+ case Opcodes.LDC:
+ currentOffset += 2;
+ break;
+ case Opcodes.SIPUSH:
+ case Constants.LDC_W:
+ case Constants.LDC2_W:
+ case Opcodes.GETSTATIC:
+ case Opcodes.PUTSTATIC:
+ case Opcodes.GETFIELD:
+ case Opcodes.PUTFIELD:
+ case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.INVOKESPECIAL:
+ case Opcodes.INVOKESTATIC:
+ case Opcodes.NEW:
+ case Opcodes.ANEWARRAY:
+ case Opcodes.CHECKCAST:
+ case Opcodes.INSTANCEOF:
+ case Opcodes.IINC:
+ currentOffset += 3;
+ break;
+ case Opcodes.INVOKEINTERFACE:
+ case Opcodes.INVOKEDYNAMIC:
+ currentOffset += 5;
+ break;
+ case Opcodes.MULTIANEWARRAY:
+ currentOffset += 4;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // Read the 'exception_table_length' and 'exception_table' field to create a label for each
+ // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
+ int exceptionTableLength = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (exceptionTableLength-- > 0) {
+ Label start = createLabel(readUnsignedShort(currentOffset), labels);
+ Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
+ Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
+ String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
+ currentOffset += 8;
+ methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
+ }
+
+ // Read the Code attributes to create a label for each referenced instruction (the variables
+ // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the
+ // attribute_name_index and attribute_length fields.
+ // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0.
+ // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is
+ // updated after each stack_map_frame is read.
+ int stackMapFrameOffset = 0;
+ // - The end offset of the StackMap[Table] attribute, or 0.
+ int stackMapTableEndOffset = 0;
+ // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not.
+ boolean compressedFrames = true;
+ // - The offset of the LocalVariableTable attribute, or 0.
+ int localVariableTableOffset = 0;
+ // - The offset of the LocalVariableTypeTable attribute, or 0.
+ int localVariableTypeTableOffset = 0;
+ // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations
+ // attribute, or null.
+ int[] visibleTypeAnnotationOffsets = null;
+ // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations
+ // attribute, or null.
+ int[] invisibleTypeAnnotationOffsets = null;
+ // - 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;
+ if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) {
+ if ((context.parsingOptions & SKIP_DEBUG) == 0) {
+ localVariableTableOffset = currentOffset;
+ // Parse the attribute to find the corresponding (debug only) labels.
+ int currentLocalVariableTableOffset = currentOffset;
+ int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset);
+ currentLocalVariableTableOffset += 2;
+ while (localVariableTableLength-- > 0) {
+ int startPc = readUnsignedShort(currentLocalVariableTableOffset);
+ createDebugLabel(startPc, labels);
+ int length = readUnsignedShort(currentLocalVariableTableOffset + 2);
+ createDebugLabel(startPc + length, labels);
+ // Skip the name_index, descriptor_index and index fields (2 bytes each).
+ currentLocalVariableTableOffset += 10;
+ }
+ }
+ } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) {
+ localVariableTypeTableOffset = currentOffset;
+ // Here we do not extract the labels corresponding to the attribute content. We assume they
+ // are the same or a subset of those of the LocalVariableTable attribute.
+ } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) {
+ if ((context.parsingOptions & SKIP_DEBUG) == 0) {
+ // Parse the attribute to find the corresponding (debug only) labels.
+ int currentLineNumberTableOffset = currentOffset;
+ int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset);
+ currentLineNumberTableOffset += 2;
+ while (lineNumberTableLength-- > 0) {
+ int startPc = readUnsignedShort(currentLineNumberTableOffset);
+ int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2);
+ currentLineNumberTableOffset += 4;
+ createDebugLabel(startPc, labels);
+ labels[startPc].addLineNumber(lineNumber);
+ }
+ }
+ } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+ visibleTypeAnnotationOffsets =
+ readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
+ // Here we do not extract the labels corresponding to the attribute content. This would
+ // require a full parsing of the attribute, which would need to be repeated when parsing
+ // the bytecode instructions (see below). Instead, the content of the attribute is read one
+ // type annotation at a time (i.e. after a type annotation has been visited, the next type
+ // annotation is read), and the labels it contains are also extracted one annotation at a
+ // time. This assumes that type annotations are ordered by increasing bytecode offset.
+ } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+ invisibleTypeAnnotationOffsets =
+ readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
+ // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
+ } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
+ if ((context.parsingOptions & SKIP_FRAMES) == 0) {
+ stackMapFrameOffset = currentOffset + 2;
+ stackMapTableEndOffset = currentOffset + attributeLength;
+ }
+ // Here we do not extract the labels corresponding to the attribute content. This would
+ // require a full parsing of the attribute, which would need to be repeated when parsing
+ // the bytecode instructions (see below). Instead, the content of the attribute is read one
+ // frame at a time (i.e. after a frame has been visited, the next frame is read), and the
+ // labels it contains are also extracted one frame at a time. Thanks to the ordering of
+ // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to
+ // see an offset smaller than the offset of the current instruction and for which no Label
+ // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map
+ // table without a full decoding (see below).
+ } else if ("StackMap".equals(attributeName)) {
+ if ((context.parsingOptions & SKIP_FRAMES) == 0) {
+ stackMapFrameOffset = currentOffset + 2;
+ stackMapTableEndOffset = currentOffset + attributeLength;
+ compressedFrames = false;
+ }
+ // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute,
+ // although this is not guaranteed by the attribute format. This allows an incremental
+ // extraction of the labels corresponding to this attribute (see the comment above for the
+ // StackMapTable attribute).
+ } else {
+ Attribute attribute =
+ readAttribute(
+ context.attributePrototypes,
+ attributeName,
+ currentOffset,
+ attributeLength,
+ charBuffer,
+ codeOffset,
+ labels);
+ attribute.nextAttribute = attributes;
+ attributes = attribute;
+ }
+ currentOffset += attributeLength;
+ }
+
+ // Initialize the context fields related to stack map frames, and generate the first
+ // (implicit) stack map frame, if needed.
+ final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0;
+ if (stackMapFrameOffset != 0) {
+ // The bytecode offset of the first explicit frame is not offset_delta + 1 but only
+ // offset_delta. Setting the implicit frame offset to -1 allows us to use of the
+ // "offset_delta + 1" rule in all cases.
+ context.currentFrameOffset = -1;
+ context.currentFrameType = 0;
+ context.currentFrameLocalCount = 0;
+ context.currentFrameLocalCountDelta = 0;
+ context.currentFrameLocalTypes = new Object[maxLocals];
+ context.currentFrameStackCount = 0;
+ context.currentFrameStackTypes = new Object[maxStack];
+ if (expandFrames) {
+ computeImplicitFrame(context);
+ }
+ // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the
+ // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type
+ // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset).
+ // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare,
+ // and the only consequence will be the creation of an unneeded label. This is better than
+ // creating a label for each NEW instruction, and faster than fully decoding the whole stack
+ // map table.
+ for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
+ if (classBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
+ int potentialBytecodeOffset = readUnsignedShort(offset + 1);
+ if (potentialBytecodeOffset >= 0
+ && potentialBytecodeOffset < codeLength
+ && (classBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
+ == Opcodes.NEW) {
+ createLabel(potentialBytecodeOffset, labels);
+ }
+ }
+ }
+ }
+ if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) {
+ // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method
+ // does not currently have any frame. These inserted frames must be computed by simulating the
+ // effect of the bytecode instructions, one by one, starting from the implicit first frame.
+ // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To
+ // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is
+ // computed in MethodWriter).
+ methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
+ }
+
+ // Visit the bytecode instructions. First, introduce state variables for the incremental parsing
+ // of the type annotations.
+
+ // Index of the next runtime visible type annotation to read (in the
+ // visibleTypeAnnotationOffsets array).
+ int currentVisibleTypeAnnotationIndex = 0;
+ // The bytecode offset of the next runtime visible type annotation to read, or -1.
+ int currentVisibleTypeAnnotationBytecodeOffset =
+ getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0);
+ // Index of the next runtime invisible type annotation to read (in the
+ // invisibleTypeAnnotationOffsets array).
+ int currentInvisibleTypeAnnotationIndex = 0;
+ // The bytecode offset of the next runtime invisible type annotation to read, or -1.
+ int currentInvisibleTypeAnnotationBytecodeOffset =
+ getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0);
+
+ // Whether a F_INSERT stack map frame must be inserted before the current instruction.
+ boolean insertFrame = false;
+
+ // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr
+ // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific
+ // instructions).
+ final int wideJumpOpcodeDelta =
+ (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0;
+
+ currentOffset = bytecodeStartOffset;
+ while (currentOffset < bytecodeEndOffset) {
+ final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;
+
+ // Visit the label and the line number(s) for this bytecode offset, if any.
+ Label currentLabel = labels[currentBytecodeOffset];
+ if (currentLabel != null) {
+ currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0);
+ }
+
+ // Visit the stack map frame for this bytecode offset, if any.
+ while (stackMapFrameOffset != 0
+ && (context.currentFrameOffset == currentBytecodeOffset
+ || context.currentFrameOffset == -1)) {
+ // If there is a stack map frame for this offset, make methodVisitor visit it, and read the
+ // next stack map frame if there is one.
+ if (context.currentFrameOffset != -1) {
+ if (!compressedFrames || expandFrames) {
+ methodVisitor.visitFrame(
+ Opcodes.F_NEW,
+ context.currentFrameLocalCount,
+ context.currentFrameLocalTypes,
+ context.currentFrameStackCount,
+ context.currentFrameStackTypes);
+ } else {
+ methodVisitor.visitFrame(
+ context.currentFrameType,
+ context.currentFrameLocalCountDelta,
+ context.currentFrameLocalTypes,
+ context.currentFrameStackCount,
+ context.currentFrameStackTypes);
+ }
+ // Since there is already a stack map frame for this bytecode offset, there is no need to
+ // insert a new one.
+ insertFrame = false;
+ }
+ if (stackMapFrameOffset < stackMapTableEndOffset) {
+ stackMapFrameOffset =
+ readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context);
+ } else {
+ stackMapFrameOffset = 0;
+ }
+ }
+
+ // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to
+ // true during the previous iteration. The actual frame content is computed in MethodWriter.
+ if (insertFrame) {
+ if ((context.parsingOptions & EXPAND_FRAMES) != 0) {
+ methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null);
+ }
+ insertFrame = false;
+ }
+
+ // Visit the instruction at this bytecode offset.
+ int opcode = classBuffer[currentOffset] & 0xFF;
+ switch (opcode) {
+ case Opcodes.NOP:
+ case Opcodes.ACONST_NULL:
+ case Opcodes.ICONST_M1:
+ case Opcodes.ICONST_0:
+ case Opcodes.ICONST_1:
+ case Opcodes.ICONST_2:
+ case Opcodes.ICONST_3:
+ case Opcodes.ICONST_4:
+ case Opcodes.ICONST_5:
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ case Opcodes.IALOAD:
+ case Opcodes.LALOAD:
+ case Opcodes.FALOAD:
+ case Opcodes.DALOAD:
+ case Opcodes.AALOAD:
+ case Opcodes.BALOAD:
+ case Opcodes.CALOAD:
+ case Opcodes.SALOAD:
+ case Opcodes.IASTORE:
+ case Opcodes.LASTORE:
+ case Opcodes.FASTORE:
+ case Opcodes.DASTORE:
+ case Opcodes.AASTORE:
+ case Opcodes.BASTORE:
+ case Opcodes.CASTORE:
+ case Opcodes.SASTORE:
+ case Opcodes.POP:
+ case Opcodes.POP2:
+ case Opcodes.DUP:
+ case Opcodes.DUP_X1:
+ case Opcodes.DUP_X2:
+ case Opcodes.DUP2:
+ case Opcodes.DUP2_X1:
+ case Opcodes.DUP2_X2:
+ case Opcodes.SWAP:
+ case Opcodes.IADD:
+ case Opcodes.LADD:
+ case Opcodes.FADD:
+ case Opcodes.DADD:
+ case Opcodes.ISUB:
+ case Opcodes.LSUB:
+ case Opcodes.FSUB:
+ case Opcodes.DSUB:
+ case Opcodes.IMUL:
+ case Opcodes.LMUL:
+ case Opcodes.FMUL:
+ case Opcodes.DMUL:
+ case Opcodes.IDIV:
+ case Opcodes.LDIV:
+ case Opcodes.FDIV:
+ case Opcodes.DDIV:
+ case Opcodes.IREM:
+ case Opcodes.LREM:
+ case Opcodes.FREM:
+ case Opcodes.DREM:
+ case Opcodes.INEG:
+ case Opcodes.LNEG:
+ case Opcodes.FNEG:
+ case Opcodes.DNEG:
+ case Opcodes.ISHL:
+ case Opcodes.LSHL:
+ case Opcodes.ISHR:
+ case Opcodes.LSHR:
+ case Opcodes.IUSHR:
+ case Opcodes.LUSHR:
+ case Opcodes.IAND:
+ case Opcodes.LAND:
+ case Opcodes.IOR:
+ case Opcodes.LOR:
+ case Opcodes.IXOR:
+ case Opcodes.LXOR:
+ case Opcodes.I2L:
+ case Opcodes.I2F:
+ case Opcodes.I2D:
+ case Opcodes.L2I:
+ case Opcodes.L2F:
+ case Opcodes.L2D:
+ case Opcodes.F2I:
+ case Opcodes.F2L:
+ case Opcodes.F2D:
+ case Opcodes.D2I:
+ case Opcodes.D2L:
+ case Opcodes.D2F:
+ case Opcodes.I2B:
+ case Opcodes.I2C:
+ case Opcodes.I2S:
+ case Opcodes.LCMP:
+ case Opcodes.FCMPL:
+ case Opcodes.FCMPG:
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG:
+ case Opcodes.IRETURN:
+ case Opcodes.LRETURN:
+ case Opcodes.FRETURN:
+ case Opcodes.DRETURN:
+ case Opcodes.ARETURN:
+ case Opcodes.RETURN:
+ case Opcodes.ARRAYLENGTH:
+ case Opcodes.ATHROW:
+ case Opcodes.MONITORENTER:
+ case Opcodes.MONITOREXIT:
+ methodVisitor.visitInsn(opcode);
+ currentOffset += 1;
+ break;
+ case Constants.ILOAD_0:
+ case Constants.ILOAD_1:
+ case Constants.ILOAD_2:
+ case Constants.ILOAD_3:
+ case Constants.LLOAD_0:
+ case Constants.LLOAD_1:
+ case Constants.LLOAD_2:
+ case Constants.LLOAD_3:
+ case Constants.FLOAD_0:
+ case Constants.FLOAD_1:
+ case Constants.FLOAD_2:
+ case Constants.FLOAD_3:
+ case Constants.DLOAD_0:
+ case Constants.DLOAD_1:
+ case Constants.DLOAD_2:
+ case Constants.DLOAD_3:
+ case Constants.ALOAD_0:
+ case Constants.ALOAD_1:
+ case Constants.ALOAD_2:
+ case Constants.ALOAD_3:
+ opcode -= Constants.ILOAD_0;
+ methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
+ currentOffset += 1;
+ break;
+ case Constants.ISTORE_0:
+ case Constants.ISTORE_1:
+ case Constants.ISTORE_2:
+ case Constants.ISTORE_3:
+ case Constants.LSTORE_0:
+ case Constants.LSTORE_1:
+ case Constants.LSTORE_2:
+ case Constants.LSTORE_3:
+ case Constants.FSTORE_0:
+ case Constants.FSTORE_1:
+ case Constants.FSTORE_2:
+ case Constants.FSTORE_3:
+ case Constants.DSTORE_0:
+ case Constants.DSTORE_1:
+ case Constants.DSTORE_2:
+ case Constants.DSTORE_3:
+ case Constants.ASTORE_0:
+ case Constants.ASTORE_1:
+ case Constants.ASTORE_2:
+ case Constants.ASTORE_3:
+ opcode -= Constants.ISTORE_0;
+ methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
+ currentOffset += 1;
+ break;
+ case Opcodes.IFEQ:
+ case Opcodes.IFNE:
+ case Opcodes.IFLT:
+ case Opcodes.IFGE:
+ case Opcodes.IFGT:
+ case Opcodes.IFLE:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ACMPEQ:
+ case Opcodes.IF_ACMPNE:
+ case Opcodes.GOTO:
+ case Opcodes.JSR:
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL:
+ methodVisitor.visitJumpInsn(
+ opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
+ currentOffset += 3;
+ break;
+ case Constants.GOTO_W:
+ case Constants.JSR_W:
+ methodVisitor.visitJumpInsn(
+ opcode - wideJumpOpcodeDelta,
+ labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
+ currentOffset += 5;
+ break;
+ case Constants.ASM_IFEQ:
+ case Constants.ASM_IFNE:
+ case Constants.ASM_IFLT:
+ case Constants.ASM_IFGE:
+ case Constants.ASM_IFGT:
+ case Constants.ASM_IFLE:
+ case Constants.ASM_IF_ICMPEQ:
+ case Constants.ASM_IF_ICMPNE:
+ case Constants.ASM_IF_ICMPLT:
+ case Constants.ASM_IF_ICMPGE:
+ case Constants.ASM_IF_ICMPGT:
+ case Constants.ASM_IF_ICMPLE:
+ case Constants.ASM_IF_ACMPEQ:
+ case Constants.ASM_IF_ACMPNE:
+ case Constants.ASM_GOTO:
+ case Constants.ASM_JSR:
+ case Constants.ASM_IFNULL:
+ case Constants.ASM_IFNONNULL: {
+ // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO
+ // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx with IFNOTxxx GOTO_W L:...,
+ // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and
+ // where designates the instruction just after the GOTO_W.
+ // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and
+ // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL.
+ opcode =
+ opcode < Constants.ASM_IFNULL
+ ? opcode - Constants.ASM_OPCODE_DELTA
+ : opcode - Constants.ASM_IFNULL_OPCODE_DELTA;
+ Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)];
+ if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
+ // Replace GOTO with GOTO_W and JSR with JSR_W.
+ methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target);
+ } else {
+ // Compute the "opposite" of opcode. This can be done by flipping the least
+ // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ
+ // (with a pre and post offset by 1).
+ opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1;
+ Label endif = createLabel(currentBytecodeOffset + 3, labels);
+ methodVisitor.visitJumpInsn(opcode, endif);
+ methodVisitor.visitJumpInsn(Constants.GOTO_W, target);
+ // endif designates the instruction just after GOTO_W, and is visited as part of the
+ // next instruction. Since it is a jump target, we need to insert a frame here.
+ insertFrame = true;
+ }
+ currentOffset += 3;
+ break;
+ }
+ case Constants.ASM_GOTO_W:
+ // Replace ASM_GOTO_W with GOTO_W.
+ methodVisitor.visitJumpInsn(
+ Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
+ // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
+ // IFNOTxxx ASM_GOTO_W L:..., see MethodWriter), so we need to insert a frame
+ // here.
+ insertFrame = true;
+ currentOffset += 5;
+ break;
+ case Constants.WIDE:
+ opcode = classBuffer[currentOffset + 1] & 0xFF;
+ if (opcode == Opcodes.IINC) {
+ methodVisitor.visitIincInsn(
+ readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
+ currentOffset += 6;
+ } else {
+ methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2));
+ currentOffset += 4;
+ }
+ break;
+ case Opcodes.TABLESWITCH: {
+ // Skip 0 to 3 padding bytes.
+ currentOffset += 4 - (currentBytecodeOffset & 3);
+ // Read the instruction.
+ Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
+ int low = readInt(currentOffset + 4);
+ int high = readInt(currentOffset + 8);
+ currentOffset += 12;
+ Label[] table = new Label[high - low + 1];
+ for (int i = 0; i < table.length; ++i) {
+ table[i] = labels[currentBytecodeOffset + readInt(currentOffset)];
+ currentOffset += 4;
+ }
+ methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
+ break;
+ }
+ case Opcodes.LOOKUPSWITCH: {
+ // Skip 0 to 3 padding bytes.
+ currentOffset += 4 - (currentBytecodeOffset & 3);
+ // Read the instruction.
+ Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
+ int numPairs = readInt(currentOffset + 4);
+ currentOffset += 8;
+ int[] keys = new int[numPairs];
+ Label[] values = new Label[numPairs];
+ for (int i = 0; i < numPairs; ++i) {
+ keys[i] = readInt(currentOffset);
+ values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
+ currentOffset += 8;
+ }
+ methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
+ break;
+ }
+ case Opcodes.ILOAD:
+ case Opcodes.LLOAD:
+ case Opcodes.FLOAD:
+ case Opcodes.DLOAD:
+ case Opcodes.ALOAD:
+ case Opcodes.ISTORE:
+ case Opcodes.LSTORE:
+ case Opcodes.FSTORE:
+ case Opcodes.DSTORE:
+ case Opcodes.ASTORE:
+ case Opcodes.RET:
+ methodVisitor.visitVarInsn(opcode, classBuffer[currentOffset + 1] & 0xFF);
+ currentOffset += 2;
+ break;
+ case Opcodes.BIPUSH:
+ case Opcodes.NEWARRAY:
+ methodVisitor.visitIntInsn(opcode, classBuffer[currentOffset + 1]);
+ currentOffset += 2;
+ break;
+ case Opcodes.SIPUSH:
+ methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
+ currentOffset += 3;
+ break;
+ case Opcodes.LDC:
+ methodVisitor.visitLdcInsn(readConst(classBuffer[currentOffset + 1] & 0xFF, charBuffer));
+ currentOffset += 2;
+ break;
+ case Constants.LDC_W:
+ case Constants.LDC2_W:
+ methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
+ currentOffset += 3;
+ break;
+ case Opcodes.GETSTATIC:
+ case Opcodes.PUTSTATIC:
+ case Opcodes.GETFIELD:
+ case Opcodes.PUTFIELD:
+ case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.INVOKESPECIAL:
+ case Opcodes.INVOKESTATIC:
+ case Opcodes.INVOKEINTERFACE: {
+ int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
+ int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
+ String owner = readClass(cpInfoOffset, charBuffer);
+ String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
+ String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
+ if (opcode < Opcodes.INVOKEVIRTUAL) {
+ methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
+ } else {
+ boolean isInterface =
+ classBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+ methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ if (opcode == Opcodes.INVOKEINTERFACE) {
+ currentOffset += 5;
+ } else {
+ currentOffset += 3;
+ }
+ break;
+ }
+ case Opcodes.INVOKEDYNAMIC: {
+ int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
+ int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
+ String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
+ String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
+ int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
+ Handle handle =
+ (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
+ Object[] bootstrapMethodArguments =
+ new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
+ bootstrapMethodOffset += 4;
+ for (int i = 0; i < bootstrapMethodArguments.length; i++) {
+ bootstrapMethodArguments[i] =
+ readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
+ bootstrapMethodOffset += 2;
+ }
+ methodVisitor.visitInvokeDynamicInsn(
+ name, descriptor, handle, bootstrapMethodArguments);
+ currentOffset += 5;
+ break;
+ }
+ case Opcodes.NEW:
+ case Opcodes.ANEWARRAY:
+ case Opcodes.CHECKCAST:
+ case Opcodes.INSTANCEOF:
+ methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
+ currentOffset += 3;
+ break;
+ case Opcodes.IINC:
+ methodVisitor.visitIincInsn(
+ classBuffer[currentOffset + 1] & 0xFF, classBuffer[currentOffset + 2]);
+ currentOffset += 3;
+ break;
+ case Opcodes.MULTIANEWARRAY:
+ methodVisitor.visitMultiANewArrayInsn(
+ readClass(currentOffset + 1, charBuffer), classBuffer[currentOffset + 3] & 0xFF);
+ currentOffset += 4;
+ break;
+ default:
+ throw new AssertionError();
+ }
+
+ // Visit the runtime visible instruction annotations, if any.
+ while (visibleTypeAnnotationOffsets != null
+ && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length
+ && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
+ if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
+ // Parse the target_type, target_info and target_path fields.
+ int currentAnnotationOffset =
+ readTypeAnnotationTarget(
+ context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]);
+ // 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.
+ readElementValues(
+ methodVisitor.visitInsnAnnotation(
+ context.currentTypeAnnotationTarget,
+ context.currentTypeAnnotationTargetPath,
+ annotationDescriptor,
+ /* visible = */ true),
+ currentAnnotationOffset,
+ /* named = */ true,
+ charBuffer);
+ }
+ currentVisibleTypeAnnotationBytecodeOffset =
+ getTypeAnnotationBytecodeOffset(
+ visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex);
+ }
+
+ // Visit the runtime invisible instruction annotations, if any.
+ while (invisibleTypeAnnotationOffsets != null
+ && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length
+ && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
+ if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
+ // Parse the target_type, target_info and target_path fields.
+ int currentAnnotationOffset =
+ readTypeAnnotationTarget(
+ context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]);
+ // 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.
+ readElementValues(
+ methodVisitor.visitInsnAnnotation(
+ context.currentTypeAnnotationTarget,
+ context.currentTypeAnnotationTargetPath,
+ annotationDescriptor,
+ /* visible = */ false),
+ currentAnnotationOffset,
+ /* named = */ true,
+ charBuffer);
+ }
+ currentInvisibleTypeAnnotationBytecodeOffset =
+ getTypeAnnotationBytecodeOffset(
+ invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex);
+ }
+ }
+ if (labels[codeLength] != null) {
+ methodVisitor.visitLabel(labels[codeLength]);
+ }
+
+ // Visit LocalVariableTable and LocalVariableTypeTable attributes.
+ if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
+ // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable.
+ int[] typeTable = null;
+ if (localVariableTypeTableOffset != 0) {
+ typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3];
+ currentOffset = localVariableTypeTableOffset + 2;
+ int typeTableIndex = typeTable.length;
+ while (typeTableIndex > 0) {
+ // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'.
+ typeTable[--typeTableIndex] = currentOffset + 6;
+ typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8);
+ typeTable[--typeTableIndex] = readUnsignedShort(currentOffset);
+ currentOffset += 10;
+ }
+ }
+ int localVariableTableLength = readUnsignedShort(localVariableTableOffset);
+ currentOffset = localVariableTableOffset + 2;
+ while (localVariableTableLength-- > 0) {
+ int startPc = readUnsignedShort(currentOffset);
+ int length = readUnsignedShort(currentOffset + 2);
+ String name = readUTF8(currentOffset + 4, charBuffer);
+ String descriptor = readUTF8(currentOffset + 6, charBuffer);
+ int index = readUnsignedShort(currentOffset + 8);
+ currentOffset += 10;
+ String signature = null;
+ if (typeTable != null) {
+ for (int i = 0; i < typeTable.length; i += 3) {
+ if (typeTable[i] == startPc && typeTable[i + 1] == index) {
+ signature = readUTF8(typeTable[i + 2], charBuffer);
+ break;
+ }
+ }
+ }
+ methodVisitor.visitLocalVariable(
+ name, descriptor, signature, labels[startPc], labels[startPc + length], index);
+ }
+ }
+
+ // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute.
+ if (visibleTypeAnnotationOffsets != null) {
+ for (int typeAnnotationOffset : visibleTypeAnnotationOffsets) {
+ int targetType = readByte(typeAnnotationOffset);
+ if (targetType == TypeReference.LOCAL_VARIABLE
+ || targetType == TypeReference.RESOURCE_VARIABLE) {
+ // Parse the target_type, target_info and target_path fields.
+ currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
+ // Parse the type_index field.
+ String annotationDescriptor = readUTF8(currentOffset, charBuffer);
+ currentOffset += 2;
+ // Parse num_element_value_pairs and element_value_pairs and visit these values.
+ readElementValues(
+ methodVisitor.visitLocalVariableAnnotation(
+ context.currentTypeAnnotationTarget,
+ context.currentTypeAnnotationTargetPath,
+ context.currentLocalVariableAnnotationRangeStarts,
+ context.currentLocalVariableAnnotationRangeEnds,
+ context.currentLocalVariableAnnotationRangeIndices,
+ annotationDescriptor,
+ /* visible = */ true),
+ currentOffset,
+ /* named = */ true,
+ charBuffer);
+ }
+ }
+ }
+
+ // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute.
+ if (invisibleTypeAnnotationOffsets != null) {
+ for (int typeAnnotationOffset : invisibleTypeAnnotationOffsets) {
+ int targetType = readByte(typeAnnotationOffset);
+ if (targetType == TypeReference.LOCAL_VARIABLE
+ || targetType == TypeReference.RESOURCE_VARIABLE) {
+ // Parse the target_type, target_info and target_path fields.
+ currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
+ // Parse the type_index field.
+ String annotationDescriptor = readUTF8(currentOffset, charBuffer);
+ currentOffset += 2;
+ // Parse num_element_value_pairs and element_value_pairs and visit these values.
+ readElementValues(
+ methodVisitor.visitLocalVariableAnnotation(
+ context.currentTypeAnnotationTarget,
+ context.currentTypeAnnotationTargetPath,
+ context.currentLocalVariableAnnotationRangeStarts,
+ context.currentLocalVariableAnnotationRangeEnds,
+ context.currentLocalVariableAnnotationRangeIndices,
+ annotationDescriptor,
+ /* visible = */ false),
+ currentOffset,
+ /* 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 MethodWriter.
+ Attribute nextAttribute = attributes.nextAttribute;
+ attributes.nextAttribute = null;
+ methodVisitor.visitAttribute(attributes);
+ attributes = nextAttribute;
+ }
+
+ // Visit the max stack and max locals values.
+ methodVisitor.visitMaxs(maxStack, maxLocals);
+ }
+
+ /**
+ * Returns the label corresponding to the given bytecode offset. The default implementation of
+ * this method creates a label for the given offset if it has not been already created.
+ *
+ * @param bytecodeOffset a bytecode offset in a method.
+ * @param labels the already created labels, indexed by their offset. If a label already exists
+ * for bytecodeOffset this method must not create a new one. Otherwise it must store the new
+ * label in this array.
+ * @return a non null Label, which must be equal to labels[bytecodeOffset].
+ */
+ protected Label readLabel(final int bytecodeOffset, final Label[] labels) {
+ if (labels[bytecodeOffset] == null) {
+ labels[bytecodeOffset] = new Label();
+ }
+ return labels[bytecodeOffset];
+ }
+
+ /**
+ * Creates a label without the {@link Label#FLAG_DEBUG_ONLY} flag set, for the given bytecode
+ * offset. The label is created with a call to {@link #readLabel} and its {@link
+ * Label#FLAG_DEBUG_ONLY} flag is cleared.
+ *
+ * @param bytecodeOffset a bytecode offset in a method.
+ * @param labels the already created labels, indexed by their offset.
+ * @return a Label without the {@link Label#FLAG_DEBUG_ONLY} flag set.
+ */
+ private Label createLabel(final int bytecodeOffset, final Label[] labels) {
+ Label label = readLabel(bytecodeOffset, labels);
+ label.flags &= ~Label.FLAG_DEBUG_ONLY;
+ return label;
+ }
+
+ /**
+ * Creates a label with the {@link Label#FLAG_DEBUG_ONLY} flag set, if there is no already
+ * existing label for the given bytecode offset (otherwise does nothing). The label is created
+ * with a call to {@link #readLabel}.
+ *
+ * @param bytecodeOffset a bytecode offset in a method.
+ * @param labels the already created labels, indexed by their offset.
+ */
+ private void createDebugLabel(final int bytecodeOffset, final Label[] labels) {
+ if (labels[bytecodeOffset] == null) {
+ readLabel(bytecodeOffset, labels).flags |= Label.FLAG_DEBUG_ONLY;
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------
+ // Methods to parse annotations, type annotations and parameter annotations
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * Parses a Runtime[In]VisibleTypeAnnotations attribute to find the offset of each type_annotation
+ * entry it contains, to find the corresponding labels, and to visit the try catch block
+ * annotations.
+ *
+ * @param methodVisitor the method visitor to be used to visit the try catch block annotations.
+ * @param context information about the class being parsed.
+ * @param runtimeTypeAnnotationsOffset the start offset of a Runtime[In]VisibleTypeAnnotations
+ * attribute, excluding the attribute_info's attribute_name_index and
+ * attribute_length fields.
+ * @param visible true if the attribute to parse is a RuntimeVisibleTypeAnnotations attribute,
+ * false it is a RuntimeInvisibleTypeAnnotations attribute.
+ * @return the start offset of each entry of the Runtime[In]VisibleTypeAnnotations_attribute's
+ * 'annotations' array field.
+ */
+ private int[] readTypeAnnotations(
+ final MethodVisitor methodVisitor,
+ final Context context,
+ final int runtimeTypeAnnotationsOffset,
+ final boolean visible) {
+ char[] charBuffer = context.charBuffer;
+ int currentOffset = runtimeTypeAnnotationsOffset;
+ // Read the num_annotations field and create an array to store the type_annotation offsets.
+ int[] typeAnnotationsOffsets = new int[readUnsignedShort(currentOffset)];
+ currentOffset += 2;
+ // Parse the 'annotations' array field.
+ for (int i = 0; i < typeAnnotationsOffsets.length; ++i) {
+ typeAnnotationsOffsets[i] = currentOffset;
+ // Parse the type_annotation's target_type and the target_info fields. The size of the
+ // target_info field depends on the value of target_type.
+ int targetType = readInt(currentOffset);
+ switch (targetType >>> 24) {
+ case TypeReference.LOCAL_VARIABLE:
+ case TypeReference.RESOURCE_VARIABLE:
+ // A localvar_target has a variable size, which depends on the value of their table_length
+ // field. It also references bytecode offsets, for which we need labels.
+ int tableLength = readUnsignedShort(currentOffset + 1);
+ currentOffset += 3;
+ while (tableLength-- > 0) {
+ int startPc = readUnsignedShort(currentOffset);
+ int length = readUnsignedShort(currentOffset + 2);
+ // Skip the index field (2 bytes).
+ currentOffset += 6;
+ createLabel(startPc, context.currentMethodLabels);
+ createLabel(startPc + length, context.currentMethodLabels);
+ }
+ break;
+ case TypeReference.CAST:
+ case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+ case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
+ case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+ case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
+ currentOffset += 4;
+ break;
+ case TypeReference.CLASS_EXTENDS:
+ case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
+ case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
+ case TypeReference.THROWS:
+ case TypeReference.EXCEPTION_PARAMETER:
+ case TypeReference.INSTANCEOF:
+ case TypeReference.NEW:
+ case TypeReference.CONSTRUCTOR_REFERENCE:
+ case TypeReference.METHOD_REFERENCE:
+ currentOffset += 3;
+ break;
+ case TypeReference.CLASS_TYPE_PARAMETER:
+ case TypeReference.METHOD_TYPE_PARAMETER:
+ case TypeReference.METHOD_FORMAL_PARAMETER:
+ case TypeReference.FIELD:
+ case TypeReference.METHOD_RETURN:
+ case TypeReference.METHOD_RECEIVER:
+ default:
+ // TypeReference type which can't be used in Code attribute, or which is unknown.
+ throw new IllegalArgumentException();
+ }
+ // Parse the rest of the type_annotation structure, starting with the target_path structure
+ // (whose size depends on its path_length field).
+ int pathLength = readByte(currentOffset);
+ if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
+ // Parse the target_path structure and create a corresponding TypePath.
+ TypePath path = pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
+ currentOffset += 1 + 2 * pathLength;
+ // Parse the type_index field.
+ String annotationDescriptor = readUTF8(currentOffset, charBuffer);
+ currentOffset += 2;
+ // Parse num_element_value_pairs and element_value_pairs and visit these values.
+ currentOffset =
+ readElementValues(
+ methodVisitor.visitTryCatchAnnotation(
+ targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
+ currentOffset,
+ /* named = */ true,
+ charBuffer);
+ } else {
+ // We don't want to visit the other target_type annotations, so we just skip them (which
+ // requires some parsing because the element_value_pairs array has a variable size). First,
+ // skip the target_path structure:
+ currentOffset += 3 + 2 * pathLength;
+ // Then skip the num_element_value_pairs and element_value_pairs fields (by reading them
+ // with a null AnnotationVisitor).
+ currentOffset =
+ readElementValues(
+ /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
+ }
+ }
+ return typeAnnotationsOffsets;
+ }
+
+ /**
+ * Returns the bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or
+ * -1 if there is no such type_annotation of if it does not have a bytecode offset.
+ *
+ * @param typeAnnotationOffsets the offset of each 'type_annotation' entry in a
+ * Runtime[In]VisibleTypeAnnotations attribute, or null.
+ * @param typeAnnotationIndex the index a 'type_annotation' entry in typeAnnotationOffsets.
+ * @return bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or -1
+ * if there is no such type_annotation of if it does not have a bytecode offset.
+ */
+ private int getTypeAnnotationBytecodeOffset(
+ final int[] typeAnnotationOffsets, final int typeAnnotationIndex) {
+ if (typeAnnotationOffsets == null
+ || typeAnnotationIndex >= typeAnnotationOffsets.length
+ || readByte(typeAnnotationOffsets[typeAnnotationIndex]) < TypeReference.INSTANCEOF) {
+ return -1;
+ }
+ return readUnsignedShort(typeAnnotationOffsets[typeAnnotationIndex] + 1);
+ }
+
+ /**
+ * Parses the header of a JVMS type_annotation structure to extract its target_type, target_info
+ * and target_path (the result is stored in the given context), and returns the start offset of
+ * the rest of the type_annotation structure.
+ *
+ * @param context information about the class being parsed. This is where the extracted
+ * target_type and target_path must be stored.
+ * @param typeAnnotationOffset the start offset of a type_annotation structure.
+ * @return the start offset of the rest of the type_annotation structure.
+ */
+ private int readTypeAnnotationTarget(final Context context, final int typeAnnotationOffset) {
+ int currentOffset = typeAnnotationOffset;
+ // Parse and store the target_type structure.
+ int targetType = readInt(typeAnnotationOffset);
+ switch (targetType >>> 24) {
+ case TypeReference.CLASS_TYPE_PARAMETER:
+ case TypeReference.METHOD_TYPE_PARAMETER:
+ case TypeReference.METHOD_FORMAL_PARAMETER:
+ targetType &= 0xFFFF0000;
+ currentOffset += 2;
+ break;
+ case TypeReference.FIELD:
+ case TypeReference.METHOD_RETURN:
+ case TypeReference.METHOD_RECEIVER:
+ targetType &= 0xFF000000;
+ currentOffset += 1;
+ break;
case TypeReference.LOCAL_VARIABLE:
case TypeReference.RESOURCE_VARIABLE:
- // A localvar_target has a variable size, which depends on the value of their table_length
- // field. It also references bytecode offsets, for which we need labels.
- int tableLength = readUnsignedShort(currentOffset + 1);
- currentOffset += 3;
- while (tableLength-- > 0) {
- int startPc = readUnsignedShort(currentOffset);
- int length = readUnsignedShort(currentOffset + 2);
- // Skip the index field (2 bytes).
- currentOffset += 6;
- createLabel(startPc, context.currentMethodLabels);
- createLabel(startPc + length, context.currentMethodLabels);
- }
- break;
+ targetType &= 0xFF000000;
+ int tableLength = readUnsignedShort(currentOffset + 1);
+ currentOffset += 3;
+ context.currentLocalVariableAnnotationRangeStarts = new Label[tableLength];
+ context.currentLocalVariableAnnotationRangeEnds = new Label[tableLength];
+ context.currentLocalVariableAnnotationRangeIndices = new int[tableLength];
+ for (int i = 0; i < tableLength; ++i) {
+ int startPc = readUnsignedShort(currentOffset);
+ int length = readUnsignedShort(currentOffset + 2);
+ int index = readUnsignedShort(currentOffset + 4);
+ currentOffset += 6;
+ context.currentLocalVariableAnnotationRangeStarts[i] =
+ createLabel(startPc, context.currentMethodLabels);
+ context.currentLocalVariableAnnotationRangeEnds[i] =
+ createLabel(startPc + length, context.currentMethodLabels);
+ context.currentLocalVariableAnnotationRangeIndices[i] = index;
+ }
+ break;
case TypeReference.CAST:
case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
- currentOffset += 4;
- break;
+ targetType &= 0xFF0000FF;
+ currentOffset += 4;
+ break;
case TypeReference.CLASS_EXTENDS:
case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
case TypeReference.THROWS:
case TypeReference.EXCEPTION_PARAMETER:
+ targetType &= 0xFFFFFF00;
+ currentOffset += 3;
+ break;
case TypeReference.INSTANCEOF:
case TypeReference.NEW:
case TypeReference.CONSTRUCTOR_REFERENCE:
case TypeReference.METHOD_REFERENCE:
- currentOffset += 3;
- break;
- case TypeReference.CLASS_TYPE_PARAMETER:
- case TypeReference.METHOD_TYPE_PARAMETER:
- case TypeReference.METHOD_FORMAL_PARAMETER:
- case TypeReference.FIELD:
- case TypeReference.METHOD_RETURN:
- case TypeReference.METHOD_RECEIVER:
+ targetType &= 0xFF000000;
+ currentOffset += 3;
+ break;
default:
- // TypeReference type which can't be used in Code attribute, or which is unknown.
- throw new IllegalArgumentException();
- }
- // Parse the rest of the type_annotation structure, starting with the target_path structure
- // (whose size depends on its path_length field).
- int pathLength = readByte(currentOffset);
- if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
- // Parse the target_path structure and create a corresponding TypePath.
- TypePath path = pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
- currentOffset += 1 + 2 * pathLength;
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
- currentOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- currentOffset =
- readElementValues(
- methodVisitor.visitTryCatchAnnotation(
- targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
- currentOffset,
- /* named = */ true,
- charBuffer);
- } else {
- // We don't want to visit the other target_type annotations, so we just skip them (which
- // requires some parsing because the element_value_pairs array has a variable size). First,
- // skip the target_path structure:
- currentOffset += 3 + 2 * pathLength;
- // Then skip the num_element_value_pairs and element_value_pairs fields (by reading them
- // with a null AnnotationVisitor).
- currentOffset =
- readElementValues(
- /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
- }
- }
- return typeAnnotationsOffsets;
- }
-
- /**
- * Returns the bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or
- * -1 if there is no such type_annotation of if it does not have a bytecode offset.
- *
- * @param typeAnnotationOffsets the offset of each 'type_annotation' entry in a
- * Runtime[In]VisibleTypeAnnotations attribute, or null.
- * @param typeAnnotationIndex the index a 'type_annotation' entry in typeAnnotationOffsets.
- * @return bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or -1
- * if there is no such type_annotation of if it does not have a bytecode offset.
- */
- private int getTypeAnnotationBytecodeOffset(
- final int[] typeAnnotationOffsets, final int typeAnnotationIndex) {
- if (typeAnnotationOffsets == null
- || typeAnnotationIndex >= typeAnnotationOffsets.length
- || readByte(typeAnnotationOffsets[typeAnnotationIndex]) < TypeReference.INSTANCEOF) {
- return -1;
- }
- return readUnsignedShort(typeAnnotationOffsets[typeAnnotationIndex] + 1);
- }
-
- /**
- * Parses the header of a JVMS type_annotation structure to extract its target_type, target_info
- * and target_path (the result is stored in the given context), and returns the start offset of
- * the rest of the type_annotation structure.
- *
- * @param context information about the class being parsed. This is where the extracted
- * target_type and target_path must be stored.
- * @param typeAnnotationOffset the start offset of a type_annotation structure.
- * @return the start offset of the rest of the type_annotation structure.
- */
- private int readTypeAnnotationTarget(final Context context, final int typeAnnotationOffset) {
- int currentOffset = typeAnnotationOffset;
- // Parse and store the target_type structure.
- int targetType = readInt(typeAnnotationOffset);
- switch (targetType >>> 24) {
- case TypeReference.CLASS_TYPE_PARAMETER:
- case TypeReference.METHOD_TYPE_PARAMETER:
- case TypeReference.METHOD_FORMAL_PARAMETER:
- targetType &= 0xFFFF0000;
- currentOffset += 2;
- break;
- case TypeReference.FIELD:
- case TypeReference.METHOD_RETURN:
- case TypeReference.METHOD_RECEIVER:
- targetType &= 0xFF000000;
- currentOffset += 1;
- break;
- case TypeReference.LOCAL_VARIABLE:
- case TypeReference.RESOURCE_VARIABLE:
- targetType &= 0xFF000000;
- int tableLength = readUnsignedShort(currentOffset + 1);
- currentOffset += 3;
- context.currentLocalVariableAnnotationRangeStarts = new Label[tableLength];
- context.currentLocalVariableAnnotationRangeEnds = new Label[tableLength];
- context.currentLocalVariableAnnotationRangeIndices = new int[tableLength];
- for (int i = 0; i < tableLength; ++i) {
- int startPc = readUnsignedShort(currentOffset);
- int length = readUnsignedShort(currentOffset + 2);
- int index = readUnsignedShort(currentOffset + 4);
- currentOffset += 6;
- context.currentLocalVariableAnnotationRangeStarts[i] =
- createLabel(startPc, context.currentMethodLabels);
- context.currentLocalVariableAnnotationRangeEnds[i] =
- createLabel(startPc + length, context.currentMethodLabels);
- context.currentLocalVariableAnnotationRangeIndices[i] = index;
+ throw new IllegalArgumentException();
}
- break;
- case TypeReference.CAST:
- case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
- case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
- case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
- case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
- targetType &= 0xFF0000FF;
- currentOffset += 4;
- break;
- case TypeReference.CLASS_EXTENDS:
- case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
- case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
- case TypeReference.THROWS:
- case TypeReference.EXCEPTION_PARAMETER:
- targetType &= 0xFFFFFF00;
- currentOffset += 3;
- break;
- case TypeReference.INSTANCEOF:
- case TypeReference.NEW:
- case TypeReference.CONSTRUCTOR_REFERENCE:
- case TypeReference.METHOD_REFERENCE:
- targetType &= 0xFF000000;
- currentOffset += 3;
- break;
- default:
- throw new IllegalArgumentException();
+ context.currentTypeAnnotationTarget = targetType;
+ // Parse and store the target_path structure.
+ int pathLength = readByte(currentOffset);
+ context.currentTypeAnnotationTargetPath =
+ pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
+ // Return the start offset of the rest of the type_annotation structure.
+ return currentOffset + 1 + 2 * pathLength;
}
- context.currentTypeAnnotationTarget = targetType;
- // Parse and store the target_path structure.
- int pathLength = readByte(currentOffset);
- context.currentTypeAnnotationTargetPath =
- pathLength == 0 ? null : new TypePath(classFileBuffer, currentOffset);
- // Return the start offset of the rest of the type_annotation structure.
- return currentOffset + 1 + 2 * pathLength;
- }
- /**
- * Reads a Runtime[In]VisibleParameterAnnotations attribute and makes the given visitor visit it.
- *
- * @param methodVisitor the visitor that must visit the parameter annotations.
- * @param context information about the class being parsed.
- * @param runtimeParameterAnnotationsOffset the start offset of a
- * Runtime[In]VisibleParameterAnnotations attribute, excluding the attribute_info's
- * attribute_name_index and attribute_length fields.
- * @param visible true if the attribute to parse is a RuntimeVisibleParameterAnnotations
- * attribute, false it is a RuntimeInvisibleParameterAnnotations attribute.
- */
- private void readParameterAnnotations(
- final MethodVisitor methodVisitor,
- final Context context,
- final int runtimeParameterAnnotationsOffset,
- final boolean visible) {
- int currentOffset = runtimeParameterAnnotationsOffset;
- int numParameters = classFileBuffer[currentOffset++] & 0xFF;
- methodVisitor.visitAnnotableParameterCount(numParameters, visible);
- char[] charBuffer = context.charBuffer;
- for (int i = 0; i < numParameters; ++i) {
- int numAnnotations = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (numAnnotations-- > 0) {
- // Parse the type_index field.
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
+ /**
+ * Reads a Runtime[In]VisibleParameterAnnotations attribute and makes the given visitor visit it.
+ *
+ * @param methodVisitor the visitor that must visit the parameter annotations.
+ * @param context information about the class being parsed.
+ * @param runtimeParameterAnnotationsOffset the start offset of a
+ * Runtime[In]VisibleParameterAnnotations attribute, excluding the
+ * attribute_info's
+ * attribute_name_index and attribute_length fields.
+ * @param visible true if the attribute to parse is a RuntimeVisibleParameterAnnotations
+ * attribute, false it is a RuntimeInvisibleParameterAnnotations attribute.
+ */
+ private void readParameterAnnotations(
+ final MethodVisitor methodVisitor,
+ final Context context,
+ final int runtimeParameterAnnotationsOffset,
+ final boolean visible) {
+ int currentOffset = runtimeParameterAnnotationsOffset;
+ int numParameters = classFileBuffer[currentOffset++] & 0xFF;
+ methodVisitor.visitAnnotableParameterCount(numParameters, visible);
+ char[] charBuffer = context.charBuffer;
+ for (int i = 0; i < numParameters; ++i) {
+ int numAnnotations = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ while (numAnnotations-- > 0) {
+ // Parse the type_index field.
+ String annotationDescriptor = readUTF8(currentOffset, charBuffer);
+ currentOffset += 2;
+ // Parse num_element_value_pairs and element_value_pairs and visit these values.
+ currentOffset =
+ readElementValues(
+ methodVisitor.visitParameterAnnotation(i, annotationDescriptor, visible),
+ currentOffset,
+ /* named = */ true,
+ charBuffer);
+ }
+ }
+ }
+
+ /**
+ * Reads the element values of a JVMS 'annotation' structure and makes the given visitor visit
+ * them. This method can also be used to read the values of the JVMS 'array_value' field of an
+ * annotation's 'element_value'.
+ *
+ * @param annotationVisitor the visitor that must visit the values.
+ * @param annotationOffset the start offset of an 'annotation' structure (excluding its type_index
+ * field) or of an 'array_value' structure.
+ * @param named if the annotation values are named or not. This should be true to parse the values
+ * of a JVMS 'annotation' structure, and false to parse the JVMS 'array_value' of an
+ * annotation's element_value.
+ * @param charBuffer the buffer used to read strings in the constant pool.
+ * @return the end offset of the JVMS 'annotation' or 'array_value' structure.
+ */
+ private int readElementValues(
+ final AnnotationVisitor annotationVisitor,
+ final int annotationOffset,
+ final boolean named,
+ final char[] charBuffer) {
+ int currentOffset = annotationOffset;
+ // Read the num_element_value_pairs field (or num_values field for an array_value).
+ int numElementValuePairs = readUnsignedShort(currentOffset);
currentOffset += 2;
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
- currentOffset =
- readElementValues(
- methodVisitor.visitParameterAnnotation(i, annotationDescriptor, visible),
- currentOffset,
- /* named = */ true,
- charBuffer);
- }
+ if (named) {
+ // Parse the element_value_pairs array.
+ while (numElementValuePairs-- > 0) {
+ String elementName = readUTF8(currentOffset, charBuffer);
+ currentOffset =
+ readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer);
+ }
+ } else {
+ // Parse the array_value array.
+ while (numElementValuePairs-- > 0) {
+ currentOffset =
+ readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer);
+ }
+ }
+ if (annotationVisitor != null) {
+ annotationVisitor.visitEnd();
+ }
+ return currentOffset;
}
- }
- /**
- * Reads the element values of a JVMS 'annotation' structure and makes the given visitor visit
- * them. This method can also be used to read the values of the JVMS 'array_value' field of an
- * annotation's 'element_value'.
- *
- * @param annotationVisitor the visitor that must visit the values.
- * @param annotationOffset the start offset of an 'annotation' structure (excluding its type_index
- * field) or of an 'array_value' structure.
- * @param named if the annotation values are named or not. This should be true to parse the values
- * of a JVMS 'annotation' structure, and false to parse the JVMS 'array_value' of an
- * annotation's element_value.
- * @param charBuffer the buffer used to read strings in the constant pool.
- * @return the end offset of the JVMS 'annotation' or 'array_value' structure.
- */
- private int readElementValues(
- final AnnotationVisitor annotationVisitor,
- final int annotationOffset,
- final boolean named,
- final char[] charBuffer) {
- int currentOffset = annotationOffset;
- // Read the num_element_value_pairs field (or num_values field for an array_value).
- int numElementValuePairs = readUnsignedShort(currentOffset);
- currentOffset += 2;
- if (named) {
- // Parse the element_value_pairs array.
- while (numElementValuePairs-- > 0) {
- String elementName = readUTF8(currentOffset, charBuffer);
- currentOffset =
- readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer);
- }
- } else {
- // Parse the array_value array.
- while (numElementValuePairs-- > 0) {
- currentOffset =
- readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer);
- }
- }
- if (annotationVisitor != null) {
- annotationVisitor.visitEnd();
- }
- return currentOffset;
- }
+ /**
+ * Reads a JVMS 'element_value' structure and makes the given visitor visit it.
+ *
+ * @param annotationVisitor the visitor that must visit the element_value structure.
+ * @param elementValueOffset the start offset in {@link #classFileBuffer} of the element_value
+ * structure to be read.
+ * @param elementName the name of the element_value structure to be read, or {@literal null}.
+ * @param charBuffer the buffer used to read strings in the constant pool.
+ * @return the end offset of the JVMS 'element_value' structure.
+ */
+ private int readElementValue(
+ final AnnotationVisitor annotationVisitor,
+ final int elementValueOffset,
+ final String elementName,
+ final char[] charBuffer) {
+ int currentOffset = elementValueOffset;
+ if (annotationVisitor == null) {
+ switch (classFileBuffer[currentOffset] & 0xFF) {
+ case 'e': // enum_const_value
+ return currentOffset + 5;
+ case '@': // annotation_value
+ return readElementValues(null, currentOffset + 3, /* named = */ true, charBuffer);
+ case '[': // array_value
+ return readElementValues(null, currentOffset + 1, /* named = */ false, charBuffer);
+ default:
+ return currentOffset + 3;
+ }
+ }
+ switch (classFileBuffer[currentOffset++] & 0xFF) {
+ case 'B': // const_value_index, CONSTANT_Integer
+ annotationVisitor.visit(
+ elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
+ currentOffset += 2;
+ break;
+ case 'C': // const_value_index, CONSTANT_Integer
+ annotationVisitor.visit(
+ elementName, (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
+ currentOffset += 2;
+ break;
+ case 'D': // const_value_index, CONSTANT_Double
+ case 'F': // const_value_index, CONSTANT_Float
+ case 'I': // const_value_index, CONSTANT_Integer
+ case 'J': // const_value_index, CONSTANT_Long
+ annotationVisitor.visit(
+ elementName, readConst(readUnsignedShort(currentOffset), charBuffer));
+ currentOffset += 2;
+ break;
+ case 'S': // const_value_index, CONSTANT_Integer
+ annotationVisitor.visit(
+ elementName, (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
+ currentOffset += 2;
+ break;
- /**
- * Reads a JVMS 'element_value' structure and makes the given visitor visit it.
- *
- * @param annotationVisitor the visitor that must visit the element_value structure.
- * @param elementValueOffset the start offset in {@link #classFileBuffer} of the element_value
- * structure to be read.
- * @param elementName the name of the element_value structure to be read, or {@literal null}.
- * @param charBuffer the buffer used to read strings in the constant pool.
- * @return the end offset of the JVMS 'element_value' structure.
- */
- private int readElementValue(
- final AnnotationVisitor annotationVisitor,
- final int elementValueOffset,
- final String elementName,
- final char[] charBuffer) {
- int currentOffset = elementValueOffset;
- if (annotationVisitor == null) {
- switch (classFileBuffer[currentOffset] & 0xFF) {
+ case 'Z': // const_value_index, CONSTANT_Integer
+ annotationVisitor.visit(
+ elementName,
+ readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]) == 0
+ ? Boolean.FALSE
+ : Boolean.TRUE);
+ currentOffset += 2;
+ break;
+ case 's': // const_value_index, CONSTANT_Utf8
+ annotationVisitor.visit(elementName, readUTF8(currentOffset, charBuffer));
+ currentOffset += 2;
+ break;
case 'e': // enum_const_value
- return currentOffset + 5;
+ annotationVisitor.visitEnum(
+ elementName,
+ readUTF8(currentOffset, charBuffer),
+ readUTF8(currentOffset + 2, charBuffer));
+ currentOffset += 4;
+ break;
+ case 'c': // class_info
+ annotationVisitor.visit(elementName, Type.getType(readUTF8(currentOffset, charBuffer)));
+ currentOffset += 2;
+ break;
case '@': // annotation_value
- return readElementValues(null, currentOffset + 3, /* named = */ true, charBuffer);
- case '[': // array_value
- return readElementValues(null, currentOffset + 1, /* named = */ false, charBuffer);
- default:
- return currentOffset + 3;
- }
- }
- switch (classFileBuffer[currentOffset++] & 0xFF) {
- case 'B': // const_value_index, CONSTANT_Integer
- annotationVisitor.visit(
- elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
- currentOffset += 2;
- break;
- case 'C': // const_value_index, CONSTANT_Integer
- annotationVisitor.visit(
- elementName, (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
- currentOffset += 2;
- break;
- case 'D': // const_value_index, CONSTANT_Double
- case 'F': // const_value_index, CONSTANT_Float
- case 'I': // const_value_index, CONSTANT_Integer
- case 'J': // const_value_index, CONSTANT_Long
- annotationVisitor.visit(
- elementName, readConst(readUnsignedShort(currentOffset), charBuffer));
- currentOffset += 2;
- break;
- case 'S': // const_value_index, CONSTANT_Integer
- annotationVisitor.visit(
- elementName, (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
- currentOffset += 2;
- break;
-
- case 'Z': // const_value_index, CONSTANT_Integer
- annotationVisitor.visit(
- elementName,
- readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]) == 0
- ? Boolean.FALSE
- : Boolean.TRUE);
- currentOffset += 2;
- break;
- case 's': // const_value_index, CONSTANT_Utf8
- annotationVisitor.visit(elementName, readUTF8(currentOffset, charBuffer));
- currentOffset += 2;
- break;
- case 'e': // enum_const_value
- annotationVisitor.visitEnum(
- elementName,
- readUTF8(currentOffset, charBuffer),
- readUTF8(currentOffset + 2, charBuffer));
- currentOffset += 4;
- break;
- case 'c': // class_info
- annotationVisitor.visit(elementName, Type.getType(readUTF8(currentOffset, charBuffer)));
- currentOffset += 2;
- break;
- case '@': // annotation_value
- currentOffset =
- readElementValues(
- annotationVisitor.visitAnnotation(elementName, readUTF8(currentOffset, charBuffer)),
- currentOffset + 2,
- true,
- charBuffer);
- break;
- case '[': // array_value
- int numValues = readUnsignedShort(currentOffset);
- currentOffset += 2;
- if (numValues == 0) {
- return readElementValues(
- annotationVisitor.visitArray(elementName),
- currentOffset - 2,
- /* named = */ false,
- charBuffer);
- }
- switch (classFileBuffer[currentOffset] & 0xFF) {
- case 'B':
- byte[] byteValues = new byte[numValues];
- for (int i = 0; i < numValues; i++) {
- byteValues[i] = (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, byteValues);
- break;
- case 'Z':
- boolean[] booleanValues = new boolean[numValues];
- for (int i = 0; i < numValues; i++) {
- booleanValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]) != 0;
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, booleanValues);
- break;
- case 'S':
- short[] shortValues = new short[numValues];
- for (int i = 0; i < numValues; i++) {
- shortValues[i] = (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, shortValues);
- break;
- case 'C':
- char[] charValues = new char[numValues];
- for (int i = 0; i < numValues; i++) {
- charValues[i] = (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, charValues);
- break;
- case 'I':
- int[] intValues = new int[numValues];
- for (int i = 0; i < numValues; i++) {
- intValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, intValues);
- break;
- case 'J':
- long[] longValues = new long[numValues];
- for (int i = 0; i < numValues; i++) {
- longValues[i] = readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, longValues);
- break;
- case 'F':
- float[] floatValues = new float[numValues];
- for (int i = 0; i < numValues; i++) {
- floatValues[i] =
- Float.intBitsToFloat(
- readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]));
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, floatValues);
- break;
- case 'D':
- double[] doubleValues = new double[numValues];
- for (int i = 0; i < numValues; i++) {
- doubleValues[i] =
- Double.longBitsToDouble(
- readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]));
- currentOffset += 3;
- }
- annotationVisitor.visit(elementName, doubleValues);
- break;
- default:
currentOffset =
readElementValues(
- annotationVisitor.visitArray(elementName),
- currentOffset - 2,
- /* named = */ false,
+ annotationVisitor.visitAnnotation(elementName, readUTF8(currentOffset, charBuffer)),
+ currentOffset + 2,
+ true,
charBuffer);
break;
- }
- break;
- default:
- throw new IllegalArgumentException();
- }
- return currentOffset;
- }
-
- // ----------------------------------------------------------------------------------------------
- // Methods to parse stack map frames
- // ----------------------------------------------------------------------------------------------
-
- /**
- * Computes the implicit frame of the method currently being parsed (as defined in the given
- * {@link Context}) and stores it in the given context.
- *
- * @param context information about the class being parsed.
- */
- private void computeImplicitFrame(final Context context) {
- String methodDescriptor = context.currentMethodDescriptor;
- Object[] locals = context.currentFrameLocalTypes;
- int numLocal = 0;
- if ((context.currentMethodAccessFlags & Opcodes.ACC_STATIC) == 0) {
- if ("".equals(context.currentMethodName)) {
- locals[numLocal++] = Opcodes.UNINITIALIZED_THIS;
- } else {
- locals[numLocal++] = readClass(header + 2, context.charBuffer);
- }
- }
- // Parse the method descriptor, one argument type descriptor at each iteration. Start by
- // skipping the first method descriptor character, which is always '('.
- int currentMethodDescritorOffset = 1;
- while (true) {
- int currentArgumentDescriptorStartOffset = currentMethodDescritorOffset;
- switch (methodDescriptor.charAt(currentMethodDescritorOffset++)) {
- case 'Z':
- case 'C':
- case 'B':
- case 'S':
- case 'I':
- locals[numLocal++] = Opcodes.INTEGER;
- break;
- case 'F':
- locals[numLocal++] = Opcodes.FLOAT;
- break;
- case 'J':
- locals[numLocal++] = Opcodes.LONG;
- break;
- case 'D':
- locals[numLocal++] = Opcodes.DOUBLE;
- break;
- case '[':
- while (methodDescriptor.charAt(currentMethodDescritorOffset) == '[') {
- ++currentMethodDescritorOffset;
- }
- if (methodDescriptor.charAt(currentMethodDescritorOffset) == 'L') {
- ++currentMethodDescritorOffset;
- while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
- ++currentMethodDescritorOffset;
+ case '[': // array_value
+ int numValues = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ if (numValues == 0) {
+ return readElementValues(
+ annotationVisitor.visitArray(elementName),
+ currentOffset - 2,
+ /* named = */ false,
+ charBuffer);
}
- }
- locals[numLocal++] =
- methodDescriptor.substring(
- currentArgumentDescriptorStartOffset, ++currentMethodDescritorOffset);
- break;
- case 'L':
- while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
- ++currentMethodDescritorOffset;
- }
- locals[numLocal++] =
- methodDescriptor.substring(
- currentArgumentDescriptorStartOffset + 1, currentMethodDescritorOffset++);
- break;
+ switch (classFileBuffer[currentOffset] & 0xFF) {
+ case 'B':
+ byte[] byteValues = new byte[numValues];
+ for (int i = 0; i < numValues; i++) {
+ byteValues[i] = (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, byteValues);
+ break;
+ case 'Z':
+ boolean[] booleanValues = new boolean[numValues];
+ for (int i = 0; i < numValues; i++) {
+ booleanValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]) != 0;
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, booleanValues);
+ break;
+ case 'S':
+ short[] shortValues = new short[numValues];
+ for (int i = 0; i < numValues; i++) {
+ shortValues[i] = (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, shortValues);
+ break;
+ case 'C':
+ char[] charValues = new char[numValues];
+ for (int i = 0; i < numValues; i++) {
+ charValues[i] = (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, charValues);
+ break;
+ case 'I':
+ int[] intValues = new int[numValues];
+ for (int i = 0; i < numValues; i++) {
+ intValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, intValues);
+ break;
+ case 'J':
+ long[] longValues = new long[numValues];
+ for (int i = 0; i < numValues; i++) {
+ longValues[i] = readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, longValues);
+ break;
+ case 'F':
+ float[] floatValues = new float[numValues];
+ for (int i = 0; i < numValues; i++) {
+ floatValues[i] =
+ Float.intBitsToFloat(
+ readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]));
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, floatValues);
+ break;
+ case 'D':
+ double[] doubleValues = new double[numValues];
+ for (int i = 0; i < numValues; i++) {
+ doubleValues[i] =
+ Double.longBitsToDouble(
+ readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]));
+ currentOffset += 3;
+ }
+ annotationVisitor.visit(elementName, doubleValues);
+ break;
+ default:
+ currentOffset =
+ readElementValues(
+ annotationVisitor.visitArray(elementName),
+ currentOffset - 2,
+ /* named = */ false,
+ charBuffer);
+ break;
+ }
+ break;
default:
- context.currentFrameLocalCount = numLocal;
- return;
- }
+ throw new IllegalArgumentException();
+ }
+ return currentOffset;
}
- }
- /**
- * Reads a JVMS 'stack_map_frame' structure and stores the result in the given {@link Context}
- * object. This method can also be used to read a full_frame structure, excluding its frame_type
- * field (this is used to parse the legacy StackMap attributes).
- *
- * @param stackMapFrameOffset the start offset in {@link #classFileBuffer} of the
- * stack_map_frame_value structure to be read, or the start offset of a full_frame structure
- * (excluding its frame_type field).
- * @param compressed true to read a 'stack_map_frame' structure, false to read a 'full_frame'
- * structure without its frame_type field.
- * @param expand if the stack map frame must be expanded. See {@link #EXPAND_FRAMES}.
- * @param context where the parsed stack map frame must be stored.
- * @return the end offset of the JVMS 'stack_map_frame' or 'full_frame' structure.
- */
- private int readStackMapFrame(
- final int stackMapFrameOffset,
- final boolean compressed,
- final boolean expand,
- final Context context) {
- int currentOffset = stackMapFrameOffset;
- final char[] charBuffer = context.charBuffer;
- final Label[] labels = context.currentMethodLabels;
- int frameType;
- if (compressed) {
- // Read the frame_type field.
- frameType = classFileBuffer[currentOffset++] & 0xFF;
- } else {
- frameType = Frame.FULL_FRAME;
- context.currentFrameOffset = -1;
- }
- int offsetDelta;
- context.currentFrameLocalCountDelta = 0;
- if (frameType < Frame.SAME_LOCALS_1_STACK_ITEM_FRAME) {
- offsetDelta = frameType;
- context.currentFrameType = Opcodes.F_SAME;
- context.currentFrameStackCount = 0;
- } else if (frameType < Frame.RESERVED) {
- offsetDelta = frameType - Frame.SAME_LOCALS_1_STACK_ITEM_FRAME;
- currentOffset =
- readVerificationTypeInfo(
- currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels);
- context.currentFrameType = Opcodes.F_SAME1;
- context.currentFrameStackCount = 1;
- } else if (frameType >= Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
- offsetDelta = readUnsignedShort(currentOffset);
- currentOffset += 2;
- if (frameType == Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
- currentOffset =
- readVerificationTypeInfo(
- currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels);
- context.currentFrameType = Opcodes.F_SAME1;
- context.currentFrameStackCount = 1;
- } else if (frameType >= Frame.CHOP_FRAME && frameType < Frame.SAME_FRAME_EXTENDED) {
- context.currentFrameType = Opcodes.F_CHOP;
- context.currentFrameLocalCountDelta = Frame.SAME_FRAME_EXTENDED - frameType;
- context.currentFrameLocalCount -= context.currentFrameLocalCountDelta;
- context.currentFrameStackCount = 0;
- } else if (frameType == Frame.SAME_FRAME_EXTENDED) {
- context.currentFrameType = Opcodes.F_SAME;
- context.currentFrameStackCount = 0;
- } else if (frameType < Frame.FULL_FRAME) {
- int local = expand ? context.currentFrameLocalCount : 0;
- for (int k = frameType - Frame.SAME_FRAME_EXTENDED; k > 0; k--) {
- currentOffset =
- readVerificationTypeInfo(
- currentOffset, context.currentFrameLocalTypes, local++, charBuffer, labels);
- }
- context.currentFrameType = Opcodes.F_APPEND;
- context.currentFrameLocalCountDelta = frameType - Frame.SAME_FRAME_EXTENDED;
- context.currentFrameLocalCount += context.currentFrameLocalCountDelta;
- context.currentFrameStackCount = 0;
- } else {
- final int numberOfLocals = readUnsignedShort(currentOffset);
- currentOffset += 2;
- context.currentFrameType = Opcodes.F_FULL;
- context.currentFrameLocalCountDelta = numberOfLocals;
- context.currentFrameLocalCount = numberOfLocals;
- for (int local = 0; local < numberOfLocals; ++local) {
- currentOffset =
- readVerificationTypeInfo(
- currentOffset, context.currentFrameLocalTypes, local, charBuffer, labels);
- }
- final int numberOfStackItems = readUnsignedShort(currentOffset);
- currentOffset += 2;
- context.currentFrameStackCount = numberOfStackItems;
- for (int stack = 0; stack < numberOfStackItems; ++stack) {
- currentOffset =
- readVerificationTypeInfo(
- currentOffset, context.currentFrameStackTypes, stack, charBuffer, labels);
- }
- }
- } else {
- throw new IllegalArgumentException();
- }
- context.currentFrameOffset += offsetDelta + 1;
- createLabel(context.currentFrameOffset, labels);
- return currentOffset;
- }
+ // ----------------------------------------------------------------------------------------------
+ // Methods to parse stack map frames
+ // ----------------------------------------------------------------------------------------------
- /**
- * Reads a JVMS 'verification_type_info' structure and stores it at the given index in the given
- * array.
- *
- * @param verificationTypeInfoOffset the start offset of the 'verification_type_info' structure to
- * read.
- * @param frame the array where the parsed type must be stored.
- * @param index the index in 'frame' where the parsed type must be stored.
- * @param charBuffer the buffer used to read strings in the constant pool.
- * @param labels the labels of the method currently being parsed, indexed by their offset. If the
- * parsed type is an ITEM_Uninitialized, a new label for the corresponding NEW instruction is
- * stored in this array if it does not already exist.
- * @return the end offset of the JVMS 'verification_type_info' structure.
- */
- private int readVerificationTypeInfo(
- final int verificationTypeInfoOffset,
- final Object[] frame,
- final int index,
- final char[] charBuffer,
- final Label[] labels) {
- int currentOffset = verificationTypeInfoOffset;
- int tag = classFileBuffer[currentOffset++] & 0xFF;
- switch (tag) {
- case Frame.ITEM_TOP:
- frame[index] = Opcodes.TOP;
- break;
- case Frame.ITEM_INTEGER:
- frame[index] = Opcodes.INTEGER;
- break;
- case Frame.ITEM_FLOAT:
- frame[index] = Opcodes.FLOAT;
- break;
- case Frame.ITEM_DOUBLE:
- frame[index] = Opcodes.DOUBLE;
- break;
- case Frame.ITEM_LONG:
- frame[index] = Opcodes.LONG;
- break;
- case Frame.ITEM_NULL:
- frame[index] = Opcodes.NULL;
- break;
- case Frame.ITEM_UNINITIALIZED_THIS:
- frame[index] = Opcodes.UNINITIALIZED_THIS;
- break;
- case Frame.ITEM_OBJECT:
- frame[index] = readClass(currentOffset, charBuffer);
+ /**
+ * Computes the implicit frame of the method currently being parsed (as defined in the given
+ * {@link Context}) and stores it in the given context.
+ *
+ * @param context information about the class being parsed.
+ */
+ private void computeImplicitFrame(final Context context) {
+ String methodDescriptor = context.currentMethodDescriptor;
+ Object[] locals = context.currentFrameLocalTypes;
+ int numLocal = 0;
+ if ((context.currentMethodAccessFlags & OpcodesC_STATIC) == 0) {
+ if ("".equals(context.currentMethodName)) {
+ locals[numLocal++] = Opcodes.UNINITIALIZED_THIS;
+ } else {
+ locals[numLocal++] = readClass(header + 2, context.charBuffer);
+ }
+ }
+ // Parse the method descriptor, one argument type descriptor at each iteration. Start by
+ // skipping the first method descriptor character, which is always '('.
+ int currentMethodDescritorOffset = 1;
+ while (true) {
+ int currentArgumentDescriptorStartOffset = currentMethodDescritorOffset;
+ switch (methodDescriptor.charAt(currentMethodDescritorOffset++)) {
+ case 'Z':
+ case 'C':
+ case 'B':
+ case 'S':
+ case 'I':
+ locals[numLocal++] = Opcodes.INTEGER;
+ break;
+ case 'F':
+ locals[numLocal++] = Opcodes.FLOAT;
+ break;
+ case 'J':
+ locals[numLocal++] = Opcodes.LONG;
+ break;
+ case 'D':
+ locals[numLocal++] = Opcodes.DOUBLE;
+ break;
+ case '[':
+ while (methodDescriptor.charAt(currentMethodDescritorOffset) == '[') {
+ ++currentMethodDescritorOffset;
+ }
+ if (methodDescriptor.charAt(currentMethodDescritorOffset) == 'L') {
+ ++currentMethodDescritorOffset;
+ while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
+ ++currentMethodDescritorOffset;
+ }
+ }
+ locals[numLocal++] =
+ methodDescriptor.substring(
+ currentArgumentDescriptorStartOffset, ++currentMethodDescritorOffset);
+ break;
+ case 'L':
+ while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
+ ++currentMethodDescritorOffset;
+ }
+ locals[numLocal++] =
+ methodDescriptor.substring(
+ currentArgumentDescriptorStartOffset + 1, currentMethodDescritorOffset++);
+ break;
+ default:
+ context.currentFrameLocalCount = numLocal;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Reads a JVMS 'stack_map_frame' structure and stores the result in the given {@link Context}
+ * object. This method can also be used to read a full_frame structure, excluding its frame_type
+ * field (this is used to parse the legacy StackMap attributes).
+ *
+ * @param stackMapFrameOffset the start offset in {@link #classFileBuffer} of the
+ * stack_map_frame_value structure to be read, or the start offset of a full_frame
+ * structure
+ * (excluding its frame_type field).
+ * @param compressed true to read a 'stack_map_frame' structure, false to read a 'full_frame'
+ * structure without its frame_type field.
+ * @param expand if the stack map frame must be expanded. See {@link #EXPAND_FRAMES}.
+ * @param context where the parsed stack map frame must be stored.
+ * @return the end offset of the JVMS 'stack_map_frame' or 'full_frame' structure.
+ */
+ private int readStackMapFrame(
+ final int stackMapFrameOffset,
+ final boolean compressed,
+ final boolean expand,
+ final Context context) {
+ int currentOffset = stackMapFrameOffset;
+ final char[] charBuffer = context.charBuffer;
+ final Label[] labels = context.currentMethodLabels;
+ int frameType;
+ if (compressed) {
+ // Read the frame_type field.
+ frameType = classFileBuffer[currentOffset++] & 0xFF;
+ } else {
+ frameType = Frame.FULL_FRAME;
+ context.currentFrameOffset = -1;
+ }
+ int offsetDelta;
+ context.currentFrameLocalCountDelta = 0;
+ if (frameType < Frame.SAME_LOCALS_1_STACK_ITEM_FRAME) {
+ offsetDelta = frameType;
+ context.currentFrameType = Opcodes.F_SAME;
+ context.currentFrameStackCount = 0;
+ } else if (frameType < Frame.RESERVED) {
+ offsetDelta = frameType - Frame.SAME_LOCALS_1_STACK_ITEM_FRAME;
+ currentOffset =
+ readVerificationTypeInfo(
+ currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels);
+ context.currentFrameType = Opcodes.F_SAME1;
+ context.currentFrameStackCount = 1;
+ } else if (frameType >= Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
+ offsetDelta = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ if (frameType == Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
+ currentOffset =
+ readVerificationTypeInfo(
+ currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels);
+ context.currentFrameType = Opcodes.F_SAME1;
+ context.currentFrameStackCount = 1;
+ } else if (frameType >= Frame.CHOP_FRAME && frameType < Frame.SAME_FRAME_EXTENDED) {
+ context.currentFrameType = Opcodes.F_CHOP;
+ context.currentFrameLocalCountDelta = Frame.SAME_FRAME_EXTENDED - frameType;
+ context.currentFrameLocalCount -= context.currentFrameLocalCountDelta;
+ context.currentFrameStackCount = 0;
+ } else if (frameType == Frame.SAME_FRAME_EXTENDED) {
+ context.currentFrameType = Opcodes.F_SAME;
+ context.currentFrameStackCount = 0;
+ } else if (frameType < Frame.FULL_FRAME) {
+ int local = expand ? context.currentFrameLocalCount : 0;
+ for (int k = frameType - Frame.SAME_FRAME_EXTENDED; k > 0; k--) {
+ currentOffset =
+ readVerificationTypeInfo(
+ currentOffset, context.currentFrameLocalTypes, local++, charBuffer, labels);
+ }
+ context.currentFrameType = Opcodes.F_APPEND;
+ context.currentFrameLocalCountDelta = frameType - Frame.SAME_FRAME_EXTENDED;
+ context.currentFrameLocalCount += context.currentFrameLocalCountDelta;
+ context.currentFrameStackCount = 0;
+ } else {
+ final int numberOfLocals = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ context.currentFrameType = Opcodes.F_FULL;
+ context.currentFrameLocalCountDelta = numberOfLocals;
+ context.currentFrameLocalCount = numberOfLocals;
+ for (int local = 0; local < numberOfLocals; ++local) {
+ currentOffset =
+ readVerificationTypeInfo(
+ currentOffset, context.currentFrameLocalTypes, local, charBuffer, labels);
+ }
+ final int numberOfStackItems = readUnsignedShort(currentOffset);
+ currentOffset += 2;
+ context.currentFrameStackCount = numberOfStackItems;
+ for (int stack = 0; stack < numberOfStackItems; ++stack) {
+ currentOffset =
+ readVerificationTypeInfo(
+ currentOffset, context.currentFrameStackTypes, stack, charBuffer, labels);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+ context.currentFrameOffset += offsetDelta + 1;
+ createLabel(context.currentFrameOffset, labels);
+ return currentOffset;
+ }
+
+ /**
+ * Reads a JVMS 'verification_type_info' structure and stores it at the given index in the given
+ * array.
+ *
+ * @param verificationTypeInfoOffset the start offset of the 'verification_type_info' structure to
+ * read.
+ * @param frame the array where the parsed type must be stored.
+ * @param index the index in 'frame' where the parsed type must be stored.
+ * @param charBuffer the buffer used to read strings in the constant pool.
+ * @param labels the labels of the method currently being parsed, indexed by their offset. If
+ * the
+ * parsed type is an ITEM_Uninitialized, a new label for the corresponding NEW
+ * instruction is
+ * stored in this array if it does not already exist.
+ * @return the end offset of the JVMS 'verification_type_info' structure.
+ */
+ private int readVerificationTypeInfo(
+ final int verificationTypeInfoOffset,
+ final Object[] frame,
+ final int index,
+ final char[] charBuffer,
+ final Label[] labels) {
+ int currentOffset = verificationTypeInfoOffset;
+ int tag = classFileBuffer[currentOffset++] & 0xFF;
+ switch (tag) {
+ case Frame.ITEM_TOP:
+ frame[index] = Opcodes.TOP;
+ break;
+ case Frame.ITEM_INTEGER:
+ frame[index] = Opcodes.INTEGER;
+ break;
+ case Frame.ITEM_FLOAT:
+ frame[index] = Opcodes.FLOAT;
+ break;
+ case Frame.ITEM_DOUBLE:
+ frame[index] = Opcodes.DOUBLE;
+ break;
+ case Frame.ITEM_LONG:
+ frame[index] = Opcodes.LONG;
+ break;
+ case Frame.ITEM_NULL:
+ frame[index] = Opcodes.NULL;
+ break;
+ case Frame.ITEM_UNINITIALIZED_THIS:
+ frame[index] = Opcodes.UNINITIALIZED_THIS;
+ break;
+ case Frame.ITEM_OBJECT:
+ frame[index] = readClass(currentOffset, charBuffer);
+ currentOffset += 2;
+ break;
+ case Frame.ITEM_UNINITIALIZED:
+ frame[index] = createLabel(readUnsignedShort(currentOffset), labels);
+ currentOffset += 2;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ return currentOffset;
+ }
+
+ // ----------------------------------------------------------------------------------------------
+ // Methods to parse attributes
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
+ * field entry.
+ *
+ * @return the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
+ * field entry.
+ */
+ final int getFirstAttributeOffset() {
+ // Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
+ // each), as well as the interfaces array field (2 bytes per interface).
+ int currentOffset = header + 8 + readUnsignedShort(header + 6) * 2;
+
+ // Read the fields_count field.
+ int fieldsCount = readUnsignedShort(currentOffset);
currentOffset += 2;
- break;
- case Frame.ITEM_UNINITIALIZED:
- frame[index] = createLabel(readUnsignedShort(currentOffset), labels);
+ // Skip the 'fields' array field.
+ while (fieldsCount-- > 0) {
+ // Invariant: currentOffset is the offset of a field_info structure.
+ // Skip the access_flags, name_index and descriptor_index fields (2 bytes each), and read the
+ // attributes_count field.
+ int attributesCount = readUnsignedShort(currentOffset + 6);
+ currentOffset += 8;
+ // Skip the 'attributes' array field.
+ while (attributesCount-- > 0) {
+ // Invariant: currentOffset is the offset of an attribute_info structure.
+ // Read the attribute_length field (2 bytes after the start of the attribute_info) and skip
+ // this many bytes, plus 6 for the attribute_name_index and attribute_length fields
+ // (yielding the total size of the attribute_info structure).
+ currentOffset += 6 + readInt(currentOffset + 2);
+ }
+ }
+
+ // Skip the methods_count and 'methods' fields, using the same method as above.
+ int methodsCount = readUnsignedShort(currentOffset);
currentOffset += 2;
- break;
- default:
+ while (methodsCount-- > 0) {
+ int attributesCount = readUnsignedShort(currentOffset + 6);
+ currentOffset += 8;
+ while (attributesCount-- > 0) {
+ currentOffset += 6 + readInt(currentOffset + 2);
+ }
+ }
+
+ // Skip the ClassFile's attributes_count field.
+ return currentOffset + 2;
+ }
+
+ /**
+ * Reads the BootstrapMethods attribute to compute the offset of each bootstrap method.
+ *
+ * @param maxStringLength a conservative estimate of the maximum length of the strings contained
+ * in the constant pool of the class.
+ * @return the offsets of the bootstrap methods.
+ */
+ private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
+ char[] charBuffer = new char[maxStringLength];
+ int currentAttributeOffset = getFirstAttributeOffset();
+ int[] currentBootstrapMethodOffsets = null;
+ for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
+ // Read the attribute_info's attribute_name and attribute_length fields.
+ String attributeName = readUTF8(currentAttributeOffset, charBuffer);
+ int attributeLength = readInt(currentAttributeOffset + 2);
+ currentAttributeOffset += 6;
+ if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
+ // Read the num_bootstrap_methods field and create an array of this size.
+ currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)];
+ // Compute and store the offset of each 'bootstrap_methods' array field entry.
+ int currentBootstrapMethodOffset = currentAttributeOffset + 2;
+ for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) {
+ currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset;
+ // Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each),
+ // as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2).
+ currentBootstrapMethodOffset +=
+ 4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
+ }
+ return currentBootstrapMethodOffsets;
+ }
+ currentAttributeOffset += attributeLength;
+ }
throw new IllegalArgumentException();
}
- return currentOffset;
- }
- // ----------------------------------------------------------------------------------------------
- // Methods to parse attributes
- // ----------------------------------------------------------------------------------------------
-
- /**
- * Returns the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
- * field entry.
- *
- * @return the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array
- * field entry.
- */
- final int getFirstAttributeOffset() {
- // Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
- // each), as well as the interfaces array field (2 bytes per interface).
- int currentOffset = header + 8 + readUnsignedShort(header + 6) * 2;
-
- // Read the fields_count field.
- int fieldsCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- // Skip the 'fields' array field.
- while (fieldsCount-- > 0) {
- // Invariant: currentOffset is the offset of a field_info structure.
- // Skip the access_flags, name_index and descriptor_index fields (2 bytes each), and read the
- // attributes_count field.
- int attributesCount = readUnsignedShort(currentOffset + 6);
- currentOffset += 8;
- // Skip the 'attributes' array field.
- while (attributesCount-- > 0) {
- // Invariant: currentOffset is the offset of an attribute_info structure.
- // Read the attribute_length field (2 bytes after the start of the attribute_info) and skip
- // this many bytes, plus 6 for the attribute_name_index and attribute_length fields
- // (yielding the total size of the attribute_info structure).
- currentOffset += 6 + readInt(currentOffset + 2);
- }
- }
-
- // Skip the methods_count and 'methods' fields, using the same method as above.
- int methodsCount = readUnsignedShort(currentOffset);
- currentOffset += 2;
- while (methodsCount-- > 0) {
- int attributesCount = readUnsignedShort(currentOffset + 6);
- currentOffset += 8;
- while (attributesCount-- > 0) {
- currentOffset += 6 + readInt(currentOffset + 2);
- }
- }
-
- // Skip the ClassFile's attributes_count field.
- return currentOffset + 2;
- }
-
- /**
- * Reads the BootstrapMethods attribute to compute the offset of each bootstrap method.
- *
- * @param maxStringLength a conservative estimate of the maximum length of the strings contained
- * in the constant pool of the class.
- * @return the offsets of the bootstrap methods.
- */
- private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
- char[] charBuffer = new char[maxStringLength];
- int currentAttributeOffset = getFirstAttributeOffset();
- int[] currentBootstrapMethodOffsets = null;
- for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
- // Read the attribute_info's attribute_name and attribute_length fields.
- String attributeName = readUTF8(currentAttributeOffset, charBuffer);
- int attributeLength = readInt(currentAttributeOffset + 2);
- currentAttributeOffset += 6;
- if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
- // Read the num_bootstrap_methods field and create an array of this size.
- currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)];
- // Compute and store the offset of each 'bootstrap_methods' array field entry.
- int currentBootstrapMethodOffset = currentAttributeOffset + 2;
- for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) {
- currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset;
- // Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each),
- // as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2).
- currentBootstrapMethodOffset +=
- 4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
+ /**
+ * Reads a non standard JVMS 'attribute' structure in {@link #classFileBuffer}.
+ *
+ * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
+ * the class. Any attribute whose type is not equal to the type of one the prototypes
+ * will not
+ * be parsed: its byte array value will be passed unchanged to the ClassWriter.
+ * @param type the type of the attribute.
+ * @param offset the start offset of the JVMS 'attribute' structure in {@link #classFileBuffer}.
+ * The 6 attribute header bytes (attribute_name_index and attribute_length) are not
+ * taken into
+ * account here.
+ * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
+ * @param charBuffer the buffer to be used to read strings in the constant pool.
+ * @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link
+ * #classFileBuffer}, or -1 if the attribute to be read is not a code attribute. The 6
+ * attribute header bytes (attribute_name_index and attribute_length) are not taken into
+ * account here.
+ * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
+ * is not a code attribute.
+ * @return the attribute that has been read.
+ */
+ private Attribute readAttribute(
+ final Attribute[] attributePrototypes,
+ final String type,
+ final int offset,
+ final int length,
+ final char[] charBuffer,
+ final int codeAttributeOffset,
+ final Label[] labels) {
+ for (Attribute attributePrototype : attributePrototypes) {
+ if (attributePrototype.type.equals(type)) {
+ return attributePrototype.read(
+ this, offset, length, charBuffer, codeAttributeOffset, labels);
+ }
}
- return currentBootstrapMethodOffsets;
- }
- currentAttributeOffset += attributeLength;
+ return new Attribute(type).read(this, offset, length, null, -1, null);
}
- throw new IllegalArgumentException();
- }
- /**
- * Reads a non standard JVMS 'attribute' structure in {@link #classFileBuffer}.
- *
- * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
- * the class. Any attribute whose type is not equal to the type of one the prototypes will not
- * be parsed: its byte array value will be passed unchanged to the ClassWriter.
- * @param type the type of the attribute.
- * @param offset the start offset of the JVMS 'attribute' structure in {@link #classFileBuffer}.
- * The 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
- * account here.
- * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
- * @param charBuffer the buffer to be used to read strings in the constant pool.
- * @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link
- * #classFileBuffer}, or -1 if the attribute to be read is not a code attribute. The 6
- * attribute header bytes (attribute_name_index and attribute_length) are not taken into
- * account here.
- * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
- * is not a code attribute.
- * @return the attribute that has been read.
- */
- private Attribute readAttribute(
- final Attribute[] attributePrototypes,
- final String type,
- final int offset,
- final int length,
- final char[] charBuffer,
- final int codeAttributeOffset,
- final Label[] labels) {
- for (Attribute attributePrototype : attributePrototypes) {
- if (attributePrototype.type.equals(type)) {
- return attributePrototype.read(
- this, offset, length, charBuffer, codeAttributeOffset, labels);
- }
+ // -----------------------------------------------------------------------------------------------
+ // Utility methods: low level parsing
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the number of entries in the class's constant pool table.
+ *
+ * @return the number of entries in the class's constant pool table.
+ */
+ public int getItemCount() {
+ return cpInfoOffsets.length;
}
- return new Attribute(type).read(this, offset, length, null, -1, null);
- }
- // -----------------------------------------------------------------------------------------------
- // Utility methods: low level parsing
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Returns the number of entries in the class's constant pool table.
- *
- * @return the number of entries in the class's constant pool table.
- */
- public int getItemCount() {
- return cpInfoOffsets.length;
- }
-
- /**
- * Returns the start offset in this {@link ClassReader} of a JVMS 'cp_info' structure (i.e. a
- * constant pool entry), plus one. This method is intended for {@link Attribute} sub classes,
- * and is normally not needed by class generators or adapters.
- *
- * @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool
- * table.
- * @return the start offset in this {@link ClassReader} of the corresponding JVMS 'cp_info'
- * structure, plus one.
- */
- public int getItem(final int constantPoolEntryIndex) {
- return cpInfoOffsets[constantPoolEntryIndex];
- }
-
- /**
- * Returns a conservative estimate of the maximum length of the strings contained in the class's
- * constant pool table.
- *
- * @return a conservative estimate of the maximum length of the strings contained in the class's
- * constant pool table.
- */
- public int getMaxStringLength() {
- return maxStringLength;
- }
-
- /**
- * Reads a byte value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
- * @return the read value.
- */
- public int readByte(final int offset) {
- return classFileBuffer[offset] & 0xFF;
- }
-
- /**
- * Reads an unsigned short value in this {@link ClassReader}. This method is intended for
- * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param offset the start index of the value to be read in this {@link ClassReader}.
- * @return the read value.
- */
- public int readUnsignedShort(final int offset) {
- byte[] classBuffer = classFileBuffer;
- return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);
- }
-
- /**
- * Reads a signed short value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
- * @return the read value.
- */
- public short readShort(final int offset) {
- byte[] classBuffer = classFileBuffer;
- return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));
- }
-
- /**
- * Reads a signed int value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
- * @return the read value.
- */
- public int readInt(final int offset) {
- byte[] classBuffer = classFileBuffer;
- return ((classBuffer[offset] & 0xFF) << 24)
- | ((classBuffer[offset + 1] & 0xFF) << 16)
- | ((classBuffer[offset + 2] & 0xFF) << 8)
- | (classBuffer[offset + 3] & 0xFF);
- }
-
- /**
- * Reads a signed long value in this {@link ClassReader}. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param offset the start offset of the value to be read in this {@link ClassReader}.
- * @return the read value.
- */
- public long readLong(final int offset) {
- long l1 = readInt(offset);
- long l0 = readInt(offset + 4) & 0xFFFFFFFFL;
- return (l1 << 32) | l0;
- }
-
- /**
- * Reads a CONSTANT_Utf8 constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
- * value is the index of a CONSTANT_Utf8 entry in the class's constant pool table.
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified CONSTANT_Utf8 entry.
- */
- // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
- public String readUTF8(final int offset, final char[] charBuffer) {
- int constantPoolEntryIndex = readUnsignedShort(offset);
- if (offset == 0 || constantPoolEntryIndex == 0) {
- return null;
+ /**
+ * Returns the start offset in this {@link ClassReader} of a JVMS 'cp_info' structure (i.e. a
+ * constant pool entry), plus one. This method is intended for {@link Attribute} sub classes,
+ * and is normally not needed by class generators or adapters.
+ *
+ * @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool
+ * table.
+ * @return the start offset in this {@link ClassReader} of the corresponding JVMS 'cp_info'
+ * structure, plus one.
+ */
+ public int getItem(final int constantPoolEntryIndex) {
+ return cpInfoOffsets[constantPoolEntryIndex];
}
- return readUtf(constantPoolEntryIndex, charBuffer);
- }
- /**
- * Reads a CONSTANT_Utf8 constant pool entry in {@link #classFileBuffer}.
- *
- * @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool
- * table.
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified CONSTANT_Utf8 entry.
- */
- final String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) {
- String value = constantUtf8Values[constantPoolEntryIndex];
- if (value != null) {
- return value;
+ /**
+ * Returns a conservative estimate of the maximum length of the strings contained in the class's
+ * constant pool table.
+ *
+ * @return a conservative estimate of the maximum length of the strings contained in the class's
+ * constant pool table.
+ */
+ public int getMaxStringLength() {
+ return maxStringLength;
}
- int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
- return constantUtf8Values[constantPoolEntryIndex] =
- readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
- }
- /**
- * Reads an UTF8 string in {@link #classFileBuffer}.
- *
- * @param utfOffset the start offset of the UTF8 string to be read.
- * @param utfLength the length of the UTF8 string to be read.
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified UTF8 string.
- */
- private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) {
- int currentOffset = utfOffset;
- int endOffset = currentOffset + utfLength;
- int strLength = 0;
- byte[] classBuffer = classFileBuffer;
- while (currentOffset < endOffset) {
- int currentByte = classBuffer[currentOffset++];
- if ((currentByte & 0x80) == 0) {
- charBuffer[strLength++] = (char) (currentByte & 0x7F);
- } else if ((currentByte & 0xE0) == 0xC0) {
- charBuffer[strLength++] =
- (char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F));
- } else {
- charBuffer[strLength++] =
- (char)
- (((currentByte & 0xF) << 12)
- + ((classBuffer[currentOffset++] & 0x3F) << 6)
- + (classBuffer[currentOffset++] & 0x3F));
- }
+ /**
+ * Reads a byte value in this {@link ClassReader}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @return the read value.
+ */
+ public int readByte(final int offset) {
+ return classFileBuffer[offset] & 0xFF;
}
- return new String(charBuffer, 0, strLength);
- }
- /**
- * Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
- * CONSTANT_Package constant pool entry in {@link #classFileBuffer}. This method is intended
- * for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @param offset the start offset of an unsigned short value in {@link #classFileBuffer}, whose
- * value is the index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
- * CONSTANT_Module or CONSTANT_Package entry in class's constant pool table.
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified constant pool entry.
- */
- private String readStringish(final int offset, final char[] charBuffer) {
- // Get the start offset of the cp_info structure (plus one), and read the CONSTANT_Utf8 entry
- // designated by the first two bytes of this cp_info.
- return readUTF8(cpInfoOffsets[readUnsignedShort(offset)], charBuffer);
- }
-
- /**
- * Reads a CONSTANT_Class constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
- * value is the index of a CONSTANT_Class entry in class's constant pool table.
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified CONSTANT_Class entry.
- */
- public String readClass(final int offset, final char[] charBuffer) {
- return readStringish(offset, charBuffer);
- }
-
- /**
- * Reads a CONSTANT_Module constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
- * value is the index of a CONSTANT_Module entry in class's constant pool table.
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified CONSTANT_Module entry.
- */
- public String readModule(final int offset, final char[] charBuffer) {
- return readStringish(offset, charBuffer);
- }
-
- /**
- * Reads a CONSTANT_Package constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
- * value is the index of a CONSTANT_Package entry in class's constant pool table.
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the String corresponding to the specified CONSTANT_Package entry.
- */
- public String readPackage(final int offset, final char[] charBuffer) {
- return readStringish(offset, charBuffer);
- }
-
- /**
- * Reads a CONSTANT_Dynamic constant pool entry in {@link #classFileBuffer}.
- *
- * @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
- * pool table.
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the ConstantDynamic corresponding to the specified CONSTANT_Dynamic entry.
- */
- private ConstantDynamic readConstantDynamic(
- final int constantPoolEntryIndex, final char[] charBuffer) {
- ConstantDynamic constantDynamic = constantDynamicValues[constantPoolEntryIndex];
- if (constantDynamic != null) {
- return constantDynamic;
+ /**
+ * Reads an unsigned short value in this {@link ClassReader}. This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param offset the start index of the value to be read in this {@link ClassReader}.
+ * @return the read value.
+ */
+ public int readUnsignedShort(final int offset) {
+ byte[] classBuffer = classFileBuffer;
+ return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);
}
- int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
- int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
- Handle handle = (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
- Object[] bootstrapMethodArguments = new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
- bootstrapMethodOffset += 4;
- for (int i = 0; i < bootstrapMethodArguments.length; i++) {
- bootstrapMethodArguments[i] = readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
- bootstrapMethodOffset += 2;
- }
- return constantDynamicValues[constantPoolEntryIndex] =
- new ConstantDynamic(name, descriptor, handle, bootstrapMethodArguments);
- }
- /**
- * Reads a numeric or string constant pool entry in this {@link ClassReader}. This method is
- * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
- * adapters.
- *
- * @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
- * CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
- * CONSTANT_MethodHandle or CONSTANT_Dynamic entry in the class's constant pool.
- * @param charBuffer the buffer to be used to read strings. This buffer must be sufficiently
- * large. It is not automatically resized.
- * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String},
- * {@link Type}, {@link Handle} or {@link ConstantDynamic} corresponding to the specified
- * constant pool entry.
- */
- public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
- int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
- switch (classFileBuffer[cpInfoOffset - 1]) {
- case Symbol.CONSTANT_INTEGER_TAG:
- return readInt(cpInfoOffset);
- case Symbol.CONSTANT_FLOAT_TAG:
- return Float.intBitsToFloat(readInt(cpInfoOffset));
- case Symbol.CONSTANT_LONG_TAG:
- return readLong(cpInfoOffset);
- case Symbol.CONSTANT_DOUBLE_TAG:
- return Double.longBitsToDouble(readLong(cpInfoOffset));
- case Symbol.CONSTANT_CLASS_TAG:
- return Type.getObjectType(readUTF8(cpInfoOffset, charBuffer));
- case Symbol.CONSTANT_STRING_TAG:
- return readUTF8(cpInfoOffset, charBuffer);
- case Symbol.CONSTANT_METHOD_TYPE_TAG:
- return Type.getMethodType(readUTF8(cpInfoOffset, charBuffer));
- case Symbol.CONSTANT_METHOD_HANDLE_TAG:
- int referenceKind = readByte(cpInfoOffset);
- int referenceCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 1)];
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(referenceCpInfoOffset + 2)];
- String owner = readClass(referenceCpInfoOffset, charBuffer);
+ /**
+ * Reads a signed short value in this {@link ClassReader}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @return the read value.
+ */
+ public short readShort(final int offset) {
+ byte[] classBuffer = classFileBuffer;
+ return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));
+ }
+
+ /**
+ * Reads a signed int value in this {@link ClassReader}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @return the read value.
+ */
+ public int readInt(final int offset) {
+ byte[] classBuffer = classFileBuffer;
+ return ((classBuffer[offset] & 0xFF) << 24)
+ | ((classBuffer[offset + 1] & 0xFF) << 16)
+ | ((classBuffer[offset + 2] & 0xFF) << 8)
+ | (classBuffer[offset + 3] & 0xFF);
+ }
+
+ /**
+ * Reads a signed long value in this {@link ClassReader}. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param offset the start offset of the value to be read in this {@link ClassReader}.
+ * @return the read value.
+ */
+ public long readLong(final int offset) {
+ long l1 = readInt(offset);
+ long l0 = readInt(offset + 4) & 0xFFFFFFFFL;
+ return (l1 << 32) | l0;
+ }
+
+ /**
+ * Reads a CONSTANT_Utf8 constant pool entry in this {@link ClassReader}. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
+ * adapters.
+ *
+ * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+ * value is the index of a CONSTANT_Utf8 entry in the class's constant pool table.
+ * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified CONSTANT_Utf8 entry.
+ */
+ // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
+ public String readUTF8(final int offset, final char[] charBuffer) {
+ int constantPoolEntryIndex = readUnsignedShort(offset);
+ if (offset == 0 || constantPoolEntryIndex == 0) {
+ return null;
+ }
+ return readUtf(constantPoolEntryIndex, charBuffer);
+ }
+
+ /**
+ * Reads a CONSTANT_Utf8 constant pool entry in {@link #classFileBuffer}.
+ *
+ * @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool
+ * table.
+ * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified CONSTANT_Utf8 entry.
+ */
+ final String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) {
+ String value = constantUtf8Values[constantPoolEntryIndex];
+ if (value != null) {
+ return value;
+ }
+ int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
+ return constantUtf8Values[constantPoolEntryIndex] =
+ readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
+ }
+
+ /**
+ * Reads an UTF8 string in {@link #classFileBuffer}.
+ *
+ * @param utfOffset the start offset of the UTF8 string to be read.
+ * @param utfLength the length of the UTF8 string to be read.
+ * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified UTF8 string.
+ */
+ private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) {
+ int currentOffset = utfOffset;
+ int endOffset = currentOffset + utfLength;
+ int strLength = 0;
+ byte[] classBuffer = classFileBuffer;
+ while (currentOffset < endOffset) {
+ int currentByte = classBuffer[currentOffset++];
+ if ((currentByte & 0x80) == 0) {
+ charBuffer[strLength++] = (char) (currentByte & 0x7F);
+ } else if ((currentByte & 0xE0) == 0xC0) {
+ charBuffer[strLength++] =
+ (char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F));
+ } else {
+ charBuffer[strLength++] =
+ (char)
+ (((currentByte & 0xF) << 12)
+ + ((classBuffer[currentOffset++] & 0x3F) << 6)
+ + (classBuffer[currentOffset++] & 0x3F));
+ }
+ }
+ return new String(charBuffer, 0, strLength);
+ }
+
+ /**
+ * Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
+ * CONSTANT_Package constant pool entry in {@link #classFileBuffer}. This method is intended
+ * for {@link Attribute} sub classes, and is normally not needed by class generators or
+ * adapters.
+ *
+ * @param offset the start offset of an unsigned short value in {@link #classFileBuffer}, whose
+ * value is the index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
+ * CONSTANT_Module or CONSTANT_Package entry in class's constant pool table.
+ * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified constant pool entry.
+ */
+ private String readStringish(final int offset, final char[] charBuffer) {
+ // Get the start offset of the cp_info structure (plus one), and read the CONSTANT_Utf8 entry
+ // designated by the first two bytes of this cp_info.
+ return readUTF8(cpInfoOffsets[readUnsignedShort(offset)], charBuffer);
+ }
+
+ /**
+ * Reads a CONSTANT_Class constant pool entry in this {@link ClassReader}. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
+ * adapters.
+ *
+ * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+ * value is the index of a CONSTANT_Class entry in class's constant pool table.
+ * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified CONSTANT_Class entry.
+ */
+ public String readClass(final int offset, final char[] charBuffer) {
+ return readStringish(offset, charBuffer);
+ }
+
+ /**
+ * Reads a CONSTANT_Module constant pool entry in this {@link ClassReader}. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
+ * adapters.
+ *
+ * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+ * value is the index of a CONSTANT_Module entry in class's constant pool table.
+ * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified CONSTANT_Module entry.
+ */
+ public String readModule(final int offset, final char[] charBuffer) {
+ return readStringish(offset, charBuffer);
+ }
+
+ /**
+ * Reads a CONSTANT_Package constant pool entry in this {@link ClassReader}. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
+ * adapters.
+ *
+ * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose
+ * value is the index of a CONSTANT_Package entry in class's constant pool table.
+ * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the String corresponding to the specified CONSTANT_Package entry.
+ */
+ public String readPackage(final int offset, final char[] charBuffer) {
+ return readStringish(offset, charBuffer);
+ }
+
+ /**
+ * Reads a CONSTANT_Dynamic constant pool entry in {@link #classFileBuffer}.
+ *
+ * @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
+ * pool table.
+ * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the ConstantDynamic corresponding to the specified CONSTANT_Dynamic entry.
+ */
+ private ConstantDynamic readConstantDynamic(
+ final int constantPoolEntryIndex, final char[] charBuffer) {
+ ConstantDynamic constantDynamic = constantDynamicValues[constantPoolEntryIndex];
+ if (constantDynamic != null) {
+ return constantDynamic;
+ }
+ int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
+ int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
- boolean isInterface =
- classFileBuffer[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
- return new Handle(referenceKind, owner, name, descriptor, isInterface);
- case Symbol.CONSTANT_DYNAMIC_TAG:
- return readConstantDynamic(constantPoolEntryIndex, charBuffer);
- default:
- throw new IllegalArgumentException();
+ int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
+ Handle handle = (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
+ Object[] bootstrapMethodArguments = new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
+ bootstrapMethodOffset += 4;
+ for (int i = 0; i < bootstrapMethodArguments.length; i++) {
+ bootstrapMethodArguments[i] = readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
+ bootstrapMethodOffset += 2;
+ }
+ return constantDynamicValues[constantPoolEntryIndex] =
+ new ConstantDynamic(name, descriptor, handle, bootstrapMethodArguments);
+ }
+
+ /**
+ * Reads a numeric or string constant pool entry in this {@link ClassReader}. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by class generators or
+ * adapters.
+ *
+ * @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
+ * CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
+ * CONSTANT_MethodHandle or CONSTANT_Dynamic entry in the class's constant pool.
+ * @param charBuffer the buffer to be used to read strings. This buffer must be sufficiently
+ * large. It is not automatically resized.
+ * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String},
+ * {@link Type}, {@link Handle} or {@link ConstantDynamic} corresponding to the specified
+ * constant pool entry.
+ */
+ public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
+ int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
+ switch (classFileBuffer[cpInfoOffset - 1]) {
+ case Symbol.CONSTANT_INTEGER_TAG:
+ return readInt(cpInfoOffset);
+ case Symbol.CONSTANT_FLOAT_TAG:
+ return Float.intBitsToFloat(readInt(cpInfoOffset));
+ case Symbol.CONSTANT_LONG_TAG:
+ return readLong(cpInfoOffset);
+ case Symbol.CONSTANT_DOUBLE_TAG:
+ return Double.longBitsToDouble(readLong(cpInfoOffset));
+ case Symbol.CONSTANT_CLASS_TAG:
+ return Type.getObjectType(readUTF8(cpInfoOffset, charBuffer));
+ case Symbol.CONSTANT_STRING_TAG:
+ return readUTF8(cpInfoOffset, charBuffer);
+ case Symbol.CONSTANT_METHOD_TYPE_TAG:
+ return Type.getMethodType(readUTF8(cpInfoOffset, charBuffer));
+ case Symbol.CONSTANT_METHOD_HANDLE_TAG:
+ int referenceKind = readByte(cpInfoOffset);
+ int referenceCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 1)];
+ int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(referenceCpInfoOffset + 2)];
+ String owner = readClass(referenceCpInfoOffset, charBuffer);
+ String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
+ String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
+ boolean isInterface =
+ classFileBuffer[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+ return new Handle(referenceKind, owner, name, descriptor, isInterface);
+ case Symbol.CONSTANT_DYNAMIC_TAG:
+ return readConstantDynamic(constantPoolEntryIndex, charBuffer);
+ default:
+ throw new IllegalArgumentException();
+ }
}
- }
}
diff --git a/src/main/java/org/objectweb/asm/ClassWriter.java b/src/main/java/org/objectweb/asm/ClassWriter.java
index 7bd6355c..e119babd 100644
--- a/src/main/java/org/objectweb/asm/ClassWriter.java
+++ b/src/main/java/org/objectweb/asm/ClassWriter.java
@@ -33,940 +33,970 @@ package org.objectweb.asm;
* scratch", or with one or more {@link ClassReader} and adapter {@link ClassVisitor} to generate a
* modified class from one or more existing Java classes.
*
- * @see JVMS 4
* @author Eric Bruneton
+ * @see JVMS 4
*/
public class ClassWriter extends ClassVisitor {
- /**
- * A flag to automatically compute the maximum stack size and the maximum number of local
- * variables of methods. If this flag is set, then the arguments of the {@link
- * MethodVisitor#visitMaxs} method of the {@link MethodVisitor} returned by the {@link
- * #visitMethod} method will be ignored, and computed automatically from the signature and the
- * bytecode of each method.
- *
- *
Note: for classes whose version is {@link Opcodes#V1_7} of more, this option requires
- * valid stack map frames. The maximum stack size is then computed from these frames, and from the
- * bytecode instructions in between. If stack map frames are not present or must be recomputed,
- * used {@link #COMPUTE_FRAMES} instead.
- *
- * @see #ClassWriter(int)
- */
- public static final int COMPUTE_MAXS = 1;
-
- /**
- * A flag to automatically compute the stack map frames of methods from scratch. If this flag is
- * set, then the calls to the {@link MethodVisitor#visitFrame} method are ignored, and the stack
- * map frames are recomputed from the methods bytecode. The arguments of the {@link
- * MethodVisitor#visitMaxs} method are also ignored and recomputed from the bytecode. In other
- * words, {@link #COMPUTE_FRAMES} implies {@link #COMPUTE_MAXS}.
- *
- * @see #ClassWriter(int)
- */
- public static final int COMPUTE_FRAMES = 2;
-
- // Note: fields are ordered as in the ClassFile structure, and those related to attributes are
- // ordered as in Section 4.7 of the JVMS.
-
- /**
- * The minor_version and major_version fields of the JVMS ClassFile structure. minor_version is
- * stored in the 16 most significant bits, and major_version in the 16 least significant bits.
- */
- private int version;
-
- /** The symbol table for this class (contains the constant_pool and the BootstrapMethods). */
- private final SymbolTable symbolTable;
-
- /**
- * The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
- * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
- * ClassFile structure.
- */
- private int accessFlags;
-
- /** The this_class field of the JVMS ClassFile structure. */
- private int thisClass;
-
- /** The super_class field of the JVMS ClassFile structure. */
- private int superClass;
-
- /** The interface_count field of the JVMS ClassFile structure. */
- private int interfaceCount;
-
- /** The 'interfaces' array of the JVMS ClassFile structure. */
- private int[] interfaces;
-
- /**
- * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their
- * {@link FieldWriter#fv} field. This field stores the first element of this list.
- */
- private FieldWriter firstField;
-
- /**
- * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their
- * {@link FieldWriter#fv} field. This field stores the last element of this list.
- */
- private FieldWriter lastField;
-
- /**
- * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their
- * {@link MethodWriter#mv} field. This field stores the first element of this list.
- */
- private MethodWriter firstMethod;
-
- /**
- * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their
- * {@link MethodWriter#mv} field. This field stores the last element of this list.
- */
- private MethodWriter lastMethod;
-
- /** The number_of_classes field of the InnerClasses attribute, or 0. */
- private int numberOfInnerClasses;
-
- /** The 'classes' array of the InnerClasses attribute, or {@literal null}. */
- private ByteVector innerClasses;
-
- /** The class_index field of the EnclosingMethod attribute, or 0. */
- private int enclosingClassIndex;
-
- /** The method_index field of the EnclosingMethod attribute. */
- private int enclosingMethodIndex;
-
- /** The signature_index field of the Signature attribute, or 0. */
- private int signatureIndex;
-
- /** The source_file_index field of the SourceFile attribute, or 0. */
- private int sourceFileIndex;
-
- /** The debug_extension field of the SourceDebugExtension attribute, or {@literal null}. */
- private ByteVector debugExtension;
-
- /**
- * The last runtime visible annotation of this class. 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 class. 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 class. 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 class. The previous ones can be accessed
- * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
- */
- private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
-
- /** The Module attribute of this class, or {@literal null}. */
- private ModuleWriter moduleWriter;
-
- /** The host_class_index field of the NestHost attribute, or 0. */
- private int nestHostClassIndex;
-
- /** The number_of_classes field of the NestMembers attribute, or 0. */
- private int numberOfNestMemberClasses;
-
- /** The 'classes' array of the NestMembers attribute, or {@literal null}. */
- private ByteVector nestMemberClasses;
-
- /**
- * The first non standard attribute of this class. The next ones can be accessed with the {@link
- * Attribute#nextAttribute} field. May be {@literal null}.
- *
- *
WARNING: this list stores the attributes in the reverse order of their visit.
- * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link
- * #toByteArray} method writes the attributes in the order defined by this list, i.e. in the
- * reverse order specified by the user.
- */
- private Attribute firstAttribute;
-
- /**
- * Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link
- * MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link
- * MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}.
- */
- private int compute;
-
- // -----------------------------------------------------------------------------------------------
- // Constructor
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Constructs a new {@link ClassWriter} object.
- *
- * @param flags option flags that can be used to modify the default behavior of this class. Must
- * be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}.
- */
- public ClassWriter(final int flags) {
- this(null, flags);
- }
-
- /**
- * Constructs a new {@link ClassWriter} object and enables optimizations for "mostly add" bytecode
- * transformations. These optimizations are the following:
- *
- *
- *
The constant pool and bootstrap methods from the original class are copied as is in the
- * new class, which saves time. New constant pool entries and new bootstrap methods will be
- * added at the end if necessary, but unused constant pool entries or bootstrap methods
- * won't be removed.
- *
Methods that are not transformed are copied as is in the new class, directly from the
- * original class bytecode (i.e. without emitting visit events for all the method
- * instructions), which saves a lot of time. Untransformed methods are detected by
- * the fact that the {@link ClassReader} receives {@link MethodVisitor} objects that come
- * from a {@link ClassWriter} (and not from any other {@link ClassVisitor} instance).
- *
- *
- * @param classReader the {@link ClassReader} used to read the original class. It will be used to
- * copy the entire constant pool and bootstrap methods from the original class and also to
- * copy other fragments of original bytecode where applicable.
- * @param flags option flags that can be used to modify the default behavior of this class.Must be
- * zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. These option flags do
- * not affect methods that are copied as is in the new class. This means that neither the
- * maximum stack size nor the stack frames will be computed for these methods.
- */
- public ClassWriter(final ClassReader classReader, final int flags) {
- super(Opcodes.ASM7);
- symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
- if ((flags & COMPUTE_FRAMES) != 0) {
- this.compute = MethodWriter.COMPUTE_ALL_FRAMES;
- } else if ((flags & COMPUTE_MAXS) != 0) {
- this.compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
- } else {
- this.compute = MethodWriter.COMPUTE_NOTHING;
- }
- }
-
- // -----------------------------------------------------------------------------------------------
- // Implementation of the ClassVisitor abstract class
- // -----------------------------------------------------------------------------------------------
-
- @Override
- public final void visit(
- final int version,
- final int access,
- final String name,
- final String signature,
- final String superName,
- final String[] interfaces)
- {
- this.version = version;
-
- //replace all pre jre minor versions to one that's outside of the old format
- if((version & 0xFFFF) == 45 && (version >>> 16) <= 2)
- {
- this.version = (3 << 16) | 45;
- }
- this.accessFlags = access;
- this.thisClass = symbolTable.setMajorVersionAndClassName(this.version & 0xFFFF, name);
- if (signature != null) {
- this.signatureIndex = symbolTable.addConstantUtf8(signature);
- }
- this.superClass = superName == null ? 0 : symbolTable.addConstantClass(superName).index;
- if (interfaces != null && interfaces.length > 0) {
- interfaceCount = interfaces.length;
- this.interfaces = new int[interfaceCount];
- for (int i = 0; i < interfaceCount; ++i) {
- this.interfaces[i] = symbolTable.addConstantClass(interfaces[i]).index;
- }
- }
- if (compute == MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL && (this.version & 0xFFFF) >= Opcodes.V1_7) {
- compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES;
- }
- }
-
- @Override
- public final void visitSource(final String file, final String debug) {
- if (file != null) {
- sourceFileIndex = symbolTable.addConstantUtf8(file);
- }
- if (debug != null) {
- debugExtension = new ByteVector().encodeUtf8(debug, 0, Integer.MAX_VALUE);
- }
- }
-
- @Override
- public final ModuleVisitor visitModule(
- final String name, final int access, final String version) {
- return moduleWriter =
- new ModuleWriter(
- symbolTable,
- symbolTable.addConstantModule(name).index,
- access,
- version == null ? 0 : symbolTable.addConstantUtf8(version));
- }
-
- @Override
- public void visitNestHost(final String nestHost) {
- nestHostClassIndex = symbolTable.addConstantClass(nestHost).index;
- }
-
- @Override
- public final void visitOuterClass(
- final String owner, final String name, final String descriptor) {
- enclosingClassIndex = symbolTable.addConstantClass(owner).index;
- if (name != null && descriptor != null) {
- enclosingMethodIndex = symbolTable.addConstantNameAndType(name, descriptor);
- }
- }
-
- @Override
- public final 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 final 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 final 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 visitNestMember(final String nestMember) {
- if (nestMemberClasses == null) {
- nestMemberClasses = new ByteVector();
- }
- ++numberOfNestMemberClasses;
- nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index);
- }
-
- @Override
- public final void visitInnerClass(
- final String name, final String outerName, final String innerName, final int access) {
- if (innerClasses == null) {
- innerClasses = new ByteVector();
- }
- // Section 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the constant_pool table
- // which represents a class or interface C that is not a package member must have exactly one
- // corresponding entry in the classes array". To avoid duplicates we keep track in the info
- // field of the Symbol of each CONSTANT_Class_info entry C whether an inner class entry has
- // already been added for C. If so, we store the index of this inner class entry (plus one) in
- // the info field. This trick allows duplicate detection in O(1) time.
- Symbol nameSymbol = symbolTable.addConstantClass(name);
- if (nameSymbol.info == 0) {
- ++numberOfInnerClasses;
- innerClasses.putShort(nameSymbol.index);
- innerClasses.putShort(outerName == null ? 0 : symbolTable.addConstantClass(outerName).index);
- innerClasses.putShort(innerName == null ? 0 : symbolTable.addConstantUtf8(innerName));
- innerClasses.putShort(access);
- nameSymbol.info = numberOfInnerClasses;
- }
- // Else, compare the inner classes entry nameSymbol.info - 1 with the arguments of this method
- // and throw an exception if there is a difference?
- }
-
- @Override
- public final FieldVisitor visitField(
- final int access,
- final String name,
- final String descriptor,
- final String signature,
- final Object value) {
- FieldWriter fieldWriter =
- new FieldWriter(symbolTable, access, name, descriptor, signature, value);
- if (firstField == null) {
- firstField = fieldWriter;
- } else {
- lastField.fv = fieldWriter;
- }
- return lastField = fieldWriter;
- }
-
- @Override
- public final MethodVisitor visitMethod(
- final int access,
- final String name,
- final String descriptor,
- final String signature,
- final String[] exceptions) {
- MethodWriter methodWriter =
- new MethodWriter(symbolTable, access, name, descriptor, signature, exceptions, compute);
- if (firstMethod == null) {
- firstMethod = methodWriter;
- } else {
- lastMethod.mv = methodWriter;
- }
- return lastMethod = methodWriter;
- }
-
- @Override
- public final void visitEnd() {
- // Nothing to do.
- }
-
- // -----------------------------------------------------------------------------------------------
- // Other public methods
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Returns the content of the class file that was built by this ClassWriter.
- *
- * @return the binary content of the JVMS ClassFile structure that was built by this ClassWriter.
- * @throws ClassTooLargeException if the constant pool of the class is too large.
- * @throws MethodTooLargeException if the Code attribute of a method is too large.
- */
- public byte[] toByteArray() {
- // First step: compute the size in bytes of the ClassFile structure.
- // The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version,
- // constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count,
- // methods_count and attributes_count) use 2 bytes each, and each interface uses 2 bytes too.
- int size = 24 + 2 * interfaceCount;
- int fieldsCount = 0;
- FieldWriter fieldWriter = firstField;
- while (fieldWriter != null) {
- ++fieldsCount;
- size += fieldWriter.computeFieldInfoSize();
- fieldWriter = (FieldWriter) fieldWriter.fv;
- }
- int methodsCount = 0;
- MethodWriter methodWriter = firstMethod;
- while (methodWriter != null) {
- ++methodsCount;
- size += methodWriter.computeMethodInfoSize();
- methodWriter = (MethodWriter) methodWriter.mv;
- }
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
- int attributesCount = 0;
- if (innerClasses != null) {
- ++attributesCount;
- size += 8 + innerClasses.length;
- symbolTable.addConstantUtf8(Constants.INNER_CLASSES);
- }
- if (enclosingClassIndex != 0) {
- ++attributesCount;
- size += 10;
- symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD);
- }
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) {
- ++attributesCount;
- size += 6;
- symbolTable.addConstantUtf8(Constants.SYNTHETIC);
- }
- if (signatureIndex != 0) {
- ++attributesCount;
- size += 8;
- symbolTable.addConstantUtf8(Constants.SIGNATURE);
- }
- if (sourceFileIndex != 0) {
- ++attributesCount;
- size += 8;
- symbolTable.addConstantUtf8(Constants.SOURCE_FILE);
- }
- if (debugExtension != null) {
- ++attributesCount;
- size += 6 + debugExtension.length;
- symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION);
- }
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
- ++attributesCount;
- size += 6;
- symbolTable.addConstantUtf8(Constants.DEPRECATED);
- }
- if (lastRuntimeVisibleAnnotation != null) {
- ++attributesCount;
- size +=
- lastRuntimeVisibleAnnotation.computeAnnotationsSize(
- Constants.RUNTIME_VISIBLE_ANNOTATIONS);
- }
- if (lastRuntimeInvisibleAnnotation != null) {
- ++attributesCount;
- size +=
- lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
- Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
- }
- if (lastRuntimeVisibleTypeAnnotation != null) {
- ++attributesCount;
- size +=
- lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
- Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
- }
- if (lastRuntimeInvisibleTypeAnnotation != null) {
- ++attributesCount;
- size +=
- lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
- Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
- }
- if (symbolTable.computeBootstrapMethodsSize() > 0) {
- ++attributesCount;
- size += symbolTable.computeBootstrapMethodsSize();
- }
- if (moduleWriter != null) {
- attributesCount += moduleWriter.getAttributeCount();
- size += moduleWriter.computeAttributesSize();
- }
- if (nestHostClassIndex != 0) {
- ++attributesCount;
- size += 8;
- symbolTable.addConstantUtf8(Constants.NEST_HOST);
- }
- if (nestMemberClasses != null) {
- ++attributesCount;
- size += 8 + nestMemberClasses.length;
- symbolTable.addConstantUtf8(Constants.NEST_MEMBERS);
- }
- if (firstAttribute != null) {
- attributesCount += firstAttribute.getAttributeCount();
- size += firstAttribute.computeAttributesSize(symbolTable);
- }
- // IMPORTANT: this must be the last part of the ClassFile size computation, because the previous
- // statements can add attribute names to the constant pool, thereby changing its size!
- size += symbolTable.getConstantPoolLength();
- int constantPoolCount = symbolTable.getConstantPoolCount();
- if (constantPoolCount > 0xFFFF) {
- throw new ClassTooLargeException(symbolTable.getClassName(), constantPoolCount);
- }
-
- // Second step: allocate a ByteVector of the correct size (in order to avoid any array copy in
- // dynamic resizes) and fill it with the ClassFile content.
- ByteVector result = new ByteVector(size);
- result.putInt(0xCAFEBABE).putInt(version);
- symbolTable.putConstantPool(result);
- int mask = (version & 0xFFFF) < Opcodes.V1_5 ? Opcodes.ACC_SYNTHETIC : 0;
- result.putShort(accessFlags & ~mask).putShort(thisClass).putShort(superClass);
- result.putShort(interfaceCount);
- for (int i = 0; i < interfaceCount; ++i) {
- result.putShort(interfaces[i]);
- }
- result.putShort(fieldsCount);
- fieldWriter = firstField;
- while (fieldWriter != null) {
- fieldWriter.putFieldInfo(result);
- fieldWriter = (FieldWriter) fieldWriter.fv;
- }
- result.putShort(methodsCount);
- boolean hasFrames = false;
- boolean hasAsmInstructions = false;
- methodWriter = firstMethod;
- while (methodWriter != null) {
- hasFrames |= methodWriter.hasFrames();
- hasAsmInstructions |= methodWriter.hasAsmInstructions();
- methodWriter.putMethodInfo(result);
- methodWriter = (MethodWriter) methodWriter.mv;
- }
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
- result.putShort(attributesCount);
- if (innerClasses != null) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.INNER_CLASSES))
- .putInt(innerClasses.length + 2)
- .putShort(numberOfInnerClasses)
- .putByteArray(innerClasses.data, 0, innerClasses.length);
- }
- if (enclosingClassIndex != 0) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD))
- .putInt(4)
- .putShort(enclosingClassIndex)
- .putShort(enclosingMethodIndex);
- }
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) {
- result.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
- }
- if (signatureIndex != 0) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
- .putInt(2)
- .putShort(signatureIndex);
- }
- if (sourceFileIndex != 0) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_FILE))
- .putInt(2)
- .putShort(sourceFileIndex);
- }
- if (debugExtension != null) {
- int length = debugExtension.length;
- result
- .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION))
- .putInt(length)
- .putByteArray(debugExtension.data, 0, length);
- }
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
- result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
- }
- AnnotationWriter.putAnnotations(
- symbolTable,
- lastRuntimeVisibleAnnotation,
- lastRuntimeInvisibleAnnotation,
- lastRuntimeVisibleTypeAnnotation,
- lastRuntimeInvisibleTypeAnnotation,
- result);
- symbolTable.putBootstrapMethods(result);
- if (moduleWriter != null) {
- moduleWriter.putAttributes(result);
- }
- if (nestHostClassIndex != 0) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.NEST_HOST))
- .putInt(2)
- .putShort(nestHostClassIndex);
- }
- if (nestMemberClasses != null) {
- result
- .putShort(symbolTable.addConstantUtf8(Constants.NEST_MEMBERS))
- .putInt(nestMemberClasses.length + 2)
- .putShort(numberOfNestMemberClasses)
- .putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length);
- }
- if (firstAttribute != null) {
- firstAttribute.putAttributes(symbolTable, result);
- }
-
- // Third step: replace the ASM specific instructions, if any.
- if (hasAsmInstructions) {
- return replaceAsmInstructions(result.data, hasFrames);
- } else {
- return result.data;
- }
- }
-
- /**
- * Returns the equivalent of the given class file, with the ASM specific instructions replaced
- * with standard ones. This is done with a ClassReader -> ClassWriter round trip.
- *
- * @param classFile a class file containing ASM specific instructions, generated by this
- * ClassWriter.
- * @param hasFrames whether there is at least one stack map frames in 'classFile'.
- * @return an equivalent of 'classFile', with the ASM specific instructions replaced with standard
- * ones.
- */
- private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) {
- final Attribute[] attributes = getAttributePrototypes();
- firstField = null;
- lastField = null;
- firstMethod = null;
- lastMethod = null;
- lastRuntimeVisibleAnnotation = null;
- lastRuntimeInvisibleAnnotation = null;
- lastRuntimeVisibleTypeAnnotation = null;
- lastRuntimeInvisibleTypeAnnotation = null;
- moduleWriter = null;
- nestHostClassIndex = 0;
- numberOfNestMemberClasses = 0;
- nestMemberClasses = null;
- firstAttribute = null;
- compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
- new ClassReader(classFile, 0, /* checkClassVersion = */ false)
- .accept(
- this,
- attributes,
- (hasFrames ? ClassReader.EXPAND_FRAMES : 0) | ClassReader.EXPAND_ASM_INSNS);
- return toByteArray();
- }
-
- /**
- * Returns the prototypes of the attributes used by this class, its fields and its methods.
- *
- * @return the prototypes of the attributes used by this class, its fields and its methods.
- */
- private Attribute[] getAttributePrototypes() {
- Attribute.Set attributePrototypes = new Attribute.Set();
- attributePrototypes.addAttributes(firstAttribute);
- FieldWriter fieldWriter = firstField;
- while (fieldWriter != null) {
- fieldWriter.collectAttributePrototypes(attributePrototypes);
- fieldWriter = (FieldWriter) fieldWriter.fv;
- }
- MethodWriter methodWriter = firstMethod;
- while (methodWriter != null) {
- methodWriter.collectAttributePrototypes(attributePrototypes);
- methodWriter = (MethodWriter) methodWriter.mv;
- }
- return attributePrototypes.toArray();
- }
-
- // -----------------------------------------------------------------------------------------------
- // Utility methods: constant pool management for Attribute sub classes
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Adds a number or string constant to the constant pool of the class being build. Does nothing if
- * the constant pool already contains a similar item. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param value the value of the constant to be added to the constant pool. This parameter must be
- * an {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double} or a {@link String}.
- * @return the index of a new or already existing constant item with the given value.
- */
- public int newConst(final Object value) {
- return symbolTable.addConstant(value).index;
- }
-
- /**
- * Adds an UTF8 string to the constant pool of the class being build. Does nothing if the constant
- * pool already contains a similar item. This method is intended for {@link Attribute} sub
- * classes, and is normally not needed by class generators or adapters.
- *
- * @param value the String value.
- * @return the index of a new or already existing UTF8 item.
- */
- // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
- public int newUTF8(final String value) {
- return symbolTable.addConstantUtf8(value);
- }
-
- /**
- * Adds a class reference to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param value the internal name of the class.
- * @return the index of a new or already existing class reference item.
- */
- public int newClass(final String value) {
- return symbolTable.addConstantClass(value).index;
- }
-
- /**
- * Adds a method type reference to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param methodDescriptor method descriptor of the method type.
- * @return the index of a new or already existing method type reference item.
- */
- public int newMethodType(final String methodDescriptor) {
- return symbolTable.addConstantMethodType(methodDescriptor).index;
- }
-
- /**
- * Adds a module reference to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param moduleName name of the module.
- * @return the index of a new or already existing module reference item.
- */
- public int newModule(final String moduleName) {
- return symbolTable.addConstantModule(moduleName).index;
- }
-
- /**
- * Adds a package reference to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param packageName name of the package in its internal form.
- * @return the index of a new or already existing module reference item.
- */
- public int newPackage(final String packageName) {
- return symbolTable.addConstantPackage(packageName).index;
- }
-
- /**
- * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool
- * already contains a similar item. This method is intended for {@link Attribute} sub classes,
- * and is normally not needed by class generators or adapters.
- *
- * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link
- * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
- * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
- * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
- * @param owner the internal name of the field or method owner class.
- * @param name the name of the field or method.
- * @param descriptor the descriptor of the field or method.
- * @return the index of a new or already existing method type reference item.
- * @deprecated this method is superseded by {@link #newHandle(int, String, String, String,
- * boolean)}.
- */
- @Deprecated
- public int newHandle(
- final int tag, final String owner, final String name, final String descriptor) {
- return newHandle(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE);
- }
-
- /**
- * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool
- * already contains a similar item. This method is intended for {@link Attribute} sub classes,
- * and is normally not needed by class generators or adapters.
- *
- * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link
- * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
- * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
- * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
- * @param owner the internal name of the field or method owner class.
- * @param name the name of the field or method.
- * @param descriptor the descriptor of the field or method.
- * @param isInterface true if the owner is an interface.
- * @return the index of a new or already existing method type reference item.
- */
- public int newHandle(
- final int tag,
- final String owner,
- final String name,
- final String descriptor,
- final boolean isInterface) {
- return symbolTable.addConstantMethodHandle(tag, owner, name, descriptor, isInterface).index;
- }
-
- /**
- * Adds a dynamic constant reference to the constant pool of the class being build. Does nothing
- * if the constant pool already contains a similar item. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param name name of the invoked method.
- * @param descriptor field descriptor of the constant type.
- * @param bootstrapMethodHandle the bootstrap method.
- * @param bootstrapMethodArguments the bootstrap method constant arguments.
- * @return the index of a new or already existing dynamic constant reference item.
- */
- public int newConstantDynamic(
- final String name,
- final String descriptor,
- final Handle bootstrapMethodHandle,
- final Object... bootstrapMethodArguments) {
- return symbolTable.addConstantDynamic(
- name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
- .index;
- }
-
- /**
- * Adds an invokedynamic reference to the constant pool of the class being build. Does nothing if
- * the constant pool already contains a similar item. This method is intended for {@link
- * Attribute} sub classes, and is normally not needed by class generators or adapters.
- *
- * @param name name of the invoked method.
- * @param descriptor descriptor of the invoke method.
- * @param bootstrapMethodHandle the bootstrap method.
- * @param bootstrapMethodArguments the bootstrap method constant arguments.
- * @return the index of a new or already existing invokedynamic reference item.
- */
- public int newInvokeDynamic(
- final String name,
- final String descriptor,
- final Handle bootstrapMethodHandle,
- final Object... bootstrapMethodArguments) {
- return symbolTable.addConstantInvokeDynamic(
- name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
- .index;
- }
-
- /**
- * Adds a field reference to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param owner the internal name of the field's owner class.
- * @param name the field's name.
- * @param descriptor the field's descriptor.
- * @return the index of a new or already existing field reference item.
- */
- public int newField(final String owner, final String name, final String descriptor) {
- return symbolTable.addConstantFieldref(owner, name, descriptor).index;
- }
-
- /**
- * Adds a method reference to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param owner the internal name of the method's owner class.
- * @param name the method's name.
- * @param descriptor the method's descriptor.
- * @param isInterface {@literal true} if {@code owner} is an interface.
- * @return the index of a new or already existing method reference item.
- */
- public int newMethod(
- final String owner, final String name, final String descriptor, final boolean isInterface) {
- return symbolTable.addConstantMethodref(owner, name, descriptor, isInterface).index;
- }
-
- /**
- * Adds a name and type to the constant pool of the class being build. Does nothing if the
- * constant pool already contains a similar item. This method is intended for {@link Attribute}
- * sub classes, and is normally not needed by class generators or adapters.
- *
- * @param name a name.
- * @param descriptor a type descriptor.
- * @return the index of a new or already existing name and type item.
- */
- public int newNameType(final String name, final String descriptor) {
- return symbolTable.addConstantNameAndType(name, descriptor);
- }
-
- // -----------------------------------------------------------------------------------------------
- // Default method to compute common super classes when computing stack map frames
- // -----------------------------------------------------------------------------------------------
-
- /**
- * Returns the common super type of the two given types. The default implementation of this method
- * loads the two given classes and uses the java.lang.Class methods to find the common
- * super class. It can be overridden to compute this common super type in other ways, in
- * particular without actually loading any class, or to take into account the class that is
- * currently being generated by this ClassWriter, which can of course not be loaded since it is
- * under construction.
- *
- * @param type1 the internal name of a class.
- * @param type2 the internal name of another class.
- * @return the internal name of the common super class of the two given classes.
- */
- protected String getCommonSuperClass(final String type1, final String type2) {
- ClassLoader classLoader = getClassLoader();
- Class> class1;
- try {
- class1 = Class.forName(type1.replace('/', '.'), false, classLoader);
- } catch (ClassNotFoundException e) {
- throw new TypeNotPresentException(type1, e);
- }
- Class> class2;
- try {
- class2 = Class.forName(type2.replace('/', '.'), false, classLoader);
- } catch (ClassNotFoundException e) {
- throw new TypeNotPresentException(type2, e);
- }
- if (class1.isAssignableFrom(class2)) {
- return type1;
- }
- if (class2.isAssignableFrom(class1)) {
- return type2;
- }
- if (class1.isInterface() || class2.isInterface()) {
- return "java/lang/Object";
- } else {
- do {
- class1 = class1.getSuperclass();
- } while (!class1.isAssignableFrom(class2));
- return class1.getName().replace('.', '/');
- }
- }
-
- /**
- * Returns the {@link ClassLoader} to be used by the default implementation of {@link
- * #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by
- * default.
- *
- * @return ClassLoader
- */
- protected ClassLoader getClassLoader() {
- return getClass().getClassLoader();
- }
+ /**
+ * A flag to automatically compute the maximum stack size and the maximum number of local
+ * variables of methods. If this flag is set, then the arguments of the {@link
+ * MethodVisitor#visitMaxs} method of the {@link MethodVisitor} returned by the {@link
+ * #visitMethod} method will be ignored, and computed automatically from the signature and the
+ * bytecode of each method.
+ *
+ *
Note: for classes whose version is {@link Opcodes#V1_7} of more, this option requires
+ * valid stack map frames. The maximum stack size is then computed from these frames, and from the
+ * bytecode instructions in between. If stack map frames are not present or must be recomputed,
+ * used {@link #COMPUTE_FRAMES} instead.
+ *
+ * @see #ClassWriter(int)
+ */
+ public static final int COMPUTE_MAXS = 1;
+
+ /**
+ * A flag to automatically compute the stack map frames of methods from scratch. If this flag is
+ * set, then the calls to the {@link MethodVisitor#visitFrame} method are ignored, and the stack
+ * map frames are recomputed from the methods bytecode. The arguments of the {@link
+ * MethodVisitor#visitMaxs} method are also ignored and recomputed from the bytecode. In other
+ * words, {@link #COMPUTE_FRAMES} implies {@link #COMPUTE_MAXS}.
+ *
+ * @see #ClassWriter(int)
+ */
+ public static final int COMPUTE_FRAMES = 2;
+
+ // Note: fields are ordered as in the ClassFile structure, and those related to attributes are
+ // ordered as in Section 4.7 of the JVMS.
+
+ /**
+ * The minor_version and major_version fields of the JVMS ClassFile structure. minor_version is
+ * stored in the 16 most significant bits, and major_version in the 16 least significant bits.
+ */
+ private int version;
+
+ /**
+ * The symbol table for this class (contains the constant_pool and the BootstrapMethods).
+ */
+ private final SymbolTable symbolTable;
+
+ /**
+ * The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
+ * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
+ * ClassFile structure.
+ */
+ private int accessFlags;
+
+ /**
+ * The this_class field of the JVMS ClassFile structure.
+ */
+ private int thisClass;
+
+ /**
+ * The super_class field of the JVMS ClassFile structure.
+ */
+ private int superClass;
+
+ /**
+ * The interface_count field of the JVMS ClassFile structure.
+ */
+ private int interfaceCount;
+
+ /**
+ * The 'interfaces' array of the JVMS ClassFile structure.
+ */
+ private int[] interfaces;
+
+ /**
+ * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their
+ * {@link FieldWriter#fv} field. This field stores the first element of this list.
+ */
+ private FieldWriter firstField;
+
+ /**
+ * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their
+ * {@link FieldWriter#fv} field. This field stores the last element of this list.
+ */
+ private FieldWriter lastField;
+
+ /**
+ * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their
+ * {@link MethodWriter#mv} field. This field stores the first element of this list.
+ */
+ private MethodWriter firstMethod;
+
+ /**
+ * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their
+ * {@link MethodWriter#mv} field. This field stores the last element of this list.
+ */
+ private MethodWriter lastMethod;
+
+ /**
+ * The number_of_classes field of the InnerClasses attribute, or 0.
+ */
+ private int numberOfInnerClasses;
+
+ /**
+ * The 'classes' array of the InnerClasses attribute, or {@literal null}.
+ */
+ private ByteVector innerClasses;
+
+ /**
+ * The class_index field of the EnclosingMethod attribute, or 0.
+ */
+ private int enclosingClassIndex;
+
+ /**
+ * The method_index field of the EnclosingMethod attribute.
+ */
+ private int enclosingMethodIndex;
+
+ /**
+ * The signature_index field of the Signature attribute, or 0.
+ */
+ private int signatureIndex;
+
+ /**
+ * The source_file_index field of the SourceFile attribute, or 0.
+ */
+ private int sourceFileIndex;
+
+ /**
+ * The debug_extension field of the SourceDebugExtension attribute, or {@literal null}.
+ */
+ private ByteVector debugExtension;
+
+ /**
+ * The last runtime visible annotation of this class. 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 class. 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 class. 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 class. The previous ones can be accessed
+ * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
+ */
+ private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
+
+ /**
+ * The Module attribute of this class, or {@literal null}.
+ */
+ private ModuleWriter moduleWriter;
+
+ /**
+ * The host_class_index field of the NestHost attribute, or 0.
+ */
+ private int nestHostClassIndex;
+
+ /**
+ * The number_of_classes field of the NestMembers attribute, or 0.
+ */
+ private int numberOfNestMemberClasses;
+
+ /**
+ * The 'classes' array of the NestMembers attribute, or {@literal null}.
+ */
+ private ByteVector nestMemberClasses;
+
+ /**
+ * The first non standard attribute of this class. The next ones can be accessed with the {@link
+ * Attribute#nextAttribute} field. May be {@literal null}.
+ *
+ *
WARNING: this list stores the attributes in the reverse order of their visit.
+ * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link
+ * #toByteArray} method writes the attributes in the order defined by this list, i.e. in the
+ * reverse order specified by the user.
+ */
+ private Attribute firstAttribute;
+
+ /**
+ * Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link
+ * MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link
+ * MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}.
+ */
+ private int compute;
+
+ // -----------------------------------------------------------------------------------------------
+ // Constructor
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link ClassWriter} object.
+ *
+ * @param flags option flags that can be used to modify the default behavior of this class. Must
+ * be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}.
+ */
+ public ClassWriter(final int flags) {
+ this(null, flags);
+ }
+
+ /**
+ * Constructs a new {@link ClassWriter} object and enables optimizations for "mostly add" bytecode
+ * transformations. These optimizations are the following:
+ *
+ *
+ *
The constant pool and bootstrap methods from the original class are copied as is in the
+ * new class, which saves time. New constant pool entries and new bootstrap methods will be
+ * added at the end if necessary, but unused constant pool entries or bootstrap methods
+ * won't be removed.
+ *
Methods that are not transformed are copied as is in the new class, directly from the
+ * original class bytecode (i.e. without emitting visit events for all the method
+ * instructions), which saves a lot of time. Untransformed methods are detected by
+ * the fact that the {@link ClassReader} receives {@link MethodVisitor} objects that come
+ * from a {@link ClassWriter} (and not from any other {@link ClassVisitor} instance).
+ *
+ *
+ * @param classReader the {@link ClassReader} used to read the original class. It will be used to
+ * copy the entire constant pool and bootstrap methods from the original class and also to
+ * copy other fragments of original bytecode where applicable.
+ * @param flags option flags that can be used to modify the default behavior of this class.Must be
+ * zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. These option flags do
+ * not affect methods that are copied as is in the new class. This means that neither the
+ * maximum stack size nor the stack frames will be computed for these methods.
+ */
+ public ClassWriter(final ClassReader classReader, final int flags) {
+ super(Opcodes.ASM7);
+ symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
+ if ((flags & COMPUTE_FRAMES) != 0) {
+ this.compute = MethodWriter.COMPUTE_ALL_FRAMES;
+ } else if ((flags & COMPUTE_MAXS) != 0) {
+ this.compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
+ } else {
+ this.compute = MethodWriter.COMPUTE_NOTHING;
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Implementation of the ClassVisitor abstract class
+ // -----------------------------------------------------------------------------------------------
+
+ @Override
+ public final void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces) {
+ this.version = version;
+
+ //replace all pre jre minor versions to one that's outside of the old format
+ if ((version & 0xFFFF) == 45 && (version >>> 16) <= 2) {
+ this.version = (3 << 16) | 45;
+ }
+ this.accessFlags = access;
+ this.thisClass = symbolTable.setMajorVersionAndClassName(this.version & 0xFFFF, name);
+ if (signature != null) {
+ this.signatureIndex = symbolTable.addConstantUtf8(signature);
+ }
+ this.superClass = superName == null ? 0 : symbolTable.addConstantClass(superName).index;
+ if (interfaces != null && interfaces.length > 0) {
+ interfaceCount = interfaces.length;
+ this.interfaces = new int[interfaceCount];
+ for (int i = 0; i < interfaceCount; ++i) {
+ this.interfaces[i] = symbolTable.addConstantClass(interfaces[i]).index;
+ }
+ }
+ if (compute == MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL && (this.version & 0xFFFF) >= Opcodes.V1_7) {
+ compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES;
+ }
+ }
+
+ @Override
+ public final void visitSource(final String file, final String debug) {
+ if (file != null) {
+ sourceFileIndex = symbolTable.addConstantUtf8(file);
+ }
+ if (debug != null) {
+ debugExtension = new ByteVector().encodeUtf8(debug, 0, Integer.MAX_VALUE);
+ }
+ }
+
+ @Override
+ public final ModuleVisitor visitModule(
+ final String name, final int access, final String version) {
+ return moduleWriter =
+ new ModuleWriter(
+ symbolTable,
+ symbolTable.addConstantModule(name).index,
+ access,
+ version == null ? 0 : symbolTable.addConstantUtf8(version));
+ }
+
+ @Override
+ public void visitNestHost(final String nestHost) {
+ nestHostClassIndex = symbolTable.addConstantClass(nestHost).index;
+ }
+
+ @Override
+ public final void visitOuterClass(
+ final String owner, final String name, final String descriptor) {
+ enclosingClassIndex = symbolTable.addConstantClass(owner).index;
+ if (name != null && descriptor != null) {
+ enclosingMethodIndex = symbolTable.addConstantNameAndType(name, descriptor);
+ }
+ }
+
+ @Override
+ public final 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 final 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 final 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 visitNestMember(final String nestMember) {
+ if (nestMemberClasses == null) {
+ nestMemberClasses = new ByteVector();
+ }
+ ++numberOfNestMemberClasses;
+ nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index);
+ }
+
+ @Override
+ public final void visitInnerClass(
+ final String name, final String outerName, final String innerName, final int access) {
+ if (innerClasses == null) {
+ innerClasses = new ByteVector();
+ }
+ // Section 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the constant_pool table
+ // which represents a class or interface C that is not a package member must have exactly one
+ // corresponding entry in the classes array". To avoid duplicates we keep track in the info
+ // field of the Symbol of each CONSTANT_Class_info entry C whether an inner class entry has
+ // already been added for C. If so, we store the index of this inner class entry (plus one) in
+ // the info field. This trick allows duplicate detection in O(1) time.
+ Symbol nameSymbol = symbolTable.addConstantClass(name);
+ if (nameSymbol.info == 0) {
+ ++numberOfInnerClasses;
+ innerClasses.putShort(nameSymbol.index);
+ innerClasses.putShort(outerName == null ? 0 : symbolTable.addConstantClass(outerName).index);
+ innerClasses.putShort(innerName == null ? 0 : symbolTable.addConstantUtf8(innerName));
+ innerClasses.putShort(access);
+ nameSymbol.info = numberOfInnerClasses;
+ }
+ // Else, compare the inner classes entry nameSymbol.info - 1 with the arguments of this method
+ // and throw an exception if there is a difference?
+ }
+
+ @Override
+ public final FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final Object value) {
+ FieldWriter fieldWriter =
+ new FieldWriter(symbolTable, access, name, descriptor, signature, value);
+ if (firstField == null) {
+ firstField = fieldWriter;
+ } else {
+ lastField.fv = fieldWriter;
+ }
+ return lastField = fieldWriter;
+ }
+
+ @Override
+ public final MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final String[] exceptions) {
+ MethodWriter methodWriter =
+ new MethodWriter(symbolTable, access, name, descriptor, signature, exceptions, compute);
+ if (firstMethod == null) {
+ firstMethod = methodWriter;
+ } else {
+ lastMethod.mv = methodWriter;
+ }
+ return lastMethod = methodWriter;
+ }
+
+ @Override
+ public final void visitEnd() {
+ // Nothing to do.
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Other public methods
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the content of the class file that was built by this ClassWriter.
+ *
+ * @return the binary content of the JVMS ClassFile structure that was built by this ClassWriter.
+ * @throws ClassTooLargeException if the constant pool of the class is too large.
+ * @throws MethodTooLargeException if the Code attribute of a method is too large.
+ */
+ public byte[] toByteArray() {
+ // First step: compute the size in bytes of the ClassFile structure.
+ // The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version,
+ // constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count,
+ // methods_count and attributes_count) use 2 bytes each, and each interface uses 2 bytes too.
+ int size = 24 + 2 * interfaceCount;
+ int fieldsCount = 0;
+ FieldWriter fieldWriter = firstField;
+ while (fieldWriter != null) {
+ ++fieldsCount;
+ size += fieldWriter.computeFieldInfoSize();
+ fieldWriter = (FieldWriter) fieldWriter.fv;
+ }
+ int methodsCount = 0;
+ MethodWriter methodWriter = firstMethod;
+ while (methodWriter != null) {
+ ++methodsCount;
+ size += methodWriter.computeMethodInfoSize();
+ methodWriter = (MethodWriter) methodWriter.mv;
+ }
+ // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
+ int attributesCount = 0;
+ if (innerClasses != null) {
+ ++attributesCount;
+ size += 8 + innerClasses.length;
+ symbolTable.addConstantUtf8(Constants.INNER_CLASSES);
+ }
+ if (enclosingClassIndex != 0) {
+ ++attributesCount;
+ size += 10;
+ symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD);
+ }
+ if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) {
+ ++attributesCount;
+ size += 6;
+ symbolTable.addConstantUtf8(Constants.SYNTHETIC);
+ }
+ if (signatureIndex != 0) {
+ ++attributesCount;
+ size += 8;
+ symbolTable.addConstantUtf8(Constants.SIGNATURE);
+ }
+ if (sourceFileIndex != 0) {
+ ++attributesCount;
+ size += 8;
+ symbolTable.addConstantUtf8(Constants.SOURCE_FILE);
+ }
+ if (debugExtension != null) {
+ ++attributesCount;
+ size += 6 + debugExtension.length;
+ symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION);
+ }
+ if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
+ ++attributesCount;
+ size += 6;
+ symbolTable.addConstantUtf8(Constants.DEPRECATED);
+ }
+ if (lastRuntimeVisibleAnnotation != null) {
+ ++attributesCount;
+ size +=
+ lastRuntimeVisibleAnnotation.computeAnnotationsSize(
+ Constants.RUNTIME_VISIBLE_ANNOTATIONS);
+ }
+ if (lastRuntimeInvisibleAnnotation != null) {
+ ++attributesCount;
+ size +=
+ lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
+ Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
+ }
+ if (lastRuntimeVisibleTypeAnnotation != null) {
+ ++attributesCount;
+ size +=
+ lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
+ Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+ }
+ if (lastRuntimeInvisibleTypeAnnotation != null) {
+ ++attributesCount;
+ size +=
+ lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
+ Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
+ }
+ if (symbolTable.computeBootstrapMethodsSize() > 0) {
+ ++attributesCount;
+ size += symbolTable.computeBootstrapMethodsSize();
+ }
+ if (moduleWriter != null) {
+ attributesCount += moduleWriter.getAttributeCount();
+ size += moduleWriter.computeAttributesSize();
+ }
+ if (nestHostClassIndex != 0) {
+ ++attributesCount;
+ size += 8;
+ symbolTable.addConstantUtf8(Constants.NEST_HOST);
+ }
+ if (nestMemberClasses != null) {
+ ++attributesCount;
+ size += 8 + nestMemberClasses.length;
+ symbolTable.addConstantUtf8(Constants.NEST_MEMBERS);
+ }
+ if (firstAttribute != null) {
+ attributesCount += firstAttribute.getAttributeCount();
+ size += firstAttribute.computeAttributesSize(symbolTable);
+ }
+ // IMPORTANT: this must be the last part of the ClassFile size computation, because the previous
+ // statements can add attribute names to the constant pool, thereby changing its size!
+ size += symbolTable.getConstantPoolLength();
+ int constantPoolCount = symbolTable.getConstantPoolCount();
+ if (constantPoolCount > 0xFFFF) {
+ throw new ClassTooLargeException(symbolTable.getClassName(), constantPoolCount);
+ }
+
+ // Second step: allocate a ByteVector of the correct size (in order to avoid any array copy in
+ // dynamic resizes) and fill it with the ClassFile content.
+ ByteVector result = new ByteVector(size);
+ result.putInt(0xCAFEBABE).putInt(version);
+ symbolTable.putConstantPool(result);
+ int mask = (version & 0xFFFF) < Opcodes.V1_5 ? Opcodes.ACC_SYNTHETIC : 0;
+ result.putShort(accessFlags & ~mask).putShort(thisClass).putShort(superClass);
+ result.putShort(interfaceCount);
+ for (int i = 0; i < interfaceCount; ++i) {
+ result.putShort(interfaces[i]);
+ }
+ result.putShort(fieldsCount);
+ fieldWriter = firstField;
+ while (fieldWriter != null) {
+ fieldWriter.putFieldInfo(result);
+ fieldWriter = (FieldWriter) fieldWriter.fv;
+ }
+ result.putShort(methodsCount);
+ boolean hasFrames = false;
+ boolean hasAsmInstructions = false;
+ methodWriter = firstMethod;
+ while (methodWriter != null) {
+ hasFrames |= methodWriter.hasFrames();
+ hasAsmInstructions |= methodWriter.hasAsmInstructions();
+ methodWriter.putMethodInfo(result);
+ methodWriter = (MethodWriter) methodWriter.mv;
+ }
+ // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
+ result.putShort(attributesCount);
+ if (innerClasses != null) {
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.INNER_CLASSES))
+ .putInt(innerClasses.length + 2)
+ .putShort(numberOfInnerClasses)
+ .putByteArray(innerClasses.data, 0, innerClasses.length);
+ }
+ if (enclosingClassIndex != 0) {
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD))
+ .putInt(4)
+ .putShort(enclosingClassIndex)
+ .putShort(enclosingMethodIndex);
+ }
+ if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) {
+ result.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
+ }
+ if (signatureIndex != 0) {
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
+ .putInt(2)
+ .putShort(signatureIndex);
+ }
+ if (sourceFileIndex != 0) {
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_FILE))
+ .putInt(2)
+ .putShort(sourceFileIndex);
+ }
+ if (debugExtension != null) {
+ int length = debugExtension.length;
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION))
+ .putInt(length)
+ .putByteArray(debugExtension.data, 0, length);
+ }
+ if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
+ result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
+ }
+ AnnotationWriter.putAnnotations(
+ symbolTable,
+ lastRuntimeVisibleAnnotation,
+ lastRuntimeInvisibleAnnotation,
+ lastRuntimeVisibleTypeAnnotation,
+ lastRuntimeInvisibleTypeAnnotation,
+ result);
+ symbolTable.putBootstrapMethods(result);
+ if (moduleWriter != null) {
+ moduleWriter.putAttributes(result);
+ }
+ if (nestHostClassIndex != 0) {
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.NEST_HOST))
+ .putInt(2)
+ .putShort(nestHostClassIndex);
+ }
+ if (nestMemberClasses != null) {
+ result
+ .putShort(symbolTable.addConstantUtf8(Constants.NEST_MEMBERS))
+ .putInt(nestMemberClasses.length + 2)
+ .putShort(numberOfNestMemberClasses)
+ .putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length);
+ }
+ if (firstAttribute != null) {
+ firstAttribute.putAttributes(symbolTable, result);
+ }
+
+ // Third step: replace the ASM specific instructions, if any.
+ if (hasAsmInstructions) {
+ return replaceAsmInstructions(result.data, hasFrames);
+ } else {
+ return result.data;
+ }
+ }
+
+ /**
+ * Returns the equivalent of the given class file, with the ASM specific instructions replaced
+ * with standard ones. This is done with a ClassReader -> ClassWriter round trip.
+ *
+ * @param classFile a class file containing ASM specific instructions, generated by this
+ * ClassWriter.
+ * @param hasFrames whether there is at least one stack map frames in 'classFile'.
+ * @return an equivalent of 'classFile', with the ASM specific instructions replaced with standard
+ * ones.
+ */
+ private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) {
+ final Attribute[] attributes = getAttributePrototypes();
+ firstField = null;
+ lastField = null;
+ firstMethod = null;
+ lastMethod = null;
+ lastRuntimeVisibleAnnotation = null;
+ lastRuntimeInvisibleAnnotation = null;
+ lastRuntimeVisibleTypeAnnotation = null;
+ lastRuntimeInvisibleTypeAnnotation = null;
+ moduleWriter = null;
+ nestHostClassIndex = 0;
+ numberOfNestMemberClasses = 0;
+ nestMemberClasses = null;
+ firstAttribute = null;
+ compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
+ new ClassReader(classFile, 0, /* checkClassVersion = */ false)
+ .accept(
+ this,
+ attributes,
+ (hasFrames ? ClassReader.EXPAND_FRAMES : 0) | ClassReader.EXPAND_ASM_INSNS);
+ return toByteArray();
+ }
+
+ /**
+ * Returns the prototypes of the attributes used by this class, its fields and its methods.
+ *
+ * @return the prototypes of the attributes used by this class, its fields and its methods.
+ */
+ private Attribute[] getAttributePrototypes() {
+ Attribute.Set attributePrototypes = new Attribute.Set();
+ attributePrototypes.addAttributes(firstAttribute);
+ FieldWriter fieldWriter = firstField;
+ while (fieldWriter != null) {
+ fieldWriter.collectAttributePrototypes(attributePrototypes);
+ fieldWriter = (FieldWriter) fieldWriter.fv;
+ }
+ MethodWriter methodWriter = firstMethod;
+ while (methodWriter != null) {
+ methodWriter.collectAttributePrototypes(attributePrototypes);
+ methodWriter = (MethodWriter) methodWriter.mv;
+ }
+ return attributePrototypes.toArray();
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Utility methods: constant pool management for Attribute sub classes
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Adds a number or string constant to the constant pool of the class being build. Does nothing if
+ * the constant pool already contains a similar item. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param value the value of the constant to be added to the constant pool. This parameter must be
+ * an {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double} or a {@link String}.
+ * @return the index of a new or already existing constant item with the given value.
+ */
+ public int newConst(final Object value) {
+ return symbolTable.addConstant(value).index;
+ }
+
+ /**
+ * Adds an UTF8 string to the constant pool of the class being build. Does nothing if the constant
+ * pool already contains a similar item. This method is intended for {@link Attribute} sub
+ * classes, and is normally not needed by class generators or adapters.
+ *
+ * @param value the String value.
+ * @return the index of a new or already existing UTF8 item.
+ */
+ // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
+ public int newUTF8(final String value) {
+ return symbolTable.addConstantUtf8(value);
+ }
+
+ /**
+ * Adds a class reference to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param value the internal name of the class.
+ * @return the index of a new or already existing class reference item.
+ */
+ public int newClass(final String value) {
+ return symbolTable.addConstantClass(value).index;
+ }
+
+ /**
+ * Adds a method type reference to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param methodDescriptor method descriptor of the method type.
+ * @return the index of a new or already existing method type reference item.
+ */
+ public int newMethodType(final String methodDescriptor) {
+ return symbolTable.addConstantMethodType(methodDescriptor).index;
+ }
+
+ /**
+ * Adds a module reference to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param moduleName name of the module.
+ * @return the index of a new or already existing module reference item.
+ */
+ public int newModule(final String moduleName) {
+ return symbolTable.addConstantModule(moduleName).index;
+ }
+
+ /**
+ * Adds a package reference to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param packageName name of the package in its internal form.
+ * @return the index of a new or already existing module reference item.
+ */
+ public int newPackage(final String packageName) {
+ return symbolTable.addConstantPackage(packageName).index;
+ }
+
+ /**
+ * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool
+ * already contains a similar item. This method is intended for {@link Attribute} sub classes,
+ * and is normally not needed by class generators or adapters.
+ *
+ * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link
+ * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
+ * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
+ * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
+ * @param owner the internal name of the field or method owner class.
+ * @param name the name of the field or method.
+ * @param descriptor the descriptor of the field or method.
+ * @return the index of a new or already existing method type reference item.
+ * @deprecated this method is superseded by {@link #newHandle(int, String, String, String,
+ * boolean)}.
+ */
+ @Deprecated
+ public int newHandle(
+ final int tag, final String owner, final String name, final String descriptor) {
+ return newHandle(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE);
+ }
+
+ /**
+ * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool
+ * already contains a similar item. This method is intended for {@link Attribute} sub classes,
+ * and is normally not needed by class generators or adapters.
+ *
+ * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link
+ * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
+ * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
+ * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
+ * @param owner the internal name of the field or method owner class.
+ * @param name the name of the field or method.
+ * @param descriptor the descriptor of the field or method.
+ * @param isInterface true if the owner is an interface.
+ * @return the index of a new or already existing method type reference item.
+ */
+ public int newHandle(
+ final int tag,
+ final String owner,
+ final String name,
+ final String descriptor,
+ final boolean isInterface) {
+ return symbolTable.addConstantMethodHandle(tag, owner, name, descriptor, isInterface).index;
+ }
+
+ /**
+ * Adds a dynamic constant reference to the constant pool of the class being build. Does nothing
+ * if the constant pool already contains a similar item. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param name name of the invoked method.
+ * @param descriptor field descriptor of the constant type.
+ * @param bootstrapMethodHandle the bootstrap method.
+ * @param bootstrapMethodArguments the bootstrap method constant arguments.
+ * @return the index of a new or already existing dynamic constant reference item.
+ */
+ public int newConstantDynamic(
+ final String name,
+ final String descriptor,
+ final Handle bootstrapMethodHandle,
+ final Object... bootstrapMethodArguments) {
+ return symbolTable.addConstantDynamic(
+ name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
+ .index;
+ }
+
+ /**
+ * Adds an invokedynamic reference to the constant pool of the class being build. Does nothing if
+ * the constant pool already contains a similar item. This method is intended for {@link
+ * Attribute} sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param name name of the invoked method.
+ * @param descriptor descriptor of the invoke method.
+ * @param bootstrapMethodHandle the bootstrap method.
+ * @param bootstrapMethodArguments the bootstrap method constant arguments.
+ * @return the index of a new or already existing invokedynamic reference item.
+ */
+ public int newInvokeDynamic(
+ final String name,
+ final String descriptor,
+ final Handle bootstrapMethodHandle,
+ final Object... bootstrapMethodArguments) {
+ return symbolTable.addConstantInvokeDynamic(
+ name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
+ .index;
+ }
+
+ /**
+ * Adds a field reference to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param owner the internal name of the field's owner class.
+ * @param name the field's name.
+ * @param descriptor the field's descriptor.
+ * @return the index of a new or already existing field reference item.
+ */
+ public int newField(final String owner, final String name, final String descriptor) {
+ return symbolTable.addConstantFieldref(owner, name, descriptor).index;
+ }
+
+ /**
+ * Adds a method reference to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param owner the internal name of the method's owner class.
+ * @param name the method's name.
+ * @param descriptor the method's descriptor.
+ * @param isInterface {@literal true} if {@code owner} is an interface.
+ * @return the index of a new or already existing method reference item.
+ */
+ public int newMethod(
+ final String owner, final String name, final String descriptor, final boolean isInterface) {
+ return symbolTable.addConstantMethodref(owner, name, descriptor, isInterface).index;
+ }
+
+ /**
+ * Adds a name and type to the constant pool of the class being build. Does nothing if the
+ * constant pool already contains a similar item. This method is intended for {@link Attribute}
+ * sub classes, and is normally not needed by class generators or adapters.
+ *
+ * @param name a name.
+ * @param descriptor a type descriptor.
+ * @return the index of a new or already existing name and type item.
+ */
+ public int newNameType(final String name, final String descriptor) {
+ return symbolTable.addConstantNameAndType(name, descriptor);
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Default method to compute common super classes when computing stack map frames
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the common super type of the two given types. The default implementation of this method
+ * loads the two given classes and uses the java.lang.Class methods to find the common
+ * super class. It can be overridden to compute this common super type in other ways, in
+ * particular without actually loading any class, or to take into account the class that is
+ * currently being generated by this ClassWriter, which can of course not be loaded since it is
+ * under construction.
+ *
+ * @param type1 the internal name of a class.
+ * @param type2 the internal name of another class.
+ * @return the internal name of the common super class of the two given classes.
+ */
+ protected String getCommonSuperClass(final String type1, final String type2) {
+ ClassLoader classLoader = getClassLoader();
+ Class> class1;
+ try {
+ class1 = Class.forName(type1.replace('/', '.'), false, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new TypeNotPresentException(type1, e);
+ }
+ Class> class2;
+ try {
+ class2 = Class.forName(type2.replace('/', '.'), false, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new TypeNotPresentException(type2, e);
+ }
+ if (class1.isAssignableFrom(class2)) {
+ return type1;
+ }
+ if (class2.isAssignableFrom(class1)) {
+ return type2;
+ }
+ if (class1.isInterface() || class2.isInterface()) {
+ return "java/lang/Object";
+ } else {
+ do {
+ class1 = class1.getSuperclass();
+ } while (!class1.isAssignableFrom(class2));
+ return class1.getName().replace('.', '/');
+ }
+ }
+
+ /**
+ * Returns the {@link ClassLoader} to be used by the default implementation of {@link
+ * #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by
+ * default.
+ *
+ * @return ClassLoader
+ */
+ protected ClassLoader getClassLoader() {
+ return getClass().getClassLoader();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/the/bytecode/club/bootloader/AbstractLoaderFactory.java b/src/main/java/the/bytecode/club/bootloader/AbstractLoaderFactory.java
index 9a3e47ca..c8f1ae99 100644
--- a/src/main/java/the/bytecode/club/bootloader/AbstractLoaderFactory.java
+++ b/src/main/java/the/bytecode/club/bootloader/AbstractLoaderFactory.java
@@ -2,7 +2,6 @@ package the.bytecode.club.bootloader;
import java.util.HashMap;
import java.util.Map;
-
import the.bytecode.club.bootloader.resource.ExternalResource;
/***************************************************************************
diff --git a/src/main/java/the/bytecode/club/bootloader/Boot.java b/src/main/java/the/bytecode/club/bootloader/Boot.java
index 48709bba..450fab22 100644
--- a/src/main/java/the/bytecode/club/bootloader/Boot.java
+++ b/src/main/java/the/bytecode/club/bootloader/Boot.java
@@ -6,18 +6,15 @@ import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
-
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
-
-import org.apache.commons.io.FileUtils;
-
import me.konloch.kontainer.io.HTTPRequest;
+import org.apache.commons.io.FileUtils;
import the.bytecode.club.bootloader.resource.EmptyExternalResource;
import the.bytecode.club.bootloader.resource.ExternalResource;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
-import the.bytecode.club.bytecodeviewer.util.ZipUtils;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
+import the.bytecode.club.bytecodeviewer.util.ZipUtils;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@@ -50,9 +47,9 @@ public class Boot {
public static boolean downloading = false;
private static InitialBootScreen screen;
- private static List libsList = new ArrayList();
- private static List libsFileList = new ArrayList();
- private static List urlList = new ArrayList();
+ private static final List libsList = new ArrayList();
+ private static final List libsFileList = new ArrayList();
+ private static final List urlList = new ArrayList();
static {
try {
@@ -62,8 +59,7 @@ public class Boot {
}
}
- public static void boot(String[] args, boolean CLI) throws Exception
- {
+ public static void boot(String[] args, boolean CLI) throws Exception {
bootstrap();
ILoader> loader = findLoader();
@@ -108,7 +104,9 @@ public class Boot {
}
if (urlList.isEmpty()) {
- JOptionPane.showMessageDialog(null, "Bytecode Viewer ran into an issue, for some reason github is not returning what we're expecting. Please try rebooting, if this issue persists please contact @Konloch.", "Error", JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(null, "Bytecode Viewer ran into an issue, for some reason github is not "
+ + "returning what we're expecting. Please try rebooting, if this issue persists please contact "
+ + "@Konloch.", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
@@ -125,7 +123,8 @@ public class Boot {
int completedCheck = 0;
for (String s : urlList) {
- String fileName = s.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length(), s.length());
+ String fileName = s.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length()
+ );
File file = new File(libsDirectory, fileName);
boolean passed = false;
@@ -177,9 +176,8 @@ public class Boot {
System.out.println("Verifying " + fileName + "...");
File f = new File(BytecodeViewer.tempDirectory, "temp");
- if(!f.exists())
- {
- f.getParentFile().mkdirs();
+ if (!f.exists()) {
+ f.getParentFile().mkdirs();
}
ZipUtils.zipFile(file, f);
f.delete();
@@ -223,7 +221,9 @@ public class Boot {
File f = new File(s);
boolean delete = true;
for (String urlS : urlList) {
- String fileName = urlS.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length(), urlS.length());
+ String fileName =
+ urlS.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length()
+ );
if (fileName.equals(f.getName()))
delete = false;
}
@@ -250,7 +250,8 @@ public class Boot {
} catch (Exception e) {
e.printStackTrace();
f.delete();
- JOptionPane.showMessageDialog(null, "Error, Library " + f.getName() + " is corrupt, please restart to redownload it.",
+ JOptionPane.showMessageDialog(null, "Error, Library " + f.getName() + " is corrupt, please "
+ + "restart to redownload it.",
"Error", JOptionPane.ERROR_MESSAGE);
}
}
@@ -318,31 +319,30 @@ public class Boot {
}
}
- public static void dropKrakatau()
- {
- File temp = new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "krakatau_" + BytecodeViewer.krakatauVersion + ".zip");
+ public static void dropKrakatau() {
+ File temp =
+ new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "krakatau_" + BytecodeViewer.krakatauVersion + ".zip");
File krakatauDirectory = new File(BytecodeViewer.krakatauWorkingDirectory);
- BytecodeViewer.krakatauWorkingDirectory = BytecodeViewer.krakatauWorkingDirectory + BytecodeViewer.fs + "Krakatau-master";
- if (!krakatauDirectory.exists() || temp.exists())
- {
- if(temp.exists())
+ BytecodeViewer.krakatauWorkingDirectory = BytecodeViewer.krakatauWorkingDirectory + BytecodeViewer.fs +
+ "Krakatau-master";
+ if (!krakatauDirectory.exists() || temp.exists()) {
+ if (temp.exists())
temp.delete();
setState("Bytecode Viewer Boot Screen - Extracting Krakatau");
System.out.println("Extracting Krakatau");
- try
- {
+ try {
while (temp.exists())
temp.delete();
- InputStream is = BytecodeViewer.class.getClassLoader().getResourceAsStream("Krakatau-" + BytecodeViewer.krakatauVersion + ".zip");
+ InputStream is =
+ BytecodeViewer.class.getClassLoader().getResourceAsStream("Krakatau-" + BytecodeViewer.krakatauVersion + ".zip");
FileOutputStream baos = new FileOutputStream(temp);
int r = 0;
byte[] buffer = new byte[8192];
- while ((r = is.read(buffer)) >= 0)
- {
+ while ((r = is.read(buffer)) >= 0) {
baos.write(buffer, 0, r);
}
@@ -350,39 +350,36 @@ public class Boot {
ZipUtils.unzipFilesToPath(temp.getAbsolutePath(), krakatauDirectory.getAbsolutePath());
temp.delete();
System.out.println("Succesfully extracted Krakatau");
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
setState("Bytecode Viewer Boot Screen - ERROR, please contact @Konloch with your stacktrace.");
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
}
- public static void dropEnjarify()
- {
- File temp = new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "enjarify" + BytecodeViewer.enjarifyVersion + ".zip");
+ public static void dropEnjarify() {
+ File temp =
+ new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "enjarify" + BytecodeViewer.enjarifyVersion + ".zip");
File enjarifyDirectory = new File(BytecodeViewer.enjarifyWorkingDirectory);
- BytecodeViewer.enjarifyWorkingDirectory = BytecodeViewer.enjarifyWorkingDirectory + BytecodeViewer.fs + "enjarify-master";
- if (!enjarifyDirectory.exists() || temp.exists())
- {
- if(temp.exists())
+ BytecodeViewer.enjarifyWorkingDirectory = BytecodeViewer.enjarifyWorkingDirectory + BytecodeViewer.fs +
+ "enjarify-master";
+ if (!enjarifyDirectory.exists() || temp.exists()) {
+ if (temp.exists())
temp.delete();
setState("Bytecode Viewer Boot Screen - Extracting Enjarify");
System.out.println("Extracting Enjarify");
- try
- {
+ try {
while (temp.exists())
temp.delete();
- InputStream is = BytecodeViewer.class.getClassLoader().getResourceAsStream("enjarify-" + BytecodeViewer.enjarifyVersion + ".zip");
+ InputStream is =
+ BytecodeViewer.class.getClassLoader().getResourceAsStream("enjarify-" + BytecodeViewer.enjarifyVersion + ".zip");
FileOutputStream baos = new FileOutputStream(temp);
int r = 0;
byte[] buffer = new byte[8192];
- while ((r = is.read(buffer)) >= 0)
- {
+ while ((r = is.read(buffer)) >= 0) {
baos.write(buffer, 0, r);
}
@@ -390,9 +387,7 @@ public class Boot {
ZipUtils.unzipFilesToPath(temp.getAbsolutePath(), enjarifyDirectory.getAbsolutePath());
temp.delete();
System.out.println("Succesfully extracted Enjarify");
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
setState("Bytecode Viewer Boot Screen - ERROR, please contact @Konloch with your stacktrace.");
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
@@ -401,7 +396,8 @@ public class Boot {
public static void downloadZipsOnly() throws Exception {
for (String s : urlList) {
- String fileName = s.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length(), s.length());
+ String fileName = s.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length()
+ );
File file = new File(libsDir(), fileName);
boolean passed = false;
@@ -495,15 +491,18 @@ public class Boot {
}
}
- BytecodeViewer.enjarifyWorkingDirectory = BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "enjarify_" + BytecodeViewer.enjarifyVersion + BytecodeViewer.fs + "enjarify-master";
- File enjarifyDirectory = new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "enjarify_" + BytecodeViewer.enjarifyVersion);
+ BytecodeViewer.enjarifyWorkingDirectory =
+ BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "enjarify_" + BytecodeViewer.enjarifyVersion + BytecodeViewer.fs + "enjarify-master";
+ File enjarifyDirectory =
+ new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "enjarify_" + BytecodeViewer.enjarifyVersion);
if (!enjarifyDirectory.exists()) {
try {
setState("Bytecode Viewer Boot Screen - Updating to " + enjarifyDirectory.getName() + "...");
ZipUtils.unzipFilesToPath(enjarifyZip.getAbsolutePath(), enjarifyDirectory.getAbsolutePath());
System.out.println("Updated to enjarify v" + BytecodeViewer.enjarifyVersion);
} catch (Exception e) {
- BytecodeViewer.showMessage("ERROR: There was an issue unzipping enjarify (possibly corrupt). Restart BCV." + BytecodeViewer.nl +
+ BytecodeViewer.showMessage("ERROR: There was an issue unzipping enjarify (possibly corrupt). Restart "
+ + "BCV." + BytecodeViewer.nl +
"If the error persists contact @Konloch.");
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
enjarifyZip.delete();
@@ -540,14 +539,16 @@ public class Boot {
BytecodeViewer.krakatauWorkingDirectory = BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "krakatau_" +
BytecodeViewer.krakatauVersion + BytecodeViewer.fs + "Krakatau-master";
- File krakatauDirectory = new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "krakatau_" + BytecodeViewer.krakatauVersion);
+ File krakatauDirectory =
+ new File(BytecodeViewer.getBCVDirectory() + BytecodeViewer.fs + "krakatau_" + BytecodeViewer.krakatauVersion);
if (!krakatauDirectory.exists()) {
try {
setState("Bytecode Viewer Boot Screen - Updating to " + krakatauDirectory.getName() + "...");
ZipUtils.unzipFilesToPath(krakatauZip.getAbsolutePath(), krakatauDirectory.getAbsolutePath());
System.out.println("Updated to krakatau v" + BytecodeViewer.krakatauVersion);
} catch (Exception e) {
- BytecodeViewer.showMessage("ERROR: There was an issue unzipping Krakatau decompiler (possibly corrupt). Restart BCV." + BytecodeViewer.nl +
+ BytecodeViewer.showMessage("ERROR: There was an issue unzipping Krakatau decompiler (possibly "
+ + "corrupt). Restart BCV." + BytecodeViewer.nl +
"If the error persists contact @Konloch.");
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
krakatauZip.delete();
diff --git a/src/main/java/the/bytecode/club/bootloader/ClassPathLoader.java b/src/main/java/the/bytecode/club/bootloader/ClassPathLoader.java
index 4db7a9fc..1e0606e1 100644
--- a/src/main/java/the/bytecode/club/bootloader/ClassPathLoader.java
+++ b/src/main/java/the/bytecode/club/bootloader/ClassPathLoader.java
@@ -4,7 +4,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
-
import the.bytecode.club.bootloader.resource.ExternalResource;
/***************************************************************************
@@ -31,13 +30,14 @@ import the.bytecode.club.bootloader.resource.ExternalResource;
*/
public class ClassPathLoader implements ILoader