/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2005 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.objectweb.asm;
import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* 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>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 performance (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;
/**
* The start index of each bootstrap method.
*/
int[] bootstrapMethods;
// ------------------------------------------------------------------------
// 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 ll = items.length;
strings = new String[ll];
int max = 0;
int index = off + 10;
for (int i = 1; i < ll; ++i) {
items[i] = index + 1;
int tag = b[index];
int size;
switch (tag) {
case ClassWriter.FIELD:
case ClassWriter.METH:
case ClassWriter.IMETH:
case ClassWriter.INT:
case ClassWriter.FLOAT:
case ClassWriter.NAME_TYPE:
case ClassWriter.INDY:
size = 5;
break;
case ClassWriter.LONG:
case ClassWriter.DOUBLE:
size = 9;
++i;
break;
case ClassWriter.UTF8:
size = 3 + readUnsignedShort(index + 1);
if (size > max) {
max = size;
}
break;
case ClassWriter.HANDLE:
size = 4;
break;
// case ClassWriter.CLASS:
// case ClassWriter.STR:
// case ClassWriter.MTYPE
default:
size = 3;
break;
}
index += size;
}
maxStringLength = max;
// the class header information starts just after the constant pool
header = index;
}
/**
* Copies the constant pool data into the given {@link ClassWriter}. Should
* be called before the {@link #accept(ClassVisitor,boolean)} method.
*
* @param classWriter the {@link ClassWriter} to copy constant pool into.
*/
void copyPool(final ClassWriter classWriter) {
char[] buf = new char[maxStringLength];
int ll = items.length;
Item[] items2 = new Item[ll];
for (int i = 1; i < ll; i++) {
int index = items[i];
int tag = b[index - 1];
Item item = new Item(i);
int nameType;
switch (tag) {
case ClassWriter.FIELD:
case ClassWriter.METH:
case ClassWriter.IMETH:
nameType = items[readUnsignedShort(index + 2)];
item.set(tag,
readClass(index, buf),
readUTF8(nameType, buf),
readUTF8(nameType + 2, buf));
break;
case ClassWriter.INT:
item.set(readInt(index));
break;
case ClassWriter.FLOAT:
item.set(Float.intBitsToFloat(readInt(index)));
break;
case ClassWriter.NAME_TYPE:
item.set(tag,
readUTF8(index, buf),
readUTF8(index + 2, buf),
null);
break;
case ClassWriter.LONG:
item.set(readLong(index));
++i;
break;
case ClassWriter.DOUBLE:
item.set(Double.longBitsToDouble(readLong(index)));
++i;
break;
case ClassWriter.UTF8: {
String s = strings[i];
if (s == null) {
index = items[i];
s = strings[i] = readUTF(index + 2,
readUnsignedShort(index),
buf);
}
item.set(tag, s, null, null);
break;
}
case ClassWriter.HANDLE: {
int fieldOrMethodRef = items[readUnsignedShort(index + 1)];
nameType = items[readUnsignedShort(fieldOrMethodRef + 2)];
item.set(ClassWriter.HANDLE_BASE + readByte(index),
readClass(fieldOrMethodRef, buf),
readUTF8(nameType, buf),
readUTF8(nameType + 2, buf));
break;
}
case ClassWriter.INDY:
if (classWriter.bootstrapMethods == null) {
copyBootstrapMethods(classWriter, items2, buf);
}
nameType = items[readUnsignedShort(index + 2)];
item.set(readUTF8(nameType, buf),
readUTF8(nameType + 2, buf),
readUnsignedShort(index));
break;
// case ClassWriter.STR:
// case ClassWriter.CLASS:
// case ClassWriter.MTYPE
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;
}
/**
* Copies the bootstrap method data into the given {@link ClassWriter}.
* Should be called before the {@link #accept(ClassVisitor,int)} method.
*
* @param classWriter
* the {@link ClassWriter} to copy bootstrap methods into.
*/
private void copyBootstrapMethods(final ClassWriter classWriter,
final Item[] items, final char[] c) {
// finds the "BootstrapMethods" attribute
int u = getAttributes();
boolean found = false;
for (int i = readUnsignedShort(u); i > 0; --i) {
String attrName = readUTF8(u + 2, c);
if ("BootstrapMethods".equals(attrName)) {
found = true;
break;
}
u += 6 + readInt(u + 4);
}
if (!found) {
return;
}
// copies the bootstrap methods in the class writer
int boostrapMethodCount = readUnsignedShort(u + 8);
for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) {
int position = v - u - 10;
int hashCode = readConst(readUnsignedShort(v), c).hashCode();
for (int k = readUnsignedShort(v + 2); k > 0; --k) {
hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode();
v += 2;
}
v += 4;
Item item = new Item(j);
item.set(position, hashCode & 0x7FFFFFFF);
int index = item.hashCode % items.length;
item.next = items[index];
items[index] = item;
}
int attrSize = readInt(u + 4);
ByteVector bootstrapMethods = new ByteVector(attrSize + 62);
bootstrapMethods.putByteArray(b, u + 10, attrSize - 2);
classWriter.bootstrapMethodsCount = boostrapMethodCount;
classWriter.bootstrapMethods = bootstrapMethods;
}
/**
* Constructs a new {@link ClassReader} object.
*
* @param is an input stream from which to read the class.
* @throws IOException if a problem occurs during reading.
*/
public ClassReader(final InputStream is) throws IOException {
this(readClass(is));
}
/**
* 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 skipDebug <tt>true</tt> if the debug information of the class
* must not be visited. In this case the
* {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
* {@link MethodVisitor#visitLineNumber visitLineNumber} methods will
* not be called.
*/
public void accept(final ClassVisitor classVisitor, final boolean skipDebug)
{
accept(classVisitor, new Attribute[0], skipDebug);
}
/**
* 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 be ignored.
* @param skipDebug <tt>true</tt> if the debug information of the class
* must not be visited. In this case the
* {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
* {@link MethodVisitor#visitLineNumber visitLineNumber} methods will
* not be called.
*/
public void accept(
final ClassVisitor classVisitor,
final Attribute[] attrs,
final boolean skipDebug)
{
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;
int xanns = 0;
int ixanns = 0;
Attribute cattrs = null;
// visits the header
u = header; // u = u2 access_flags
access = readUnsignedShort(u);
name = readClass(u + 2, c); // name = u2 this_class
v = items[readUnsignedShort(u + 4)]; // u + 4 = u2 super_class
String superClassName = v == 0 ? null : readUTF8(v, c);
String[] implementedItfs = new String[readUnsignedShort(u + 6)];
// u + 6 = u2 interfaces_count;
w = 0;
u += 8; // u + 8 = interfaces[interfaces_count]
for (i = 0; i < implementedItfs.length; ++i) {
implementedItfs[i] = readClass(u, c);
u += 2;
}
// u = u2 fields_count
// skips fields and methods
v = u;
i = readUnsignedShort(v); // i = u2 fields_count
v += 2; // v = field_info fields[fields_count]
for (; i > 0; --i) {
j = readUnsignedShort(v + 6);
v += 8;
for (; j > 0; --j) {
v += 6 + readInt(v + 2);
}
}
i = readUnsignedShort(v); // i = u2 methods_count
v += 2; // v = method_info methods[methods_count]
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); // i = u2 attributes_count
v += 2; // v = attribute_info attributes[attributes_count]
for (; i > 0; --i) {
attrName = readUTF8(v, c);
if (attrName.equals("SourceFile")) {
sourceFile = readUTF8(v + 6, c);
} else if (attrName.equals("Deprecated")) {
access |= Opcodes.ACC_DEPRECATED;
} else if (attrName.equals("Synthetic")) {
access |= Opcodes.ACC_SYNTHETIC;
} else if (attrName.equals("Annotation")) {
access |= Opcodes.ACC_ANNOTATION;
} else if (attrName.equals("Enum")) {
access |= Opcodes.ACC_ENUM;
} else if (attrName.equals("InnerClasses")) {
w = v + 6;
} else if (attrName.equals("Signature")) {
signature = readUTF8(v + 6, c);
} else if (attrName.equals("SourceDebugExtension")) {
int len = readInt(v + 2);
sourceDebug = readUTF(v + 6, len, new char[len]);
} else if (attrName.equals("EnclosingMethod")) {
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 (attrName.equals("RuntimeVisibleAnnotations")) {
anns = v + 6;
} else if (attrName.equals("RuntimeInvisibleAnnotations")) {
ianns = v + 6;
} else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
xanns = v + 6;
} else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
ixanns = v + 6;
} else if (attrName.equals("BootstrapMethods")) {
bootstrapMethods = new int[readUnsignedShort(v + 6)];
for (j = 0, u = v + 8; j < bootstrapMethods.length; j++) {
bootstrapMethods[j] = u;
u += 2 + readUnsignedShort(u + 2) << 1;
}
} 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 (sourceFile != null || sourceDebug != null) {
classVisitor.visitSource(sourceFile, sourceDebug);
}
// calls the visitOuterClass method
if (enclosingOwner != null) {
classVisitor.visitOuterClass(enclosingOwner,
enclosingName,
enclosingDesc);
}
// visits the class annotations
for (i = 1; i >= 0; --i) {
v = i == 0 ? ianns : anns;
if (v != 0) {
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
desc = readUTF8(v, c);
v += 2;
v = readAnnotationValues(v,
c,
classVisitor.visitAnnotation(desc, i != 0));
}
}
}
// TODO
// visits the class extended annotations
for (i = 1; i >= 0; --i) {
v = i == 0 ? ixanns : xanns;
if (v != 0) {
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
v = readTypeAnnotationValues(v,
c, classVisitor, i != 0);
}
}
}
// visits the class attributes
while (cattrs != null) {
attr = cattrs.next;
cattrs.next = null;
classVisitor.visitAttribute(cattrs);
cattrs = attr;
}
// class 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
u = header + 8 + 2 * implementedItfs.length;
i = readUnsignedShort(u); // i = u2 fields_count
u += 2; // u = field_info[fields_count]
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;
xanns = 0;
ixanns = 0;
cattrs = null;
j = readUnsignedShort(u + 6); // j = u2 attributes_count
u += 8; // u = attributes[attributes_count]
for (; j > 0; --j) {
attrName = readUTF8(u, c);
if (attrName.equals("ConstantValue")) {
fieldValueItem = readUnsignedShort(u + 6);
} else if (attrName.equals("Synthetic")) {
access |= Opcodes.ACC_SYNTHETIC;
} else if (attrName.equals("Deprecated")) {
access |= Opcodes.ACC_DEPRECATED;
} else if (attrName.equals("Enum")) {
access |= Opcodes.ACC_ENUM;
} else if (attrName.equals("Signature")) {
signature = readUTF8(u + 6, c);
} else if (attrName.equals("RuntimeVisibleAnnotations")) {
anns = u + 6;
} else if (attrName.equals("RuntimeInvisibleAnnotations")) {
ianns = u + 6;
} else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
xanns = u + 6;
} else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
ixanns = 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);
}
// reads the field's value, if any
Object value = (fieldValueItem == 0
? null
: readConst(fieldValueItem, c));
// visits the field
FieldVisitor fv = classVisitor.visitField(access,
name,
desc,
signature,
value);
// visits the field annotations and attributes
if (fv != null) {
for (j = 1; j >= 0; --j) {
v = j == 0 ? ianns : anns;
if (v != 0) {
k = readUnsignedShort(v);
v += 2;
for (; k > 0; --k) {
desc = readUTF8(v, c);
v += 2;
v = readAnnotationValues(v,
c,
fv.visitAnnotation(desc, j != 0));
}
}
}
//TODO
// now visit the extended annotations
if(xanns != 0) {
v = xanns;
k = readUnsignedShort(v);
v += 2;
for(; k > 0; --k) {
v = readTypeAnnotationValues(v,
c, fv, true);
}
}
if(ixanns != 0) {
v = ixanns;
k = readUnsignedShort(v);
v += 2;
for(; k > 0; --k) {
v = readTypeAnnotationValues(v,
c, fv, false);
}
}
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;
//jaime
xanns = 0;
ixanns = 0;
// end jaime
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);
u += 2;
int attrSize = readInt(u);
u += 4;
if (attrName.equals("Code")) {
v = u;
} else if (attrName.equals("Exceptions")) {
w = u;
} else if (attrName.equals("Synthetic")) {
access |= Opcodes.ACC_SYNTHETIC;
} else if (attrName.equals("Varargs")) {
access |= Opcodes.ACC_VARARGS;
} else if (attrName.equals("Bridge")) {
access |= Opcodes.ACC_BRIDGE;
} else if (attrName.equals("Deprecated")) {
access |= Opcodes.ACC_DEPRECATED;
} else if (attrName.equals("Signature")) {
signature = readUTF8(u, c);
} else if (attrName.equals("AnnotationDefault")) {
dann = u;
} else if (attrName.equals("RuntimeVisibleAnnotations")) {
anns = u;
} else if (attrName.equals("RuntimeInvisibleAnnotations")) {
ianns = u;
} else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
xanns = u;
} else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
ixanns = u;
} else if (attrName.equals("RuntimeVisibleParameterAnnotations")) {
mpanns = u;
} else if (attrName.equals("RuntimeInvisibleParameterAnnotations")) {
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 (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 (dann != 0) {
AnnotationVisitor dv = mv.visitAnnotationDefault();
readAnnotationValue(dann, c, null, dv);
dv.visitEnd();
}
for (j = 1; j >= 0; --j) {
w = j == 0 ? ianns : anns;
if (w != 0) {
k = readUnsignedShort(w);
w += 2;
for (; k > 0; --k) {
desc = readUTF8(w, c);
w += 2;
w = readAnnotationValues(w,
c,
mv.visitAnnotation(desc, j != 0));
}
}
}
// now visit the method extended annotations
for (j = 1; j >= 0; --j) {
w = j == 0 ? ixanns : xanns;
if (w != 0) {
k = readUnsignedShort(w);
w += 2;
for (; k > 0; --k) {
w = readTypeAnnotationValues(w,
c, mv, j != 0);
}
}
}
if (mpanns != 0) {
readParameterAnnotations(mpanns, c, true, mv);
}
if (impanns != 0) {
readParameterAnnotations(impanns, 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 + 1];
while (v < codeEnd) {
int opcode = b[v] & 0xFF;
switch (ClassWriter.TYPE[opcode]) {
case ClassWriter.NOARG_INSN:
case ClassWriter.IMPLVAR_INSN:
v += 1;
break;
case ClassWriter.LABEL_INSN:
label = v - codeStart + readShort(v + 1);
if (labels[label] == null) {
labels[label] = new Label();
}
v += 3;
break;
case ClassWriter.LABELW_INSN:
label = v - codeStart + readInt(v + 1);
if (labels[label] == null) {
labels[label] = new Label();
}
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
w = v - codeStart;
v = v + 4 - (w & 3);
// reads instruction
label = w + readInt(v);
v += 4;
if (labels[label] == null) {
labels[label] = new Label();
}
j = readInt(v);
v += 4;
j = readInt(v) - j + 1;
v += 4;
for (; j > 0; --j) {
label = w + readInt(v);
v += 4;
if (labels[label] == null) {
labels[label] = new Label();
}
}
break;
case ClassWriter.LOOK_INSN:
// skips 0 to 3 padding bytes
w = v - codeStart;
v = v + 4 - (w & 3);
// reads instruction
label = w + readInt(v);
v += 4;
if (labels[label] == null) {
labels[label] = new Label();
}
j = readInt(v);
v += 4;
for (; j > 0; --j) {
v += 4; // skips key
label = w + readInt(v);
v += 4;
if (labels[label] == null) {
labels[label] = new Label();
}
}
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.ITFMETH_INSN:
case ClassWriter.INDY:
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 = readUnsignedShort(v);
Label start = labels[label];
if (start == null) {
labels[label] = start = new Label();
}
label = readUnsignedShort(v + 2);
Label end = labels[label];
if (end == null) {
labels[label] = end = new Label();
}
label = readUnsignedShort(v + 4);
Label handler = labels[label];
if (handler == null) {
labels[label] = handler = new Label();
}
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;
cattrs = null;
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
attrName = readUTF8(v, c);
if (attrName.equals("LocalVariableTable")) {
if (!skipDebug) {
varTable = v + 6;
k = readUnsignedShort(v + 6);
w = v + 8;
for (; k > 0; --k) {
label = readUnsignedShort(w);
if (labels[label] == null) {
labels[label] = new Label();
}
label += readUnsignedShort(w + 2);
if (labels[label] == null) {
labels[label] = new Label();
}
w += 10;
}
}
} else if (attrName.equals("LocalVariableTypeTable")) {
varTypeTable = v + 6;
} else if (attrName.equals("LineNumberTable")) {
if (!skipDebug) {
k = readUnsignedShort(v + 6);
w = v + 8;
for (; k > 0; --k) {
label = readUnsignedShort(w);
if (labels[label] == null) {
labels[label] = new Label();
}
labels[label].line = readUnsignedShort(w + 2);
w += 4;
}
}
} else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
k = readUnsignedShort(v + 6);
w = v + 8;
for (; k > 0; --k) {
w = readTypeAnnotationValues(w,
c, mv, false);
}
} else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
k = readUnsignedShort(v + 6);
w = v + 8;
for (; k > 0; --k) {
w = readTypeAnnotationValues(w,
c, mv, true);
}
} 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
v = codeStart;
PrecompiledMethodVisitor pmv =
(mv instanceof PrecompiledMethodVisitor)
? (PrecompiledMethodVisitor) mv : null;
Label l;
while (v < codeEnd) {
w = v - codeStart;
if (pmv != null)
pmv.visitCurrentPosition(w);
l = labels[w];
if (l != null) {
mv.visitLabel(l);
if (!skipDebug && l.line > 0) {
mv.visitLineNumber(l.line, l);
}
}
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);
v += 4;
int min = readInt(v);
v += 4;
int max = readInt(v);
v += 4;
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);
v += 4;
j = readInt(v);
v += 4;
int[] keys = new int[j];
Label[] values = new Label[j];
for (j = 0; j < keys.length; ++j) {
keys[j] = readInt(v);
v += 4;
values[j] = labels[w + readInt(v)];
v += 4;
}
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.ITFMETH_INSN:
int cpIndex = items[readUnsignedShort(v + 1)];
String 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) {
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 ClassWriter.INDY:
cpIndex = items[readUnsignedShort(v + 1)];
int bsmIndex = bootstrapMethods[readUnsignedShort(cpIndex)];
Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c);
int bsmArgCount = readUnsignedShort(bsmIndex + 2);
Object[] bsmArgs = new Object[bsmArgCount];
bsmIndex += 4;
for (j = 0; j < bsmArgCount; j++) {
bsmArgs[j] = readConst(readUnsignedShort(bsmIndex), c);
bsmIndex += 2;
}
cpIndex = items[readUnsignedShort(cpIndex + 2)];
iname = readUTF8(cpIndex, c);
idesc = readUTF8(cpIndex + 2, c);
mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs);
v += 5;
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) {
if (pmv != null)
pmv.visitCurrentPosition(codeEnd - codeStart);
mv.visitLabel(l);
}
// visits the local variable tables
if (!skipDebug && varTable != 0) {
int[] typeTable = null;
if (varTypeTable != 0) {
w = varTypeTable;
k = readUnsignedShort(w) * 3;
w += 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;
}
}
w = varTable;
k = readUnsignedShort(w);
w += 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 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 char[] buf,
final boolean visible,
final MethodVisitor mv)
{
int n = b[v++] & 0xFF;
for (int i = 0; i < n; ++i) {
int j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
String desc = readUTF8(v, buf);
v += 2;
AnnotationVisitor av = mv.visitParameterAnnotation(i,
desc,
visible);
v = readAnnotationValues(v, buf, 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 av the visitor that must visit the values.
* @return the end offset of the annotations values.
*/
private int readAnnotationValues(
int v,
final char[] buf,
final AnnotationVisitor av)
{
int i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
String name = readUTF8(v, buf);
v += 2;
v = readAnnotationValue(v, buf, name, av);
}
av.visitEnd();
return v;
}
/**
* Reads the values and reference info of an extended 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 mv the visitor to generate the visitor that must visit the values.
* @param visible {@code true} if the annotation is visible at runtime.
* @return the end offset of the annotations values.
* @author jaimeq
*/
private int readTypeAnnotationValues(
int v,
final char[] buf,
final MemberVisitor mv,
final boolean visible)
{
// first handle
//
// u1 target_type
// { ...
// } reference_info
//
int target_type_value = readByte(v);
v += 1;
Integer offset = null;
Integer location_length = null;
List<TypePathEntry> locations = new ArrayList<TypePathEntry>();
Integer start_pc = null;
Integer length = null;
Integer index = null;
Integer param_index = null;
Integer bound_index = null;
Integer type_index = null;
Integer exception_index = null;
Integer table_length = null;
TargetType target_type = TargetType.fromTargetTypeValue(target_type_value);
switch(target_type) {
// type test (instanceof)
// object creation
// constructor/method reference receiver
// {
// u2 offset;
// } reference_info;
case INSTANCEOF:
case NEW:
case CONSTRUCTOR_REFERENCE:
case METHOD_REFERENCE:
offset = readUnsignedShort(v);
v += 2;
break;
// method receiver
// {
// } reference_info;
case METHOD_RECEIVER:
break;
// local variable
// u2 table_length;
// {
// u2 start_pc;
// u2 length;
// u2 index;
// } reference_info;
case LOCAL_VARIABLE:
// resource variable
case RESOURCE_VARIABLE:
table_length = readUnsignedShort(v);
v += 2;
assert table_length == 1; // FIXME
start_pc = readUnsignedShort(v);
v += 2;
length = readUnsignedShort(v);
v += 2;
index = readUnsignedShort(v);
v += 2;
break;
// method return type
// {
// } reference_info;
case METHOD_RETURN:
break;
// method parameter
// {
// u1 param;
// } reference_info;
case METHOD_FORMAL_PARAMETER:
param_index = readByte(v);
v++;
break;
// field
// {
// } reference_info;
case FIELD:
break;
// class type parameter bound
// method type parameter bound
// {
// u1 param_index;
// u1 bound_index;
// } reference_info;
case CLASS_TYPE_PARAMETER_BOUND:
case METHOD_TYPE_PARAMETER_BOUND:
param_index = readByte(v);
v++;
bound_index = readByte(v);
v++;
break;
// class extends/implements
// exception type in throws
// {
// u1 type_index;
// } reference_info;
case CLASS_EXTENDS:
type_index = readUnsignedShort(v);
if (type_index == 0xFFFF) type_index = -1;
v += 2;
break;
case THROWS:
type_index = readUnsignedShort(v);
v += 2;
break;
case EXCEPTION_PARAMETER:
exception_index = readUnsignedShort(v);
v += 2;
break;
// typecast
// type argument in constructor call
// type argument in method call
// type argument in constructor reference
// type argument in method reference
// {
// u2 offset;
// u1 type_index;
// } reference_info;
case CAST:
case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
case METHOD_INVOCATION_TYPE_ARGUMENT:
case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
case METHOD_REFERENCE_TYPE_ARGUMENT:
offset = readUnsignedShort(v);
v += 2;
type_index = readByte(v);
v++;
break;
// method type parameter
// {
// u1 param_index;
// } reference_info;
case CLASS_TYPE_PARAMETER:
case METHOD_TYPE_PARAMETER:
param_index = readByte(v);
v++;
break;
default: throw new RuntimeException(
"Unrecognized target type: " + target_type);
}
// now read in the location information
{
location_length = readByte(v);
v += 1;
for (int m = 0; m < location_length; m++) {
int loctag = readByte(v);
int locarg = readByte(v + 1);
v += TypePathEntry.bytesPerEntry;
locations.add(TypePathEntry.fromBinary(loctag, locarg));
}
}
String desc = readUTF8(v, buf);
v += 2;
TypeAnnotationVisitor xav = mv.visitTypeAnnotation(desc, visible, false);
xav.visitXTargetType(target_type_value);
if (start_pc != null) {
xav.visitXStartPc(start_pc);
}
if (length != null) {
xav.visitXLength(length);
}
if (index != null) {
xav.visitXIndex(index);
}
if (offset != null) {
xav.visitXOffset(offset);
}
if (type_index != null) {
xav.visitXTypeIndex(type_index);
}
if (param_index != null) {
xav.visitXParamIndex(param_index);
}
if (bound_index != null) {
xav.visitXBoundIndex(bound_index);
}
if (exception_index != null) {
xav.visitXExceptionIndex(exception_index);
}
if (location_length != null) {
xav.visitXLocationLength(location_length);
}
for (TypePathEntry location : locations) {
xav.visitXLocation(location);
}
// Visit the annotation name and save space for the values count.
xav.visitXNameAndArgsSize();
// then read annotation values
int i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
String name = readUTF8(v, buf);
v += 2;
// can use the same method as for declaration annotations because
// the first part of an extended annotation matches the normal
// annotations
v = readAnnotationValue(v, buf, name, xav);
}
xav.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;
switch (readByte(v++)) {
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
boolean b = readInt(items[readUnsignedShort(v)]) == 0;
av.visit(name, b ? 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
String desc = readUTF8(v, buf);
v += 2;
v = readAnnotationValues(v, buf, av.visitAnnotation(name, desc));
break;
case '[': // array_value
int size = readUnsignedShort(v);
v += 2;
if (size == 0) {
av.visitArray(name).visitEnd();
return v;
}
switch (readByte(v++)) {
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--;
AnnotationVisitor aav = av.visitArray(name);
for (i = size; i > 0; --i) {
v = readAnnotationValue(v, buf, null, aav);
}
aav.visitEnd();
}
}
return v;
}
/**
* Returns the start index of the attribute_info structure of this class.
*
* @return the start index of the attribute_info structure of this class.
*/
private int getAttributes() {
// skips the header
int u = header + 8 + readUnsignedShort(header + 6) * 2;
// skips fields and methods
for (int i = readUnsignedShort(u); i > 0; --i) {
for (int j = readUnsignedShort(u + 8); j > 0; --j) {
u += 6 + readInt(u + 12);
}
u += 8;
}
u += 2;
for (int i = readUnsignedShort(u); i > 0; --i) {
for (int j = readUnsignedShort(u + 8); j > 0; --j) {
u += 6 + readInt(u + 12);
}
u += 8;
}
// the attribute_info structure starts just after the methods
return u + 2;
}
/**
* 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, int utfLen, char[] buf) {
int endIndex = index + utfLen;
byte[] b = this.b;
int strLen = 0;
int c, d, e;
while (index < endIndex) {
c = b[index++] & 0xFF;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
buf[strLen++] = (char) c;
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
d = b[index++];
buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F));
break;
default:
// 1110 xxxx 10xx xxxx 10xx xxxx
d = b[index++];
e = b[index++];
buf[strLen++] = (char) (((c & 0x0F) << 12)
| ((d & 0x3F) << 6) | (e & 0x3F));
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:
String s = readUTF8(index, buf);
return Type.getType(s.charAt(0) == '[' ? s : "L" + s + ";");
case ClassWriter.STR:
return readUTF8(index, buf);
case ClassWriter.MTYPE:
return Type.getMethodType(readUTF8(index, buf));
default: // case ClassWriter.HANDLE_BASE + [1..9]:
int tag = readByte(index);
int[] items = this.items;
int cpIndex = items[readUnsignedShort(index + 1)];
String owner = readClass(cpIndex, buf);
cpIndex = items[readUnsignedShort(cpIndex + 2)];
String name = readUTF8(cpIndex, buf);
String desc = readUTF8(cpIndex + 2, buf);
return new Handle(tag, owner, name, desc);
}
}
}