/*** * ASM: a very small and fast Java bytecode manipulation framework * Copyright (c) 2000-2007 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.nutz.repo.org.objectweb.asm; //import java.io.InputStream; //import java.io.IOException; /** * 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. * * @author Eric Bruneton * @author Eugene Kuleshov */ public class ClassReader { /** * True to enable signatures support. */ static final boolean SIGNATURES = true; // /** // * True to enable annotations support. // */ // static final boolean ANNOTATIONS = false; /** * True to enable stack map frames support. */ static final boolean FRAMES = true; // /** // * True to enable bytecode writing support. // */ // static final boolean WRITER = true; /** * True to enable JSR_W and GOTO_W support. */ static final boolean RESIZE = true; // /** // * 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; // /** // * 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; // // 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: // 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.CLASS: // // case ClassWriter.STR: // 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() { // int n = items[readUnsignedShort(header + 4)]; // return n == 0 ? null : readUTF8(n, 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.STR: // // case ClassWriter.CLASS: // default: // item.set(tag, readUTF8(index, buf), null, null); // break; // } // // int index2 = item.hashCode % items2.length; // item.next = items2[index2]; // items2[index2] = item; // } // // int off = items[1] - 1; // classWriter.pool.putByteArray(b, off, header - off); // classWriter.items = items2; // classWriter.threshold = (int) (0.75d * ll); // classWriter.index = ll; // } // // /** // * 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)); // } // // /** // * Constructs a new {@link ClassReader} object. // * // * @param name the fully qualified name of the class to be read. // * @throws IOException if an exception occurs during reading. // */ // public ClassReader(final String name) throws IOException { // this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/') // + ".class")); // } // // /** // * Reads the bytecode of a class. // * // * @param is an input stream from which to read the class. // * @return the bytecode read from the given input stream. // * @throws IOException if a problem occurs during reading. // */ // private static byte[] readClass(final InputStream is) throws IOException { // if (is == null) { // throw new IOException("Class not found"); // } // 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; // } // } // } // // // ------------------------------------------------------------------------ // // 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) // { // byte[] b = this.b; // the bytecode array // char[] c = new char[maxStringLength]; // buffer used to read strings // int i, j, k; // loop variables // int u, v, w; // indexes in b // Attribute attr; // // int access; // String name; // String desc; // String attrName; // String signature; // int anns = 0; // int ianns = 0; // Attribute cattrs = null; // // // visits the header // u = header; // access = readUnsignedShort(u); // name = readClass(u + 2, c); // v = items[readUnsignedShort(u + 4)]; // String superClassName = v == 0 ? null : readUTF8(v, c); // String[] implementedItfs = new String[readUnsignedShort(u + 6)]; // w = 0; // u += 8; // for (i = 0; i < implementedItfs.length; ++i) { // implementedItfs[i] = readClass(u, c); // u += 2; // } // // boolean skipCode = (flags & SKIP_CODE) != 0; // boolean skipDebug = (flags & SKIP_DEBUG) != 0; // boolean unzip = (flags & EXPAND_FRAMES) != 0; // // // skips fields and methods // v = u; // i = readUnsignedShort(v); // v += 2; // for (; i > 0; --i) { // j = readUnsignedShort(v + 6); // v += 8; // for (; j > 0; --j) { // v += 6 + readInt(v + 2); // } // } // i = readUnsignedShort(v); // v += 2; // for (; i > 0; --i) { // j = readUnsignedShort(v + 6); // v += 8; // for (; j > 0; --j) { // v += 6 + readInt(v + 2); // } // } // // reads the class's attributes // signature = null; // String sourceFile = null; // String sourceDebug = null; // String enclosingOwner = null; // String enclosingName = null; // String enclosingDesc = null; // // i = readUnsignedShort(v); // v += 2; // for (; i > 0; --i) { // attrName = readUTF8(v, c); // // tests are sorted in decreasing frequency order // // (based on frequencies observed on typical classes) // if ("SourceFile".equals(attrName)) { // sourceFile = readUTF8(v + 6, c); // } else if ("InnerClasses".equals(attrName)) { // w = v + 6; // } else if ("EnclosingMethod".equals(attrName)) { // enclosingOwner = readClass(v + 6, c); // int item = readUnsignedShort(v + 8); // if (item != 0) { // enclosingName = readUTF8(items[item], c); // enclosingDesc = readUTF8(items[item] + 2, c); // } // } else if (SIGNATURES && "Signature".equals(attrName)) { // signature = readUTF8(v + 6, c); // } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { // anns = v + 6; // } 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(v + 2); // sourceDebug = readUTF(v + 6, len, new char[len]); // } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { // ianns = v + 6; // } else { // attr = readAttribute(attrs, // attrName, // v + 6, // readInt(v + 2), // c, // -1, // null); // if (attr != null) { // attr.next = cattrs; // cattrs = attr; // } // } // v += 6 + readInt(v + 2); // } // // calls the visit method // classVisitor.visit(readInt(4), // access, // name, // signature, // superClassName, // implementedItfs); // // // calls the visitSource method // if (!skipDebug && (sourceFile != null || sourceDebug != null)) { // classVisitor.visitSource(sourceFile, sourceDebug); // } // // // calls the visitOuterClass method // if (enclosingOwner != null) { // classVisitor.visitOuterClass(enclosingOwner, // enclosingName, // enclosingDesc); // } // // // visits the class annotations // if (ANNOTATIONS) { // for (i = 1; i >= 0; --i) { // v = i == 0 ? ianns : anns; // if (v != 0) { // j = readUnsignedShort(v); // v += 2; // for (; j > 0; --j) { // v = readAnnotationValues(v + 2, // c, // true, // classVisitor.visitAnnotation(readUTF8(v, c), i != 0)); // } // } // } // } // // // visits the class attributes // while (cattrs != null) { // attr = cattrs.next; // cattrs.next = null; // classVisitor.visitAttribute(cattrs); // cattrs = attr; // } // // // calls the visitInnerClass method // if (w != 0) { // i = readUnsignedShort(w); // w += 2; // for (; i > 0; --i) { // classVisitor.visitInnerClass(readUnsignedShort(w) == 0 // ? null // : readClass(w, c), readUnsignedShort(w + 2) == 0 // ? null // : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 // ? null // : readUTF8(w + 4, c), readUnsignedShort(w + 6)); // w += 8; // } // } // // // visits the fields // i = readUnsignedShort(u); // u += 2; // for (; i > 0; --i) { // access = readUnsignedShort(u); // name = readUTF8(u + 2, c); // desc = readUTF8(u + 4, c); // // visits the field's attributes and looks for a ConstantValue // // attribute // int fieldValueItem = 0; // signature = null; // anns = 0; // ianns = 0; // cattrs = null; // // j = readUnsignedShort(u + 6); // u += 8; // for (; j > 0; --j) { // attrName = readUTF8(u, c); // // tests are sorted in decreasing frequency order // // (based on frequencies observed on typical classes) // if ("ConstantValue".equals(attrName)) { // fieldValueItem = readUnsignedShort(u + 6); // } else if (SIGNATURES && "Signature".equals(attrName)) { // signature = readUTF8(u + 6, 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 (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { // anns = u + 6; // } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { // ianns = u + 6; // } else { // attr = readAttribute(attrs, // attrName, // u + 6, // readInt(u + 2), // c, // -1, // null); // if (attr != null) { // attr.next = cattrs; // cattrs = attr; // } // } // u += 6 + readInt(u + 2); // } // // visits the field // FieldVisitor fv = classVisitor.visitField(access, // name, // desc, // signature, // fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); // // visits the field annotations and attributes // if (fv != null) { // if (ANNOTATIONS) { // for (j = 1; j >= 0; --j) { // v = j == 0 ? ianns : anns; // if (v != 0) { // k = readUnsignedShort(v); // v += 2; // for (; k > 0; --k) { // v = readAnnotationValues(v + 2, // c, // true, // fv.visitAnnotation(readUTF8(v, c), j != 0)); // } // } // } // } // while (cattrs != null) { // attr = cattrs.next; // cattrs.next = null; // fv.visitAttribute(cattrs); // cattrs = attr; // } // fv.visitEnd(); // } // } // // // visits the methods // i = readUnsignedShort(u); // u += 2; // for (; i > 0; --i) { // int u0 = u + 6; // access = readUnsignedShort(u); // name = readUTF8(u + 2, c); // desc = readUTF8(u + 4, c); // signature = null; // anns = 0; // ianns = 0; // int dann = 0; // int mpanns = 0; // int impanns = 0; // cattrs = null; // v = 0; // w = 0; // // // looks for Code and Exceptions attributes // j = readUnsignedShort(u + 6); // u += 8; // for (; j > 0; --j) { // attrName = readUTF8(u, c); // int attrSize = readInt(u + 2); // u += 6; // // tests are sorted in decreasing frequency order // // (based on frequencies observed on typical classes) // if ("Code".equals(attrName)) { // if (!skipCode) { // v = u; // } // } else if ("Exceptions".equals(attrName)) { // w = u; // } else if (SIGNATURES && "Signature".equals(attrName)) { // signature = readUTF8(u, c); // } else if ("Deprecated".equals(attrName)) { // access |= Opcodes.ACC_DEPRECATED; // } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { // anns = u; // } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { // dann = u; // } else if ("Synthetic".equals(attrName)) { // access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; // } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { // ianns = u; // } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName)) // { // mpanns = u; // } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName)) // { // impanns = u; // } else { // attr = readAttribute(attrs, // attrName, // u, // attrSize, // c, // -1, // null); // if (attr != null) { // attr.next = cattrs; // cattrs = attr; // } // } // u += attrSize; // } // // reads declared exceptions // String[] exceptions; // if (w == 0) { // exceptions = null; // } else { // exceptions = new String[readUnsignedShort(w)]; // w += 2; // for (j = 0; j < exceptions.length; ++j) { // exceptions[j] = readClass(w, c); // w += 2; // } // } // // // visits the method's code, if any // MethodVisitor mv = classVisitor.visitMethod(access, // name, // desc, // signature, // exceptions); // // if (mv != null) { // /* // * 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 (WRITER && mv instanceof MethodWriter) { // MethodWriter mw = (MethodWriter) mv; // if (mw.cw.cr == this) { // if (signature == mw.signature) { // boolean sameExceptions = false; // if (exceptions == null) { // sameExceptions = mw.exceptionCount == 0; // } else { // if (exceptions.length == mw.exceptionCount) { // sameExceptions = true; // for (j = exceptions.length - 1; j >= 0; --j) // { // w -= 2; // if (mw.exceptions[j] != readUnsignedShort(w)) // { // 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 = u0; // mw.classReaderLength = u - u0; // continue; // } // } // } // } // // if (ANNOTATIONS && dann != 0) { // AnnotationVisitor dv = mv.visitAnnotationDefault(); // readAnnotationValue(dann, c, null, dv); // if (dv != null) { // dv.visitEnd(); // } // } // if (ANNOTATIONS) { // for (j = 1; j >= 0; --j) { // w = j == 0 ? ianns : anns; // if (w != 0) { // k = readUnsignedShort(w); // w += 2; // for (; k > 0; --k) { // w = readAnnotationValues(w + 2, // c, // true, // mv.visitAnnotation(readUTF8(w, c), j != 0)); // } // } // } // } // if (ANNOTATIONS && mpanns != 0) { // readParameterAnnotations(mpanns, desc, c, true, mv); // } // if (ANNOTATIONS && impanns != 0) { // readParameterAnnotations(impanns, desc, c, false, mv); // } // while (cattrs != null) { // attr = cattrs.next; // cattrs.next = null; // mv.visitAttribute(cattrs); // cattrs = attr; // } // } // // if (mv != null && v != 0) { // int maxStack = readUnsignedShort(v); // int maxLocals = readUnsignedShort(v + 2); // int codeLength = readInt(v + 4); // v += 8; // // int codeStart = v; // int codeEnd = v + codeLength; // // mv.visitCode(); // // // 1st phase: finds the labels // int label; // Label[] labels = new Label[codeLength + 2]; // readLabel(codeLength + 1, labels); // while (v < codeEnd) { // w = v - codeStart; // int opcode = b[v] & 0xFF; // switch (ClassWriter.TYPE[opcode]) { // case ClassWriter.NOARG_INSN: // case ClassWriter.IMPLVAR_INSN: // v += 1; // break; // case ClassWriter.LABEL_INSN: // readLabel(w + readShort(v + 1), labels); // v += 3; // break; // case ClassWriter.LABELW_INSN: // readLabel(w + readInt(v + 1), labels); // v += 5; // break; // case ClassWriter.WIDE_INSN: // opcode = b[v + 1] & 0xFF; // if (opcode == Opcodes.IINC) { // v += 6; // } else { // v += 4; // } // break; // case ClassWriter.TABL_INSN: // // skips 0 to 3 padding bytes* // v = v + 4 - (w & 3); // // reads instruction // readLabel(w + readInt(v), labels); // j = readInt(v + 8) - readInt(v + 4) + 1; // v += 12; // for (; j > 0; --j) { // readLabel(w + readInt(v), labels); // v += 4; // } // break; // case ClassWriter.LOOK_INSN: // // skips 0 to 3 padding bytes* // v = v + 4 - (w & 3); // // reads instruction // readLabel(w + readInt(v), labels); // j = readInt(v + 4); // v += 8; // for (; j > 0; --j) { // readLabel(w + readInt(v + 4), labels); // v += 8; // } // break; // case ClassWriter.VAR_INSN: // case ClassWriter.SBYTE_INSN: // case ClassWriter.LDC_INSN: // v += 2; // break; // case ClassWriter.SHORT_INSN: // case ClassWriter.LDCW_INSN: // case ClassWriter.FIELDORMETH_INSN: // case ClassWriter.TYPE_INSN: // case ClassWriter.IINC_INSN: // v += 3; // break; // case ClassWriter.ITFDYNMETH_INSN: // v += 5; // break; // // case MANA_INSN: // default: // v += 4; // break; // } // } // // parses the try catch entries // j = readUnsignedShort(v); // v += 2; // for (; j > 0; --j) { // Label start = readLabel(readUnsignedShort(v), labels); // Label end = readLabel(readUnsignedShort(v + 2), labels); // Label handler = readLabel(readUnsignedShort(v + 4), labels); // int type = readUnsignedShort(v + 6); // if (type == 0) { // mv.visitTryCatchBlock(start, end, handler, null); // } else { // mv.visitTryCatchBlock(start, // end, // handler, // readUTF8(items[type], c)); // } // v += 8; // } // // parses the local variable, line number tables, and code // // attributes // int varTable = 0; // int varTypeTable = 0; // int stackMap = 0; // int stackMapSize = 0; // int frameCount = 0; // int frameMode = 0; // int frameOffset = 0; // int frameLocalCount = 0; // int frameLocalDiff = 0; // int frameStackCount = 0; // Object[] frameLocal = null; // Object[] frameStack = null; // boolean zip = true; // cattrs = null; // j = readUnsignedShort(v); // v += 2; // for (; j > 0; --j) { // attrName = readUTF8(v, c); // if ("LocalVariableTable".equals(attrName)) { // if (!skipDebug) { // varTable = v + 6; // k = readUnsignedShort(v + 6); // w = v + 8; // for (; k > 0; --k) { // label = readUnsignedShort(w); // if (labels[label] == null) { // readLabel(label, labels).status |= Label.DEBUG; // } // label += readUnsignedShort(w + 2); // if (labels[label] == null) { // readLabel(label, labels).status |= Label.DEBUG; // } // w += 10; // } // } // } else if ("LocalVariableTypeTable".equals(attrName)) { // varTypeTable = v + 6; // } else if ("LineNumberTable".equals(attrName)) { // if (!skipDebug) { // k = readUnsignedShort(v + 6); // w = v + 8; // for (; k > 0; --k) { // label = readUnsignedShort(w); // if (labels[label] == null) { // readLabel(label, labels).status |= Label.DEBUG; // } // labels[label].line = readUnsignedShort(w + 2); // w += 4; // } // } // } else if (FRAMES && "StackMapTable".equals(attrName)) { // if ((flags & SKIP_FRAMES) == 0) { // stackMap = v + 8; // stackMapSize = readInt(v + 2); // frameCount = readUnsignedShort(v + 6); // } // /* // * 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 in the second phase (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 insn and for // * which no Label exist. // */ // /* // * This is not true for UNINITIALIZED type offsets. We // * solve this by parsing the stack map table without a // * full decoding (see below). // */ // } else if (FRAMES && "StackMap".equals(attrName)) { // if ((flags & SKIP_FRAMES) == 0) { // stackMap = v + 8; // stackMapSize = readInt(v + 2); // frameCount = readUnsignedShort(v + 6); // zip = false; // } // /* // * IMPORTANT! here we assume that the frames are // * ordered, as in the StackMapTable attribute, although // * this is not guaranteed by the attribute format. // */ // } else { // for (k = 0; k < attrs.length; ++k) { // if (attrs[k].type.equals(attrName)) { // attr = attrs[k].read(this, // v + 6, // readInt(v + 2), // c, // codeStart - 8, // labels); // if (attr != null) { // attr.next = cattrs; // cattrs = attr; // } // } // } // } // v += 6 + readInt(v + 2); // } // // // 2nd phase: visits each instruction // if (FRAMES && stackMap != 0) { // // creates the very first (implicit) frame from the method // // descriptor // frameLocal = new Object[maxLocals]; // frameStack = new Object[maxStack]; // if (unzip) { // int local = 0; // if ((access & Opcodes.ACC_STATIC) == 0) { // if ("<init>".equals(name)) { // frameLocal[local++] = Opcodes.UNINITIALIZED_THIS; // } else { // frameLocal[local++] = readClass(header + 2, c); // } // } // j = 1; // loop: while (true) { // k = j; // switch (desc.charAt(j++)) { // case 'Z': // case 'C': // case 'B': // case 'S': // case 'I': // frameLocal[local++] = Opcodes.INTEGER; // break; // case 'F': // frameLocal[local++] = Opcodes.FLOAT; // break; // case 'J': // frameLocal[local++] = Opcodes.LONG; // break; // case 'D': // frameLocal[local++] = Opcodes.DOUBLE; // break; // case '[': // while (desc.charAt(j) == '[') { // ++j; // } // if (desc.charAt(j) == 'L') { // ++j; // while (desc.charAt(j) != ';') { // ++j; // } // } // frameLocal[local++] = desc.substring(k, ++j); // break; // case 'L': // while (desc.charAt(j) != ';') { // ++j; // } // frameLocal[local++] = desc.substring(k + 1, // j++); // break; // default: // break loop; // } // } // frameLocalCount = local; // } // /* // * for the first explicit frame the offset is not // * offset_delta + 1 but only offset_delta; setting the // * implicit frame offset to -1 allow the use of the // * "offset_delta + 1" rule in all cases // */ // frameOffset = -1; // /* // * Finds 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 8, offset within code 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 (j = stackMap; j < stackMap + stackMapSize - 2; ++j) { // if (b[j] == 8) { // UNINITIALIZED FRAME TYPE // k = readUnsignedShort(j + 1); // if (k >= 0 && k < codeLength) { // potential offset // if ((b[codeStart + k] & 0xFF) == Opcodes.NEW) { // NEW at this offset // readLabel(k, labels); // } // } // } // } // } // v = codeStart; // Label l; // while (v < codeEnd) { // w = v - codeStart; // // l = labels[w]; // if (l != null) { // mv.visitLabel(l); // if (!skipDebug && l.line > 0) { // mv.visitLineNumber(l.line, l); // } // } // // while (FRAMES && frameLocal != null // && (frameOffset == w || frameOffset == -1)) // { // // if there is a frame for this offset, // // makes the visitor visit it, // // and reads the next frame if there is one. // if (!zip || unzip) { // mv.visitFrame(Opcodes.F_NEW, // frameLocalCount, // frameLocal, // frameStackCount, // frameStack); // } else if (frameOffset != -1) { // mv.visitFrame(frameMode, // frameLocalDiff, // frameLocal, // frameStackCount, // frameStack); // } // // if (frameCount > 0) { // int tag, delta, n; // if (zip) { // tag = b[stackMap++] & 0xFF; // } else { // tag = MethodWriter.FULL_FRAME; // frameOffset = -1; // } // frameLocalDiff = 0; // if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) // { // delta = tag; // frameMode = Opcodes.F_SAME; // frameStackCount = 0; // } else if (tag < MethodWriter.RESERVED) { // delta = tag // - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; // stackMap = readFrameType(frameStack, // 0, // stackMap, // c, // labels); // frameMode = Opcodes.F_SAME1; // frameStackCount = 1; // } else { // delta = readUnsignedShort(stackMap); // stackMap += 2; // if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) // { // stackMap = readFrameType(frameStack, // 0, // stackMap, // c, // labels); // frameMode = Opcodes.F_SAME1; // frameStackCount = 1; // } else if (tag >= MethodWriter.CHOP_FRAME // && tag < MethodWriter.SAME_FRAME_EXTENDED) // { // frameMode = Opcodes.F_CHOP; // frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED // - tag; // frameLocalCount -= frameLocalDiff; // frameStackCount = 0; // } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) // { // frameMode = Opcodes.F_SAME; // frameStackCount = 0; // } else if (tag < MethodWriter.FULL_FRAME) { // j = unzip ? frameLocalCount : 0; // for (k = tag // - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--) // { // stackMap = readFrameType(frameLocal, // j++, // stackMap, // c, // labels); // } // frameMode = Opcodes.F_APPEND; // frameLocalDiff = tag // - MethodWriter.SAME_FRAME_EXTENDED; // frameLocalCount += frameLocalDiff; // frameStackCount = 0; // } else { // if (tag == FULL_FRAME) { // frameMode = Opcodes.F_FULL; // n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap); // stackMap += 2; // for (j = 0; n > 0; n--) { // stackMap = readFrameType(frameLocal, // j++, // stackMap, // c, // labels); // } // n = frameStackCount = readUnsignedShort(stackMap); // stackMap += 2; // for (j = 0; n > 0; n--) { // stackMap = readFrameType(frameStack, // j++, // stackMap, // c, // labels); // } // } // } // frameOffset += delta + 1; // readLabel(frameOffset, labels); // // --frameCount; // } else { // frameLocal = null; // } // } // // int opcode = b[v] & 0xFF; // switch (ClassWriter.TYPE[opcode]) { // case ClassWriter.NOARG_INSN: // mv.visitInsn(opcode); // v += 1; // break; // case ClassWriter.IMPLVAR_INSN: // if (opcode > Opcodes.ISTORE) { // opcode -= 59; // ISTORE_0 // mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), // opcode & 0x3); // } else { // opcode -= 26; // ILOAD_0 // mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), // opcode & 0x3); // } // v += 1; // break; // case ClassWriter.LABEL_INSN: // mv.visitJumpInsn(opcode, labels[w // + readShort(v + 1)]); // v += 3; // break; // case ClassWriter.LABELW_INSN: // mv.visitJumpInsn(opcode - 33, labels[w // + readInt(v + 1)]); // v += 5; // break; // case ClassWriter.WIDE_INSN: // opcode = b[v + 1] & 0xFF; // if (opcode == Opcodes.IINC) { // mv.visitIincInsn(readUnsignedShort(v + 2), // readShort(v + 4)); // v += 6; // } else { // mv.visitVarInsn(opcode, // readUnsignedShort(v + 2)); // v += 4; // } // break; // case ClassWriter.TABL_INSN: // // skips 0 to 3 padding bytes // v = v + 4 - (w & 3); // // reads instruction // label = w + readInt(v); // int min = readInt(v + 4); // int max = readInt(v + 8); // v += 12; // Label[] table = new Label[max - min + 1]; // for (j = 0; j < table.length; ++j) { // table[j] = labels[w + readInt(v)]; // v += 4; // } // mv.visitTableSwitchInsn(min, // max, // labels[label], // table); // break; // case ClassWriter.LOOK_INSN: // // skips 0 to 3 padding bytes // v = v + 4 - (w & 3); // // reads instruction // label = w + readInt(v); // j = readInt(v + 4); // v += 8; // int[] keys = new int[j]; // Label[] values = new Label[j]; // for (j = 0; j < keys.length; ++j) { // keys[j] = readInt(v); // values[j] = labels[w + readInt(v + 4)]; // v += 8; // } // mv.visitLookupSwitchInsn(labels[label], // keys, // values); // break; // case ClassWriter.VAR_INSN: // mv.visitVarInsn(opcode, b[v + 1] & 0xFF); // v += 2; // break; // case ClassWriter.SBYTE_INSN: // mv.visitIntInsn(opcode, b[v + 1]); // v += 2; // break; // case ClassWriter.SHORT_INSN: // mv.visitIntInsn(opcode, readShort(v + 1)); // v += 3; // break; // case ClassWriter.LDC_INSN: // mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); // v += 2; // break; // case ClassWriter.LDCW_INSN: // mv.visitLdcInsn(readConst(readUnsignedShort(v + 1), // c)); // v += 3; // break; // case ClassWriter.FIELDORMETH_INSN: // case ClassWriter.ITFDYNMETH_INSN: // int cpIndex = items[readUnsignedShort(v + 1)]; // String iowner; // // INVOKEDYNAMIC is receiverless // if (opcode == Opcodes.INVOKEDYNAMIC) { // iowner = Opcodes.INVOKEDYNAMIC_OWNER; // } else { // iowner = readClass(cpIndex, c); // cpIndex = items[readUnsignedShort(cpIndex + 2)]; // } // String iname = readUTF8(cpIndex, c); // String idesc = readUTF8(cpIndex + 2, c); // if (opcode < Opcodes.INVOKEVIRTUAL) { // mv.visitFieldInsn(opcode, iowner, iname, idesc); // } else { // mv.visitMethodInsn(opcode, iowner, iname, idesc); // } // if (opcode == Opcodes.INVOKEINTERFACE || opcode == Opcodes.INVOKEDYNAMIC) { // v += 5; // } else { // v += 3; // } // break; // case ClassWriter.TYPE_INSN: // mv.visitTypeInsn(opcode, readClass(v + 1, c)); // v += 3; // break; // case ClassWriter.IINC_INSN: // mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); // v += 3; // break; // // case MANA_INSN: // default: // mv.visitMultiANewArrayInsn(readClass(v + 1, c), // b[v + 3] & 0xFF); // v += 4; // break; // } // } // l = labels[codeEnd - codeStart]; // if (l != null) { // mv.visitLabel(l); // } // // visits the local variable tables // if (!skipDebug && varTable != 0) { // int[] typeTable = null; // if (varTypeTable != 0) { // k = readUnsignedShort(varTypeTable) * 3; // w = varTypeTable + 2; // typeTable = new int[k]; // while (k > 0) { // typeTable[--k] = w + 6; // signature // typeTable[--k] = readUnsignedShort(w + 8); // index // typeTable[--k] = readUnsignedShort(w); // start // w += 10; // } // } // k = readUnsignedShort(varTable); // w = varTable + 2; // for (; k > 0; --k) { // int start = readUnsignedShort(w); // int length = readUnsignedShort(w + 2); // int index = readUnsignedShort(w + 8); // String vsignature = null; // if (typeTable != null) { // for (int a = 0; a < typeTable.length; a += 3) { // if (typeTable[a] == start // && typeTable[a + 1] == index) // { // vsignature = readUTF8(typeTable[a + 2], c); // break; // } // } // } // mv.visitLocalVariable(readUTF8(w + 4, c), // readUTF8(w + 6, c), // vsignature, // labels[start], // labels[start + length], // index); // w += 10; // } // } // // visits the other attributes // while (cattrs != null) { // attr = cattrs.next; // cattrs.next = null; // mv.visitAttribute(cattrs); // cattrs = attr; // } // // visits the max stack and max locals values // mv.visitMaxs(maxStack, maxLocals); // } // // if (mv != null) { // mv.visitEnd(); // } // } // // // visits the end of the class // classVisitor.visitEnd(); // } // // /** // * Reads parameter annotations and makes the given visitor visit them. // * // * @param v start offset in {@link #b b} of the annotations to be read. // * @param desc the method descriptor. // * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, // * {@link #readClass(int,char[]) readClass} or // * {@link #readConst readConst}. // * @param visible <tt>true</tt> if the annotations to be read are visible // * at runtime. // * @param mv the visitor that must visit the annotations. // */ // private void readParameterAnnotations( // int v, // final String desc, // final char[] buf, // final boolean visible, // final MethodVisitor mv) // { // int i; // int n = b[v++] & 0xFF; // // workaround for a bug in javac (javac compiler generates a parameter // // annotation array whose size is equal to the number of parameters in // // the Java source file, while it should generate an array whose size is // // equal to the number of parameters in the method descriptor - which // // includes the synthetic parameters added by the compiler). This work- // // around supposes that the synthetic parameters are the first ones. // int synthetics = Type.getArgumentTypes(desc).length - n; // AnnotationVisitor av; // for (i = 0; i < synthetics; ++i) { // // virtual annotation to detect synthetic parameters in MethodWriter // av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false); // if (av != null) { // av.visitEnd(); // } // } // for (; i < n + synthetics; ++i) { // int j = readUnsignedShort(v); // v += 2; // for (; j > 0; --j) { // av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible); // v = readAnnotationValues(v + 2, buf, true, av); // } // } // } // // /** // * Reads the values of an annotation and makes the given visitor visit them. // * // * @param v the start offset in {@link #b b} of the values to be read // * (including the unsigned short that gives the number of values). // * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, // * {@link #readClass(int,char[]) readClass} or // * {@link #readConst readConst}. // * @param named if the annotation values are named or not. // * @param av the visitor that must visit the values. // * @return the end offset of the annotation values. // */ // private int readAnnotationValues( // int v, // final char[] buf, // final boolean named, // final AnnotationVisitor av) // { // int i = readUnsignedShort(v); // v += 2; // if (named) { // for (; i > 0; --i) { // v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); // } // } else { // for (; i > 0; --i) { // v = readAnnotationValue(v, buf, null, av); // } // } // if (av != null) { // av.visitEnd(); // } // return v; // } // // /** // * Reads a value of an annotation and makes the given visitor visit it. // * // * @param v the start offset in {@link #b b} of the value to be read (<i>not // * including the value name constant pool index</i>). // * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, // * {@link #readClass(int,char[]) readClass} or // * {@link #readConst readConst}. // * @param name the name of the value to be read. // * @param av the visitor that must visit the value. // * @return the end offset of the annotation value. // */ // private int readAnnotationValue( // int v, // final char[] buf, // final String name, // final AnnotationVisitor av) // { // int i; // if (av == null) { // switch (b[v] & 0xFF) { // case 'e': // enum_const_value // return v + 5; // case '@': // annotation_value // return readAnnotationValues(v + 3, buf, true, null); // case '[': // array_value // return readAnnotationValues(v + 1, buf, false, null); // default: // return v + 3; // } // } // switch (b[v++] & 0xFF) { // case 'I': // pointer to CONSTANT_Integer // case 'J': // pointer to CONSTANT_Long // case 'F': // pointer to CONSTANT_Float // case 'D': // pointer to CONSTANT_Double // av.visit(name, readConst(readUnsignedShort(v), buf)); // v += 2; // break; // case 'B': // pointer to CONSTANT_Byte // av.visit(name, // new Byte((byte) readInt(items[readUnsignedShort(v)]))); // v += 2; // break; // case 'Z': // pointer to CONSTANT_Boolean // av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 // ? Boolean.FALSE // : Boolean.TRUE); // v += 2; // break; // case 'S': // pointer to CONSTANT_Short // av.visit(name, // new Short((short) readInt(items[readUnsignedShort(v)]))); // v += 2; // break; // case 'C': // pointer to CONSTANT_Char // av.visit(name, // new Character((char) readInt(items[readUnsignedShort(v)]))); // v += 2; // break; // case 's': // pointer to CONSTANT_Utf8 // av.visit(name, readUTF8(v, buf)); // v += 2; // break; // case 'e': // enum_const_value // av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); // v += 4; // break; // case 'c': // class_info // av.visit(name, Type.getType(readUTF8(v, buf))); // v += 2; // break; // case '@': // annotation_value // v = readAnnotationValues(v + 2, // buf, // true, // av.visitAnnotation(name, readUTF8(v, buf))); // break; // case '[': // array_value // int size = readUnsignedShort(v); // v += 2; // if (size == 0) { // return readAnnotationValues(v - 2, // buf, // false, // av.visitArray(name)); // } // switch (this.b[v++] & 0xFF) { // case 'B': // byte[] bv = new byte[size]; // for (i = 0; i < size; i++) { // bv[i] = (byte) readInt(items[readUnsignedShort(v)]); // v += 3; // } // av.visit(name, bv); // --v; // break; // case 'Z': // boolean[] zv = new boolean[size]; // for (i = 0; i < size; i++) { // zv[i] = readInt(items[readUnsignedShort(v)]) != 0; // v += 3; // } // av.visit(name, zv); // --v; // break; // case 'S': // short[] sv = new short[size]; // for (i = 0; i < size; i++) { // sv[i] = (short) readInt(items[readUnsignedShort(v)]); // v += 3; // } // av.visit(name, sv); // --v; // break; // case 'C': // char[] cv = new char[size]; // for (i = 0; i < size; i++) { // cv[i] = (char) readInt(items[readUnsignedShort(v)]); // v += 3; // } // av.visit(name, cv); // --v; // break; // case 'I': // int[] iv = new int[size]; // for (i = 0; i < size; i++) { // iv[i] = readInt(items[readUnsignedShort(v)]); // v += 3; // } // av.visit(name, iv); // --v; // break; // case 'J': // long[] lv = new long[size]; // for (i = 0; i < size; i++) { // lv[i] = readLong(items[readUnsignedShort(v)]); // v += 3; // } // av.visit(name, lv); // --v; // break; // case 'F': // float[] fv = new float[size]; // for (i = 0; i < size; i++) { // fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)])); // v += 3; // } // av.visit(name, fv); // --v; // break; // case 'D': // double[] dv = new double[size]; // for (i = 0; i < size; i++) { // dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)])); // v += 3; // } // av.visit(name, dv); // --v; // break; // default: // v = readAnnotationValues(v - 3, // buf, // false, // av.visitArray(name)); // } // } // return v; // } // // private int readFrameType( // final Object[] frame, // final int index, // int v, // final char[] buf, // final Label[] labels) // { // int type = b[v++] & 0xFF; // switch (type) { // case 0: // frame[index] = Opcodes.TOP; // break; // case 1: // frame[index] = Opcodes.INTEGER; // break; // case 2: // frame[index] = Opcodes.FLOAT; // break; // case 3: // frame[index] = Opcodes.DOUBLE; // break; // case 4: // frame[index] = Opcodes.LONG; // break; // case 5: // frame[index] = Opcodes.NULL; // break; // case 6: // frame[index] = Opcodes.UNINITIALIZED_THIS; // break; // case 7: // Object // frame[index] = readClass(v, buf); // v += 2; // break; // default: // Uninitialized // frame[index] = readLabel(readUnsignedShort(v), labels); // v += 2; // } // return v; // } // // /** // * Returns the label corresponding to the given offset. The default // * implementation of this method creates a label for the given offset if it // * has not been already created. // * // * @param offset a bytecode offset in a method. // * @param labels the already created labels, indexed by their offset. If a // * label already exists for offset 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[offset]. // */ // protected Label readLabel(int offset, Label[] labels) { // if (labels[offset] == null) { // labels[offset] = new Label(); // } // return labels[offset]; // } // // /** // * Reads an attribute in {@link #b b}. // * // * @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 is ignored (i.e. an empty // * {@link Attribute} instance is returned). // * @param type the type of the attribute. // * @param off index of the first byte of the attribute's content in // * {@link #b b}. The 6 attribute header bytes, containing the type // * and the length of the attribute, are not taken into account here // * (they have already been read). // * @param len the length of the attribute's content. // * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, // * {@link #readClass(int,char[]) readClass} or // * {@link #readConst readConst}. // * @param codeOff index of the first byte of code's attribute content in // * {@link #b b}, or -1 if the attribute to be read is not a code // * attribute. The 6 attribute header bytes, containing the type and // * the length of the attribute, are not taken into account here. // * @param labels the labels of the method's code, or <tt>null</tt> if the // * attribute to be read is not a code attribute. // * @return the attribute that has been read, or <tt>null</tt> to skip this // * attribute. // */ // private Attribute readAttribute( // final Attribute[] attrs, // final String type, // final int off, // final int len, // final char[] buf, // final int codeOff, // final Label[] labels) // { // for (int i = 0; i < attrs.length; ++i) { // if (attrs[i].type.equals(type)) { // return attrs[i].read(this, off, len, buf, codeOff, labels); // } // } // return new Attribute(type).read(this, off, len, null, -1, null); // } // // // ------------------------------------------------------------------------ // // Utility methods: low level parsing // // ------------------------------------------------------------------------ // // /** // * Returns the start index of the constant pool item in {@link #b b}, plus // * one. <i>This method is intended for {@link Attribute} sub classes, and is // * normally not needed by class generators or adapters.</i> // * // * @param item the index a constant pool item. // * @return the start index of the constant pool item in {@link #b b}, plus // * one. // */ // public int getItem(final int item) { // return items[item]; // } // // /** // * Reads a byte value in {@link #b b}. <i>This method is intended for // * {@link Attribute} sub classes, and is normally not needed by class // * generators or adapters.</i> // * // * @param index the start index of the value to be read in {@link #b b}. // * @return the read value. // */ // public int readByte(final int index) { // return b[index] & 0xFF; // } // // /** // * Reads an unsigned short value in {@link #b b}. <i>This method is // * intended for {@link Attribute} sub classes, and is normally not needed by // * class generators or adapters.</i> // * // * @param index the start index of the value to be read in {@link #b b}. // * @return the read value. // */ // public int readUnsignedShort(final int index) { // byte[] b = this.b; // return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); // } // // /** // * Reads a signed short value in {@link #b b}. <i>This method is intended // * for {@link Attribute} sub classes, and is normally not needed by class // * generators or adapters.</i> // * // * @param index the start index of the value to be read in {@link #b b}. // * @return the read value. // */ // public short readShort(final int index) { // byte[] b = this.b; // return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); // } // // /** // * Reads a signed int value in {@link #b b}. <i>This method is intended for // * {@link Attribute} sub classes, and is normally not needed by class // * generators or adapters.</i> // * // * @param index the start index of the value to be read in {@link #b b}. // * @return the read value. // */ // public int readInt(final int index) { // byte[] b = this.b; // return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) // | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); // } // // /** // * Reads a signed long value in {@link #b b}. <i>This method is intended // * for {@link Attribute} sub classes, and is normally not needed by class // * generators or adapters.</i> // * // * @param index the start index of the value to be read in {@link #b b}. // * @return the read value. // */ // public long readLong(final int index) { // long l1 = readInt(index); // long l0 = readInt(index + 4) & 0xFFFFFFFFL; // return (l1 << 32) | l0; // } // // /** // * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method // * is intended for {@link Attribute} sub classes, and is normally not needed // * by class generators or adapters.</i> // * // * @param index the start index of an unsigned short value in {@link #b b}, // * whose value is the index of an UTF8 constant pool item. // * @param buf 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 UTF8 item. // */ // public String readUTF8(int index, final char[] buf) { // int item = readUnsignedShort(index); // String s = strings[item]; // if (s != null) { // return s; // } // index = items[item]; // return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); // } // // /** // * Reads UTF8 string in {@link #b b}. // * // * @param index start offset of the UTF8 string to be read. // * @param utfLen length of the UTF8 string to be read. // * @param buf 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(int index, final int utfLen, final char[] buf) { // int endIndex = index + utfLen; // byte[] b = this.b; // int strLen = 0; // int c; // int st = 0; // char cc = 0; // while (index < endIndex) { // c = b[index++]; // switch (st) { // case 0: // c = c & 0xFF; // if (c < 0x80) { // 0xxxxxxx // buf[strLen++] = (char) c; // } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx // cc = (char) (c & 0x1F); // st = 1; // } else { // 1110 xxxx 10xx xxxx 10xx xxxx // cc = (char) (c & 0x0F); // st = 2; // } // break; // // case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char // buf[strLen++] = (char) ((cc << 6) | (c & 0x3F)); // st = 0; // break; // // case 2: // byte 2 of 3-byte char // cc = (char) ((cc << 6) | (c & 0x3F)); // st = 1; // break; // } // } // return new String(buf, 0, strLen); // } // // /** // * Reads a class constant pool item in {@link #b b}. <i>This method is // * intended for {@link Attribute} sub classes, and is normally not needed by // * class generators or adapters.</i> // * // * @param index the start index of an unsigned short value in {@link #b b}, // * whose value is the index of a class constant pool item. // * @param buf 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 class item. // */ // public String readClass(final int index, final char[] buf) { // // computes the start index of the CONSTANT_Class item in b // // and reads the CONSTANT_Utf8 item designated by // // the first two bytes of this CONSTANT_Class item // return readUTF8(items[readUnsignedShort(index)], buf); // } // // /** // * Reads a numeric or string constant pool item in {@link #b b}. <i>This // * method is intended for {@link Attribute} sub classes, and is normally not // * needed by class generators or adapters.</i> // * // * @param item the index of a constant pool item. // * @param buf buffer to be used to read the item. This buffer must be // * sufficiently large. It is not automatically resized. // * @return the {@link Integer}, {@link Float}, {@link Long}, // * {@link Double}, {@link String} or {@link Type} corresponding to // * the given constant pool item. // */ // public Object readConst(final int item, final char[] buf) { // int index = items[item]; // switch (b[index - 1]) { // case ClassWriter.INT: // return new Integer(readInt(index)); // case ClassWriter.FLOAT: // return new Float(Float.intBitsToFloat(readInt(index))); // case ClassWriter.LONG: // return new Long(readLong(index)); // case ClassWriter.DOUBLE: // return new Double(Double.longBitsToDouble(readLong(index))); // case ClassWriter.CLASS: // return Type.getObjectType(readUTF8(index, buf)); // // case ClassWriter.STR: // default: // return readUTF8(index, buf); // } // } }