diff --git a/src/main/java/org/objectweb/asm/ClassReader.java b/src/main/java/org/objectweb/asm/ClassReader.java deleted file mode 100644 index be0f764d..00000000 --- a/src/main/java/org/objectweb/asm/ClassReader.java +++ /dev/null @@ -1,3642 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package org.objectweb.asm; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * A parser to make a {@link ClassVisitor} visit a ClassFile structure, as defined in the Java - * Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the - * appropriate visit methods of a given {@link ClassVisitor} for each field, method and bytecode - * instruction encountered. - * - * @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 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 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; - - /** - * The size of the temporary byte array used to read class input streams chunk by chunk. - */ - private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096; - - /** - * A byte array containing the JVMS ClassFile structure to be parsed. - * - * @deprecated Use {@link #readByte(int)} and the other read methods instead. This field will - * eventually be deleted. - */ - @Deprecated - // DontCheck(MemberName): can't be renamed (for backward binary compatibility). - public final byte[] b; - - /** - * A byte array containing the JVMS ClassFile structure to be parsed. The content of this array - * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally - * 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 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 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; - - /** - * The offset in bytes of the ClassFile's access_flags field. - */ - public final int header; - - // ----------------------------------------------------------------------------------------------- - // 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; - } - - // 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: - 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: - 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; - } - - /** - * 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; - 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; - - 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, - 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; - } - } - 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.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; - // 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; - 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(); - } - - /** - * 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 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; - } - 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); - 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 deleted file mode 100644 index e119babd..00000000 --- a/src/main/java/org/objectweb/asm/ClassWriter.java +++ /dev/null @@ -1,1002 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package org.objectweb.asm; - -/** - * A {@link ClassVisitor} that generates a corresponding ClassFile structure, as defined in the Java - * Virtual Machine Specification (JVMS). It can be used alone, to generate a Java class "from - * scratch", or with one or more {@link ClassReader} and adapter {@link ClassVisitor} to generate a - * modified class from one or more existing Java classes. - * - * @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: - * - *

- * - * @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