http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1c71aec7/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java ---------------------------------------------------------------------- diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java old mode 100644 new mode 100755 index 6d810e0..41b6de2 --- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java +++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java @@ -1,2756 +1,3603 @@ -/*** - * 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. - */ +// 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.apache.tapestry5.internal.plastic.asm; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** - * A Java class parser to make a {@link ClassVisitor} visit an existing class. - * This class parses a byte array conforming to the Java class file format and - * calls the appropriate visit methods of a given class visitor for each field, - * method and bytecode instruction encountered. - * + * 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. + * + * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html">JVMS 4</a> * @author Eric Bruneton * @author Eugene Kuleshov */ public class ClassReader { - /** - * Flag to skip method code. If this class is set <code>CODE</code> - * attribute won't be visited. This can be used, for example, to retrieve - * annotations for methods and method parameters. - */ - public static final int SKIP_CODE = 1; - - /** - * Flag to skip the debug information in the class. If this flag is set the - * debug information of the class is not visited, i.e. the - * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and - * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be - * called. - */ - public static final int SKIP_DEBUG = 2; - - /** - * Flag to skip the stack map frames in the class. If this flag is set the - * stack map frames of the class is not visited, i.e. the - * {@link MethodVisitor#visitFrame visitFrame} method will not be 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 in the class writer. - */ - public static final int SKIP_FRAMES = 4; - - /** - * 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/recompression step in ClassReader and - * ClassWriter which degrades performances quite a lot). - */ - public static final int EXPAND_FRAMES = 8; - - /** - * Flag to expand the ASM pseudo 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 pseudo instruction using an unsigned 2 - * bytes offset (see 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 <i>not</i> 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 class to be parsed. <i>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 generators or adapters.</i> - */ - public final byte[] b; - - /** - * The start index of each constant pool item in {@link #b b}, plus one. The - * one byte offset skips the constant pool item tag that indicates its type. - */ - private final int[] items; - - /** - * The String objects corresponding to the CONSTANT_Utf8 items. This cache - * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, - * which GREATLY improves performances (by a factor 2 to 3). This caching - * strategy could be extended to all constant pool items, but its benefit - * would not be so great for these items (because they are much less - * expensive to parse than CONSTANT_Utf8 items). - */ - private final String[] strings; - - /** - * Maximum length of the strings contained in the constant pool of the - * class. - */ - private final int maxStringLength; - - /** - * Start index of the class header information (access, name...) in - * {@link #b b}. - */ - public final int header; - - // ------------------------------------------------------------------------ - // Constructors - // ------------------------------------------------------------------------ - - /** - * Constructs a new {@link ClassReader} object. - * - * @param b - * the bytecode of the class to be read. - */ - public ClassReader(final byte[] b) { - this(b, 0, b.length); - } - - /** - * Constructs a new {@link ClassReader} object. - * - * @param b - * the bytecode of the class to be read. - * @param off - * the start offset of the class data. - * @param len - * the length of the class data. - */ - public ClassReader(final byte[] b, final int off, final int len) { - this.b = b; - // checks the class version - if (readShort(off + 6) > Opcodes.V9) { - throw new IllegalArgumentException(); - } - // parses the constant pool - items = new int[readUnsignedShort(off + 8)]; - int n = items.length; - strings = new String[n]; - int max = 0; - int index = off + 10; - for (int i = 1; i < n; ++i) { - items[i] = index + 1; - int size; - switch (b[index]) { - case ClassWriter.FIELD: - case ClassWriter.METH: - case ClassWriter.IMETH: - case ClassWriter.INT: - case ClassWriter.FLOAT: - case ClassWriter.NAME_TYPE: - case ClassWriter.INDY: - size = 5; - break; - case ClassWriter.LONG: - case ClassWriter.DOUBLE: - size = 9; - ++i; - break; - case ClassWriter.UTF8: - size = 3 + readUnsignedShort(index + 1); - if (size > max) { - max = size; - } - break; - case ClassWriter.HANDLE: - size = 4; - break; - // case ClassWriter.CLASS: - // case ClassWriter.STR: - // case ClassWriter.MTYPE - // case ClassWriter.PACKAGE: - // case ClassWriter.MODULE: - default: - size = 3; - break; - } - index += size; - } - maxStringLength = max; - // the class header information starts just after the constant pool - header = index; - } - - /** - * 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() getInternalName}). - * - * @return the internal class name - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public String getClassName() { - return readClass(header + 2, new char[maxStringLength]); - } - - /** - * Returns the internal of name of the super class (see - * {@link Type#getInternalName() getInternalName}). For interfaces, the - * super class is {@link Object}. - * - * @return the internal name of super class, or <tt>null</tt> for - * {@link Object} class. - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public String getSuperName() { - return readClass(header + 4, new char[maxStringLength]); - } - - /** - * Returns the internal names of the class's interfaces (see - * {@link Type#getInternalName() getInternalName}). - * - * @return the array of internal names for all implemented interfaces or - * <tt>null</tt>. - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public String[] getInterfaces() { - int index = header + 6; - int n = readUnsignedShort(index); - String[] interfaces = new String[n]; - if (n > 0) { - char[] buf = new char[maxStringLength]; - for (int i = 0; i < n; ++i) { - index += 2; - interfaces[i] = readClass(index, buf); - } - } - return interfaces; - } - - /** - * Copies the constant pool data into the given {@link ClassWriter}. Should - * be called before the {@link #accept(ClassVisitor,int)} method. - * - * @param classWriter - * the {@link ClassWriter} to copy constant pool into. - */ - void copyPool(final ClassWriter classWriter) { - char[] buf = new char[maxStringLength]; - int ll = items.length; - Item[] items2 = new Item[ll]; - for (int i = 1; i < ll; i++) { - int index = items[i]; - int tag = b[index - 1]; - Item item = new Item(i); - int nameType; - switch (tag) { - case ClassWriter.FIELD: - case ClassWriter.METH: - case ClassWriter.IMETH: - nameType = items[readUnsignedShort(index + 2)]; - item.set(tag, readClass(index, buf), readUTF8(nameType, buf), - readUTF8(nameType + 2, buf)); - break; - case ClassWriter.INT: - item.set(readInt(index)); - break; - case ClassWriter.FLOAT: - item.set(Float.intBitsToFloat(readInt(index))); - break; - case ClassWriter.NAME_TYPE: - item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf), - null); - break; - case ClassWriter.LONG: - item.set(readLong(index)); - ++i; - break; - case ClassWriter.DOUBLE: - item.set(Double.longBitsToDouble(readLong(index))); - ++i; - break; - case ClassWriter.UTF8: { - String s = strings[i]; - if (s == null) { - index = items[i]; - s = strings[i] = readUTF(index + 2, - readUnsignedShort(index), buf); - } - item.set(tag, s, null, null); - break; - } - case ClassWriter.HANDLE: { - int fieldOrMethodRef = items[readUnsignedShort(index + 1)]; - nameType = items[readUnsignedShort(fieldOrMethodRef + 2)]; - item.set(ClassWriter.HANDLE_BASE + readByte(index), - readClass(fieldOrMethodRef, buf), - readUTF8(nameType, buf), readUTF8(nameType + 2, buf)); - break; - } - case ClassWriter.INDY: - if (classWriter.bootstrapMethods == null) { - copyBootstrapMethods(classWriter, items2, buf); - } - nameType = items[readUnsignedShort(index + 2)]; - item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf), - readUnsignedShort(index)); - break; - // case ClassWriter.STR: - // case ClassWriter.CLASS: - // case ClassWriter.MTYPE: - // case ClassWriter.MODULE: - // case ClassWriter.PACKAGE: - default: - item.set(tag, readUTF8(index, buf), null, null); - break; - } + /** + * 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 <i>not</i> 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. <i>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.</i> + * + * <p>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. + */ + // DontCheck(MemberName): can't be renamed (for backward binary compatibility). + public final byte[] b; + + /** + * The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool + * array, <i>plus one</i>. 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 #b} of each element of the bootstrap_methods array (in the + * BootstrapMethods attribute). + * + * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS + * 4.7.23</a> + */ + 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, in {@link #b}, 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. <i>This internal constructor must not be exposed + * as a public API</i>. + * + * @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) { + 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.V12) { + 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 hasConstantDynamic = false; + boolean hasConstantInvokeDynamic = false; + // The offset of the other entries depend on the total size of all the previous entries. + while (currentCpInfoIndex < constantPoolCount) { + cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1; + 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; + hasConstantDynamic = true; + break; + case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG: + cpInfoSize = 5; + hasConstantInvokeDynamic = 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 = + (hasConstantDynamic | hasConstantInvokeDynamic) + ? 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. <i>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</i>. + * @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; + } - int index2 = item.hashCode % items2.length; - item.next = items2[index2]; - items2[index2] = item; - } + // 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 <i>reverse order</i> 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; + } - int off = items[1] - 1; - classWriter.pool.putByteArray(b, off, header - off); - classWriter.items = items2; - classWriter.threshold = (int) (0.75d * ll); - classWriter.index = ll; - } - - /** - * Copies the bootstrap method data into the given {@link ClassWriter}. - * Should be called before the {@link #accept(ClassVisitor,int)} method. - * - * @param classWriter - * the {@link ClassWriter} to copy bootstrap methods into. - */ - private void copyBootstrapMethods(final ClassWriter classWriter, - final Item[] items, final char[] c) { - // finds the "BootstrapMethods" attribute - int u = getAttributes(); - boolean found = false; - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - if ("BootstrapMethods".equals(attrName)) { - found = true; - break; - } - u += 6 + readInt(u + 4); - } - if (!found) { - return; - } - // copies the bootstrap methods in the class writer - int boostrapMethodCount = readUnsignedShort(u + 8); - for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) { - int position = v - u - 10; - int hashCode = readConst(readUnsignedShort(v), c).hashCode(); - for (int k = readUnsignedShort(v + 2); k > 0; --k) { - hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode(); - v += 2; - } - v += 4; - Item item = new Item(j); - item.set(position, hashCode & 0x7FFFFFFF); - int index = item.hashCode % items.length; - item.next = items[index]; - items[index] = item; - } - int attrSize = readInt(u + 4); - ByteVector bootstrapMethods = new ByteVector(attrSize + 62); - bootstrapMethods.putByteArray(b, u + 10, attrSize - 2); - classWriter.bootstrapMethodsCount = boostrapMethodCount; - classWriter.bootstrapMethods = bootstrapMethods; - } - - /** - * Constructs a new {@link ClassReader} object. - * - * @param is - * an input stream from which to read the class. - * @throws IOException - * if a problem occurs during reading. - */ - public ClassReader(final InputStream is) throws IOException { - this(readClass(is, false)); - } - - /** - * Constructs a new {@link ClassReader} object. - * - * @param name - * the binary qualified name of the class to be read. - * @throws IOException - * if an exception occurs during reading. - */ - public ClassReader(final String name) throws IOException { - this(readClass( - ClassLoader.getSystemResourceAsStream(name.replace('.', '/') - + ".class"), true)); - } - - /** - * Reads the bytecode of a class. - * - * @param is - * an input stream from which to read the class. - * @param close - * true to close the input stream after reading. - * @return the bytecode read from the given input stream. - * @throws IOException - * if a problem occurs during reading. - */ - private static byte[] readClass(final InputStream is, boolean close) - throws IOException { - if (is == null) { - throw new IOException("Class not found"); - } - try { - byte[] b = new byte[is.available()]; - int len = 0; - while (true) { - int n = is.read(b, len, b.length - len); - if (n == -1) { - if (len < b.length) { - byte[] c = new byte[len]; - System.arraycopy(b, 0, c, 0, len); - b = c; - } - return b; - } - len += n; - if (len == b.length) { - int last = is.read(); - if (last < 0) { - return b; - } - byte[] c = new byte[b.length + 1000]; - System.arraycopy(b, 0, c, 0, len); - c[len++] = (byte) last; - b = c; - } - } - } finally { - if (close) { - is.close(); - } - } + // 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); } - // ------------------------------------------------------------------------ - // Public methods - // ------------------------------------------------------------------------ - - /** - * Makes the given visitor visit the Java class of this {@link ClassReader} - * . This class is the one specified in the constructor (see - * {@link #ClassReader(byte[]) ClassReader}). - * - * @param classVisitor - * the visitor that must visit this class. - * @param flags - * option flags that can be used to modify the default behavior - * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} - * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. - */ - public void accept(final ClassVisitor classVisitor, final int flags) { - accept(classVisitor, new Attribute[0], flags); - } - - /** - * Makes the given visitor visit the Java class of this {@link ClassReader}. - * This class is the one specified in the constructor (see - * {@link #ClassReader(byte[]) ClassReader}). - * - * @param classVisitor - * the visitor that must visit this class. - * @param attrs - * 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. - * <i>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</i>. - * @param flags - * option flags that can be used to modify the default behavior - * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} - * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. - */ - public void accept(final ClassVisitor classVisitor, - final Attribute[] attrs, final int flags) { - int u = header; // current offset in the class file - char[] c = new char[maxStringLength]; // buffer used to read strings - - Context context = new Context(); - context.attrs = attrs; - context.flags = flags; - context.buffer = c; - - // reads the class declaration - int access = readUnsignedShort(u); - String name = readClass(u + 2, c); - String superClass = readClass(u + 4, c); - String[] interfaces = new String[readUnsignedShort(u + 6)]; - u += 8; - for (int i = 0; i < interfaces.length; ++i) { - interfaces[i] = readClass(u, c); - u += 2; - } + // Visit the Module, ModulePackages and ModuleMainClass attributes. + if (moduleOffset != 0) { + readModuleAttributes( + classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass); + } - // reads the class attributes - String signature = null; - String sourceFile = null; - String sourceDebug = null; - String enclosingOwner = null; - String enclosingName = null; - String enclosingDesc = null; - String moduleMainClass = null; - int anns = 0; - int ianns = 0; - int tanns = 0; - int itanns = 0; - int innerClasses = 0; - int module = 0; - int packages = 0; - Attribute attributes = null; - - u = getAttributes(); - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if ("SourceFile".equals(attrName)) { - sourceFile = readUTF8(u + 8, c); - } else if ("InnerClasses".equals(attrName)) { - innerClasses = u + 8; - } else if ("EnclosingMethod".equals(attrName)) { - enclosingOwner = readClass(u + 8, c); - int item = readUnsignedShort(u + 10); - if (item != 0) { - enclosingName = readUTF8(items[item], c); - enclosingDesc = readUTF8(items[item] + 2, c); - } - } else if ("Signature".equals(attrName)) { - signature = readUTF8(u + 8, c); - } else if ("RuntimeVisibleAnnotations".equals(attrName)) { - anns = u + 8; - } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = u + 8; - } else if ("Deprecated".equals(attrName)) { - access |= Opcodes.ACC_DEPRECATED; - } else if ("Synthetic".equals(attrName)) { - access |= Opcodes.ACC_SYNTHETIC - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; - } else if ("SourceDebugExtension".equals(attrName)) { - int len = readInt(u + 4); - sourceDebug = readUTF(u + 8, len, new char[len]); - } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { - ianns = u + 8; - } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = u + 8; - } else if ("Module".equals(attrName)) { - module = u + 8; - } else if ("ModuleMainClass".equals(attrName)) { - moduleMainClass = readClass(u + 8, c); - } else if ("ModulePackages".equals(attrName)) { - packages = u + 10; - } else if ("BootstrapMethods".equals(attrName)) { - int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; - for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { - bootstrapMethods[j] = v; - v += 2 + readUnsignedShort(v + 2) << 1; - } - context.bootstrapMethods = bootstrapMethods; - } else { - Attribute attr = readAttribute(attrs, attrName, u + 8, - readInt(u + 4), c, -1, null); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - u += 6 + readInt(u + 4); - } + // Visit the NestHost attribute. + if (nestHostClass != null) { + classVisitor.visitNestHost(nestHostClass); + } - // visits the class declaration - classVisitor.visit(readInt(items[1] - 7), access, name, signature, - superClass, interfaces); + // 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); + } - // visits the source and debug info - if ((flags & SKIP_DEBUG) == 0 - && (sourceFile != null || sourceDebug != null)) { - classVisitor.visitSource(sourceFile, sourceDebug); - } + // 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); + } + } - // visits the module info and associated attributes - if (module != 0) { - readModule(classVisitor, context, module, - moduleMainClass, packages); - } - - // visits the outer class - if (enclosingOwner != null) { - classVisitor.visitOuterClass(enclosingOwner, enclosingName, - enclosingDesc); - } + // 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); + } + } - // visits the class annotations and type annotations - if (anns != 0) { - for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitAnnotation(readUTF8(v, c), true)); - } - } - if (ianns != 0) { - for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitAnnotation(readUTF8(v, c), false)); - } - } - if (tanns != 0) { - for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - } - if (itanns != 0) { - for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - } + // 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); + } + } - // visits the attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - classVisitor.visitAttribute(attributes); - attributes = attr; - } + // 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); + } + } - // visits the inner classes - if (innerClasses != 0) { - int v = innerClasses + 2; - for (int i = readUnsignedShort(innerClasses); i > 0; --i) { - classVisitor.visitInnerClass(readClass(v, c), - readClass(v + 2, c), readUTF8(v + 4, c), - readUnsignedShort(v + 6)); - v += 8; - } - } + // 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; + } - // visits the fields and methods - u = header + 10 + 2 * interfaces.length; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - u = readField(classVisitor, context, u); - } - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - u = readMethod(classVisitor, context, u); - } + // 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; + } + } - // visits the end of the class - classVisitor.visitEnd(); - } - - /** - * Reads the module attribute and visit it. - * - * @param classVisitor - * the current class visitor - * @param context - * information about the class being parsed. - * @param u - * start offset of the module attribute in the class file. - * @param mainClass - * name of the main class of a module or null. - * @param packages - * start offset of the concealed package attribute. - */ - private void readModule(final ClassVisitor classVisitor, - final Context context, int u, - final String mainClass, int packages) { - - char[] buffer = context.buffer; - - // reads module name, flags and version - String name = readModule(u, buffer); - int flags = readUnsignedShort(u + 2); - String version = readUTF8(u + 4, buffer); - u += 6; - - ModuleVisitor mv = classVisitor.visitModule(name, flags, version); - if (mv == null) { - return; - } - - // module attributes (main class, packages) - if (mainClass != null) { - mv.visitMainClass(mainClass); - } - - if (packages != 0) { - for (int i = readUnsignedShort(packages - 2); i > 0; --i) { - String packaze = readPackage(packages, buffer); - mv.visitPackage(packaze); - packages += 2; - } - } - - // reads requires - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - String module = readModule(u, buffer); - int access = readUnsignedShort(u + 2); - String requireVersion = readUTF8(u + 4, buffer); - mv.visitRequire(module, access, requireVersion); - u += 6; - } - - // reads exports - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - String export = readPackage(u, buffer); - int access = readUnsignedShort(u + 2); - int exportToCount = readUnsignedShort(u + 4); - u += 6; - String[] tos = null; - if (exportToCount != 0) { - tos = new String[exportToCount]; - for (int j = 0; j < tos.length; ++j) { - tos[j] = readModule(u, buffer); - u += 2; - } - } - mv.visitExport(export, access, tos); - } - - // reads opens - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - String open = readPackage(u, buffer); - int access = readUnsignedShort(u + 2); - int openToCount = readUnsignedShort(u + 4); - u += 6; - String[] tos = null; - if (openToCount != 0) { - tos = new String[openToCount]; - for (int j = 0; j < tos.length; ++j) { - tos[j] = readModule(u, buffer); - u += 2; - } - } - mv.visitOpen(open, access, tos); - } - - // read uses - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - mv.visitUse(readClass(u, buffer)); - u += 2; - } - - // read provides - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - String service = readClass(u, buffer); - int provideWithCount = readUnsignedShort(u + 2); - u += 4; - String[] withs = new String[provideWithCount]; - for (int j = 0; j < withs.length; ++j) { - withs[j] = readClass(u, buffer); - u += 2; - } - mv.visitProvide(service, withs); - } - - mv.visitEnd(); - } - - /** - * Reads a field 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 u - * the start offset of the field in the class file. - * @return the offset of the first byte following the field in the class. - */ - private int readField(final ClassVisitor classVisitor, - final Context context, int u) { - // reads the field declaration - char[] c = context.buffer; - int access = readUnsignedShort(u); - String name = readUTF8(u + 2, c); - String desc = readUTF8(u + 4, c); - u += 6; - - // reads the field attributes - String signature = null; - int anns = 0; - int ianns = 0; - int tanns = 0; - int itanns = 0; - Object value = null; - Attribute attributes = null; - - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if ("ConstantValue".equals(attrName)) { - int item = readUnsignedShort(u + 8); - value = item == 0 ? null : readConst(item, c); - } else if ("Signature".equals(attrName)) { - signature = readUTF8(u + 8, c); - } else if ("Deprecated".equals(attrName)) { - access |= Opcodes.ACC_DEPRECATED; - } else if ("Synthetic".equals(attrName)) { - access |= Opcodes.ACC_SYNTHETIC - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; - } else if ("RuntimeVisibleAnnotations".equals(attrName)) { - anns = u + 8; - } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = u + 8; - } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { - ianns = u + 8; - } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = u + 8; - } else { - Attribute attr = readAttribute(context.attrs, attrName, u + 8, - readInt(u + 4), c, -1, null); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - u += 6 + readInt(u + 4); - } - u += 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; + } + } - // visits the field declaration - FieldVisitor fv = classVisitor.visitField(access, name, desc, - signature, value); - if (fv == null) { - return u; - } + // 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); + } - // visits the field annotations and type annotations - if (anns != 0) { - for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - fv.visitAnnotation(readUTF8(v, c), true)); - } - } - if (ianns != 0) { - for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - fv.visitAnnotation(readUTF8(v, c), false)); - } - } - if (tanns != 0) { - for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - fv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - } - if (itanns != 0) { - for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - fv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - } + // 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; + } - // visits the field attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - fv.visitAttribute(attributes); - attributes = attr; - } + // Visit the ModuleMainClass attribute. + if (moduleMainClass != null) { + moduleVisitor.visitMainClass(moduleMainClass); + } - // visits the end of the field - fv.visitEnd(); - - return u; - } - - /** - * Reads a method 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 u - * the start offset of the method in the class file. - * @return the offset of the first byte following the method in the class. - */ - private int readMethod(final ClassVisitor classVisitor, - final Context context, int u) { - // reads the method declaration - char[] c = context.buffer; - context.access = readUnsignedShort(u); - context.name = readUTF8(u + 2, c); - context.desc = readUTF8(u + 4, c); - u += 6; - - // reads the method attributes - int code = 0; - int exception = 0; - String[] exceptions = null; - String signature = null; - int methodParameters = 0; - int anns = 0; - int ianns = 0; - int tanns = 0; - int itanns = 0; - int dann = 0; - int mpanns = 0; - int impanns = 0; - int firstAttribute = u; - Attribute attributes = null; - - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if ("Code".equals(attrName)) { - if ((context.flags & SKIP_CODE) == 0) { - code = u + 8; - } - } else if ("Exceptions".equals(attrName)) { - exceptions = new String[readUnsignedShort(u + 8)]; - exception = u + 10; - for (int j = 0; j < exceptions.length; ++j) { - exceptions[j] = readClass(exception, c); - exception += 2; - } - } else if ("Signature".equals(attrName)) { - signature = readUTF8(u + 8, c); - } else if ("Deprecated".equals(attrName)) { - context.access |= Opcodes.ACC_DEPRECATED; - } else if ("RuntimeVisibleAnnotations".equals(attrName)) { - anns = u + 8; - } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = u + 8; - } else if ("AnnotationDefault".equals(attrName)) { - dann = u + 8; - } else if ("Synthetic".equals(attrName)) { - context.access |= Opcodes.ACC_SYNTHETIC - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; - } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { - ianns = u + 8; - } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = u + 8; - } else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) { - mpanns = u + 8; - } else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) { - impanns = u + 8; - } else if ("MethodParameters".equals(attrName)) { - methodParameters = u + 8; - } else { - Attribute attr = readAttribute(context.attrs, attrName, u + 8, - readInt(u + 4), c, -1, null); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - u += 6 + readInt(u + 4); - } - u += 2; + // 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; + } + } - // visits the method declaration - MethodVisitor mv = classVisitor.visitMethod(context.access, - context.name, context.desc, signature, exceptions); - if (mv == null) { - return u; - } + // 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); + } - /* - * if the returned MethodVisitor is in fact a MethodWriter, it means - * there is no method adapter between the reader and the writer. If, in - * addition, the writer's constant pool was copied from this reader - * (mw.cw.cr == this), and the signature and exceptions of the method - * have not been changed, then it is possible to skip all visit events - * and just copy the original code of the method to the writer (the - * access, name and descriptor can have been changed, this is not - * important since they are not copied as is from the reader). - */ - if (mv instanceof MethodWriter) { - MethodWriter mw = (MethodWriter) mv; - if (mw.cw.cr == this && signature == mw.signature) { - boolean sameExceptions = false; - if (exceptions == null) { - sameExceptions = mw.exceptionCount == 0; - } else if (exceptions.length == mw.exceptionCount) { - sameExceptions = true; - for (int j = exceptions.length - 1; j >= 0; --j) { - exception -= 2; - if (mw.exceptions[j] != readUnsignedShort(exception)) { - sameExceptions = false; - break; - } - } - } - if (sameExceptions) { - /* - * we do not copy directly the code into MethodWriter to - * save a byte array copy operation. The real copy will be - * done in ClassWriter.toByteArray(). - */ - mw.classReaderOffset = firstAttribute; - mw.classReaderLength = u - firstAttribute; - return u; - } - } - } + // 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); + } - // visit the method parameters - if (methodParameters != 0) { - for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { - mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); - } - } + // 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); + } - // visits the method annotations - if (dann != 0) { - AnnotationVisitor dv = mv.visitAnnotationDefault(); - readAnnotationValue(dann, c, null, dv); - if (dv != null) { - dv.visitEnd(); - } - } - if (anns != 0) { - for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - mv.visitAnnotation(readUTF8(v, c), true)); - } - } - if (ianns != 0) { - for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - mv.visitAnnotation(readUTF8(v, c), false)); - } - } - if (tanns != 0) { - for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - mv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - } - if (itanns != 0) { - for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - mv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - } - if (mpanns != 0) { - readParameterAnnotations(mv, context, mpanns, true); - } - if (impanns != 0) { - readParameterAnnotations(mv, context, impanns, false); - } + // Read the 'uses_count' and 'uses' fields. + int usesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (usesCount-- > 0) { + moduleVisitor.visitUse(readClass(currentOffset, buffer)); + currentOffset += 2; + } - // visits the method attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - mv.visitAttribute(attributes); - attributes = attr; - } + // 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); + } - // visits the method code - if (code != 0) { - mv.visitCode(); - readCode(mv, context, code); - } + // 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 <i>reverse order</i> 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; + } - // visits the end of the method - mv.visitEnd(); - - return u; - } - - /** - * Reads the bytecode of a method and makes the given visitor visit it. - * - * @param mv - * the visitor that must visit the method's code. - * @param context - * information about the class being parsed. - * @param u - * the start offset of the code attribute in the class file. - */ - private void readCode(final MethodVisitor mv, final Context context, int u) { - // reads the header - byte[] b = this.b; - char[] c = context.buffer; - int maxStack = readUnsignedShort(u); - int maxLocals = readUnsignedShort(u + 2); - int codeLength = readInt(u + 4); - u += 8; - - // reads the bytecode to find the labels - int codeStart = u; - int codeEnd = u + codeLength; - Label[] labels = context.labels = new Label[codeLength + 2]; - createLabel(codeLength + 1, labels); - while (u < codeEnd) { - int offset = u - codeStart; - int opcode = b[u] & 0xFF; - switch (ClassWriter.TYPE[opcode]) { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - u += 1; - break; - case ClassWriter.LABEL_INSN: - createLabel(offset + readShort(u + 1), labels); - u += 3; - break; - case ClassWriter.ASM_LABEL_INSN: - createLabel(offset + readUnsignedShort(u + 1), labels); - u += 3; - break; - case ClassWriter.LABELW_INSN: - case ClassWriter.ASM_LABELW_INSN: - createLabel(offset + readInt(u + 1), labels); - u += 5; - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if (opcode == Opcodes.IINC) { - u += 6; - } else { - u += 4; - } - break; - case ClassWriter.TABL_INSN: - // skips 0 to 3 padding bytes - u = u + 4 - (offset & 3); - // reads instruction - createLabel(offset + readInt(u), labels); - for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { - createLabel(offset + readInt(u + 12), labels); - u += 4; - } - u += 12; - break; - case ClassWri
<TRUNCATED>
