org.jboss.reflect.util.objectweb.asm.ClassReader Maven / Gradle / Ivy
The newest version!
/***
* 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.jboss.reflect.util.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 = true;
/**
* 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
* 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;
//Extra flags for performance
public static final int SKIP_OUTER_CLASSES = 32;
public static final int SKIP_INNER_CLASSES = 64;
public static final int SKIP_UNKNOWN_ATTRIBUTES = 128;
public static final int INCLUDE_HEADER = 256;
public static final int INCLUDE_CLASS_ANNOTATIONS = 512;
public static final int INCLUDE_FIELD_OVERVIEW = 1024;
public static final int INCLUDE_FIELD_ANNOTATIONS = 2048;
public static final int INCLUDE_FIELD_UNKNOWN_ATTRIBUTES = 4096;
public static final int INCLUDE_METHOD_OVERVIEW = 8192;
public static final int INCLUDE_METHOD_ANNOTATIONS = 16384;
public static final int INCLUDE_METHOD_PARAMETER_ANNOTATIONS = 32768;
public static final int INCLUDE_METHOD_UNKNOWN_ATTRIBUTES = 65536;
public static final int INCLUDE_DEFAULT_ANNOTATION_VALUES = 131072;
/**
* The class to be parsed. The content of this array must not be
* modified. This field is intended for {@link Attribute} sub classes, and
* is normally not needed by class generators or adapters.
*/
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 null 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
* null.
*
* @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. This may
* corrupt it if this value contains references to the constant pool,
* or has syntactic or semantic links with a class element that has
* been transformed by a class adapter between the reader and the
* writer.
* @param 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)
{
char[] c = new char[maxStringLength]; // buffer used to read strings
int i, j; // loop variables
int u, v, w; // indexes in b
Attribute attr;
int access;
String name;
String attrName;
String signature;
int anns = 0;
int ianns = 0;
Attribute cattrs = null;
boolean skipDebug = (flags & SKIP_DEBUG) != 0;
boolean skipOuterClasses = (flags & SKIP_OUTER_CLASSES) != 0;
boolean skipInnerClasses = (flags & SKIP_INNER_CLASSES) != 0;
boolean includeHeader = (flags & INCLUDE_HEADER) != 0;
boolean includeClassAnnotations = (flags & INCLUDE_CLASS_ANNOTATIONS) != 0;
boolean skipUnknownAttributes = (flags & SKIP_UNKNOWN_ATTRIBUTES) != 0;
// visits the header
u = header;
access = readUnsignedShort(u);
name = readClass(u + 2, c);
v = items[readUnsignedShort(u + 4)];
String superClassName = v == 0 || includeHeader ? null : readUTF8(v, c);
String[] implementedItfs = new String[readUnsignedShort(u + 6)];
w = 0;
u += 8;
for (i = 0; i < implementedItfs.length; ++i)
{
if (includeHeader)
implementedItfs[i] = readClass(u, c);
u += 2;
}
// 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))
{
if (!skipDebug)
sourceFile = readUTF8(v + 6, c);
}
else if ("InnerClasses".equals(attrName))
{
w = v + 6;
}
else if ("EnclosingMethod".equals(attrName))
{
if (!skipOuterClasses)
{
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))
{
if (includeHeader)
signature = readUTF8(v + 6, c);
}
else if (includeClassAnnotations && "RuntimeVisibleAnnotations".equals(attrName))
{
anns = v + 6;
}
else if ("Deprecated".equals(attrName))
{
access |= Opcodes.ACC_DEPRECATED;
}
else if ("Synthetic".equals(attrName))
{
access |= Opcodes.ACC_SYNTHETIC;
}
else if ("SourceDebugExtension".equals(attrName))
{
if (!skipDebug)
{
int len = readInt(v + 2);
sourceDebug = readUTF(v + 6, len, new char[len]);
}
}
else if (includeClassAnnotations && "RuntimeInvisibleAnnotations".equals(attrName))
{
ianns = v + 6;
}
else if (!skipUnknownAttributes)
{
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
if (includeHeader)
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 && !skipOuterClasses)
{
classVisitor.visitOuterClass(enclosingOwner, enclosingName, enclosingDesc);
}
// visits the class annotations
if (includeClassAnnotations)
{
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
if (!skipUnknownAttributes)
{
while (cattrs != null)
{
attr = cattrs.next;
cattrs.next = null;
classVisitor.visitAttribute(cattrs);
cattrs = attr;
}
}
// calls the visitInnerClass method
if (w != 0 && !skipInnerClasses)
{
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)
{
u = readField(classVisitor, flags, attrs, c, u);
}
// visits the methods
i = readUnsignedShort(u);
u += 2;
for (; i > 0; --i)
{
u = readMethod(classVisitor, flags, attrs, c, u);
}
// visits the end of the class
classVisitor.visitEnd();
}
public int readMethod(final ClassVisitor classVisitor, int flags, final Attribute[] attrs, char[] c, int u)
{
int byteCodeIndex = u;
if (c == null)
c = new char[maxStringLength];
byte[] b = this.b; // the bytecode array
boolean skipCode = (flags & SKIP_CODE) != 0;
boolean unzip = (flags & EXPAND_FRAMES) != 0;
boolean skipDebug = (flags & EXPAND_FRAMES) != 0;
boolean includeMethodOverview = (flags & INCLUDE_METHOD_OVERVIEW) != 0;
boolean includeMethodAnnotations = (flags & INCLUDE_METHOD_ANNOTATIONS) != 0;
boolean includeMethodParameterAnnotations = (flags & INCLUDE_METHOD_PARAMETER_ANNOTATIONS) != 0;
boolean includeAnnotationDefaultValues = (flags & INCLUDE_DEFAULT_ANNOTATION_VALUES) != 0;
boolean includeMethodUnknownAttributes = (flags & INCLUDE_METHOD_UNKNOWN_ATTRIBUTES) != 0;
int u0 = u + 6;
int access = 0;
String name = null;
String desc = null;
if (includeMethodOverview)
{
access = readUnsignedShort(u);
name = readUTF8(u + 2, c);
desc = readUTF8(u + 4, c);
}
else if (includeMethodParameterAnnotations)
{
desc = readUTF8(u + 4, c);
}
String signature = null;
int anns = 0;
int ianns = 0;
int dann = 0;
int mpanns = 0;
int impanns = 0;
Attribute cattrs = null;
Attribute attr;
int v = 0;
int w = 0;
int k = 0;
// looks for Code and Exceptions attributes
int j = readUnsignedShort(u + 6);
u += 8;
for (; j > 0; --j)
{
String 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))
{
if (includeMethodOverview)
signature = readUTF8(u, c);
}
else if ("Deprecated".equals(attrName))
{
if (includeMethodOverview)
access |= Opcodes.ACC_DEPRECATED;
}
else if (includeMethodAnnotations && "RuntimeVisibleAnnotations".equals(attrName))
{
anns = u;
}
else if (ANNOTATIONS && "AnnotationDefault".equals(attrName))
{
dann = u;
}
else if ("Synthetic".equals(attrName))
{
if (includeMethodOverview)
access |= Opcodes.ACC_SYNTHETIC;
}
else if (includeMethodAnnotations && "RuntimeInvisibleAnnotations".equals(attrName))
{
ianns = u;
}
else if (includeMethodParameterAnnotations && "RuntimeVisibleParameterAnnotations".equals(attrName))
{
mpanns = u;
}
else if (includeMethodParameterAnnotations && "RuntimeInvisibleParameterAnnotations".equals(attrName))
{
impanns = u;
}
else
{
if (includeMethodUnknownAttributes)
{
attr = readAttribute(attrs, attrName, u, attrSize, c, -1, null);
if (attr != null)
{
attr.next = cattrs;
cattrs = attr;
}
}
}
u += attrSize;
}
// reads declared exceptions
String[] exceptions = null;
if (includeMethodOverview)
{
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, byteCodeIndex);
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;
return u;
}
}
}
}
if (includeAnnotationDefaultValues && dann != 0)
{
AnnotationVisitor dv = mv.visitAnnotationDefault();
readAnnotationValue(dann, c, null, dv);
if (dv != null)
{
dv.visitEnd();
}
}
if (includeMethodAnnotations)
{
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 (includeMethodParameterAnnotations && mpanns != 0)
{
readParameterAnnotations(mpanns, desc, c, true, mv);
}
if (includeMethodParameterAnnotations && impanns != 0)
{
readParameterAnnotations(impanns, desc, c, false, mv);
}
if (includeMethodUnknownAttributes)
{
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 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)
{
String 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;
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.
*/
// TODO true for frame offsets,
// but for UNINITIALIZED type offsets?
}
else if (FRAMES && "StackMap".equals(attrName))
{
if ((flags & SKIP_FRAMES) == 0)
{
stackMap = v + 8;
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 ("".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;
}
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();
}
return u;
}
public int readField(final ClassVisitor classVisitor, final int flags, final Attribute[] attrs, char[] c, int u)
{
int byteCodeIndex = u;
if (c == null)
c = new char[maxStringLength];
boolean includeFieldOverview = (flags & INCLUDE_FIELD_OVERVIEW) != 0;
boolean includeFieldAnnotations = (flags & INCLUDE_FIELD_ANNOTATIONS) != 0;
boolean includeFieldUnknownAttributes = (flags & INCLUDE_FIELD_UNKNOWN_ATTRIBUTES) != 0;
int j;
int k;
int v;
Attribute attr;
int access = 0;
String name = null;
String desc = null;
String attrName;
String signature;
int anns;
int ianns;
Attribute cattrs;
if (includeFieldOverview)
{
access = readUnsignedShort(u);
desc = readUTF8(u + 4, c);
name = readUTF8(u + 2, 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))
{
if (includeFieldOverview)
fieldValueItem = readUnsignedShort(u + 6);
}
else if (SIGNATURES && "Signature".equals(attrName))
{
if (includeFieldOverview)
signature = readUTF8(u + 6, c);
}
else if ("Deprecated".equals(attrName))
{
if (includeFieldOverview)
access |= Opcodes.ACC_DEPRECATED;
}
else if ("Synthetic".equals(attrName))
{
if (includeFieldOverview)
access |= Opcodes.ACC_SYNTHETIC;
}
else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName))
{
if (includeFieldAnnotations)
anns = u + 6;
}
else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName))
{
if (includeFieldAnnotations)
ianns = u + 6;
}
else
{
if (includeFieldUnknownAttributes)
{
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), byteCodeIndex);
// visits the field annotations and attributes
if (fv != null)
{
if (includeFieldAnnotations)
{
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));
}
}
}
}
if (includeFieldUnknownAttributes)
{
while (cattrs != null)
{
attr = cattrs.next;
cattrs.next = null;
fv.visitAttribute(cattrs);
cattrs = attr;
}
}
fv.visitEnd();
}
return u;
}
/**
* 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 true 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 (not
* including the value name constant pool index).
* @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 null if the
* attribute to be read is not a code attribute.
* @return the attribute that has been read, or null 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. This method is intended for {@link Attribute} sub classes, and is
* normally not needed by class generators or adapters.
*
* @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}. This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.
*
* @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}. This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.
*
* @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}. This method is intended
* for {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.
*
* @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}. This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.
*
* @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}. This method is intended
* for {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.
*
* @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}. This method
* is intended for {@link Attribute} sub classes, and is normally not needed
* by class generators or adapters.
*
* @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}. This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.
*
* @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}. This
* method is intended for {@link Attribute} sub classes, and is normally not
* needed by class generators or adapters.
*
* @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);
}
}
}