ClassReaderpublic class ClassReader extends Object A Java class parser to make a {@link ClassVisitor 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. |
Fields Summary |
---|
public final byte[] | bThe 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. | private int[] | itemsThe 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 String[] | stringsThe 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 int | maxStringLengthMaximum length of the strings contained in the constant pool of the class. | private int | headerStart index of the class header information (access, name...) in {@link #b
b}. |
Constructors Summary |
---|
public ClassReader(byte[] b)Constructs a new {@link ClassReader ClassReader} object.
this(b, 0, b.length);
| public ClassReader(byte[] b, int off, int len)Constructs a new {@link ClassReader ClassReader} object.
this.b = b;
// parses the constant pool
items = new int[readUnsignedShort(off + 8)];
strings = new String[items.length];
int max = 0;
int index = off + 10;
for (int i = 1; i < items.length; ++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:
size = 5;
break;
case ClassWriter.LONG:
case ClassWriter.DOUBLE:
size = 9;
++i;
break;
case ClassWriter.UTF8:
size = 3 + readUnsignedShort(index + 1);
max = (size > max ? size : max);
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;
| public ClassReader(InputStream is)Constructs a new {@link ClassReader ClassReader} object.
this(readClass(is));
| public ClassReader(String name)Constructs a new {@link ClassReader ClassReader} object.
this(ClassLoader.getSystemResourceAsStream(name.replace('.",'/") + ".class"));
|
Methods Summary |
---|
public void | accept(oracle.toplink.libraries.asm.ClassVisitor classVisitor, boolean skipDebug)Makes the given visitor visit the Java class of this {@link ClassReader
ClassReader}. This class is the one specified in the constructor (see
{@link #ClassReader(byte[]) ClassReader}).
accept(classVisitor, new Attribute[0], skipDebug);
| public void | accept(oracle.toplink.libraries.asm.ClassVisitor classVisitor, oracle.toplink.libraries.asm.Attribute[] attrs, boolean skipDebug)Makes the given visitor visit the Java class of this {@link ClassReader
ClassReader}. This class is the one specified in the constructor (see
{@link #ClassReader(byte[]) ClassReader}).
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;
// visits the header
u = header;
int version = readInt(4);
int access = readUnsignedShort(u);
String className = readClass(u + 2, c);
v = items[readUnsignedShort(u + 4)];
String superClassName = v == 0 ? null : readUTF8(v, c);
String[] implementedItfs = new String[readUnsignedShort(u + 6)];
String sourceFile = null;
Attribute clattrs = null;
w = 0;
u += 8;
for (i = 0; i < implementedItfs.length; ++i) {
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
i = readUnsignedShort(v); v += 2;
for ( ; i > 0; --i) {
String attrName = readUTF8(v, c);
if (attrName.equals("SourceFile")) {
sourceFile = readUTF8(v + 6, c);
} else if (attrName.equals("Deprecated")) {
access |= Constants.ACC_DEPRECATED;
} else if (attrName.equals("Synthetic")) {
access |= Constants.ACC_SYNTHETIC;
} else if (attrName.equals("InnerClasses")) {
w = v + 6;
} else {
attr = readAttribute(
attrs, attrName, v + 6, readInt(v + 2), c, -1, null);
if (attr != null) {
attr.next = clattrs;
clattrs = attr;
}
}
v += 6 + readInt(v + 2);
}
// calls the visit method
classVisitor.visit(
version, access, className, superClassName, implementedItfs, sourceFile);
// visits the inner classes info
if (w != 0) {
i = readUnsignedShort(w); w += 2;
for ( ; i > 0; --i) {
classVisitor.visitInnerClass(
readUnsignedShort(w) == 0 ? null : readClass(w, c),
readUnsignedShort(w + 2) == 0 ? null : readClass(w + 2, c),
readUnsignedShort(w + 4) == 0 ? null : readUTF8(w + 4, c),
readUnsignedShort(w + 6));
w += 8;
}
}
// visits the fields
i = readUnsignedShort(u); u += 2;
for ( ; i > 0; --i) {
access = readUnsignedShort(u);
String fieldName = readUTF8(u + 2, c);
String fieldDesc = readUTF8(u + 4, c);
Attribute fattrs = null;
// visits the field's attributes and looks for a ConstantValue attribute
int fieldValueItem = 0;
j = readUnsignedShort(u + 6);
u += 8;
for ( ; j > 0; --j) {
String attrName = readUTF8(u, c);
if (attrName.equals("ConstantValue")) {
fieldValueItem = readUnsignedShort(u + 6);
} else if (attrName.equals("Synthetic")) {
access |= Constants.ACC_SYNTHETIC;
} else if (attrName.equals("Deprecated")) {
access |= Constants.ACC_DEPRECATED;
} else {
attr = readAttribute(
attrs, attrName, u + 6, readInt(u + 2), c, -1, null);
if (attr != null) {
attr.next = fattrs;
fattrs = attr;
}
}
u += 6 + readInt(u + 2);
}
// reads the field's value, if any
Object value = (fieldValueItem == 0 ? null : readConst(fieldValueItem, c));
// visits the field
classVisitor.visitField(access, fieldName, fieldDesc, value, fattrs);
}
// visits the methods
i = readUnsignedShort(u); u += 2;
for ( ; i > 0; --i) {
access = readUnsignedShort(u);
String methName = readUTF8(u + 2, c);
String methDesc = readUTF8(u + 4, c);
Attribute mattrs = null;
Attribute cattrs = null;
v = 0;
w = 0;
// looks for Code and Exceptions attributes
j = readUnsignedShort(u + 6);
u += 8;
for ( ; j > 0; --j) {
String 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 |= Constants.ACC_SYNTHETIC;
} else if (attrName.equals("Deprecated")) {
access |= Constants.ACC_DEPRECATED;
} else {
attr = readAttribute(attrs, attrName, u, attrSize, c, -1, null);
if (attr != null) {
attr.next = mattrs;
mattrs = 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
CodeVisitor cv;
cv = classVisitor.visitMethod(
access, methName, methDesc, exceptions, mattrs);
if (cv != 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;
// 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 == Constants.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:
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);
if (labels[label] == null) {
labels[label] = new Label();
}
label = readUnsignedShort(v + 2);
if (labels[label] == null) {
labels[label] = new Label();
}
label = readUnsignedShort(v + 4);
if (labels[label] == null) {
labels[label] = new Label();
}
v += 8;
}
// parses the local variable, line number tables, and code attributes
j = readUnsignedShort(v); v += 2;
for ( ; j > 0; --j) {
String attrName = readUTF8(v, c);
if (attrName.equals("LocalVariableTable")) {
if (!skipDebug) {
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("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 {
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;
Label l;
while (v < codeEnd) {
w = v - codeStart;
l = labels[w];
if (l != null) {
cv.visitLabel(l);
if (!skipDebug && l.line > 0) {
cv.visitLineNumber(l.line, l);
}
}
int opcode = b[v] & 0xFF;
switch (ClassWriter.TYPE[opcode]) {
case ClassWriter.NOARG_INSN:
cv.visitInsn(opcode);
v += 1;
break;
case ClassWriter.IMPLVAR_INSN:
if (opcode > Constants.ISTORE) {
opcode -= 59; //ISTORE_0
cv.visitVarInsn(Constants.ISTORE + (opcode >> 2), opcode & 0x3);
} else {
opcode -= 26; //ILOAD_0
cv.visitVarInsn(Constants.ILOAD + (opcode >> 2), opcode & 0x3);
}
v += 1;
break;
case ClassWriter.LABEL_INSN:
cv.visitJumpInsn(opcode, labels[w + readShort(v + 1)]);
v += 3;
break;
case ClassWriter.LABELW_INSN:
cv.visitJumpInsn(opcode - 33, labels[w + readInt(v + 1)]);
v += 5;
break;
case ClassWriter.WIDE_INSN:
opcode = b[v + 1] & 0xFF;
if (opcode == Constants.IINC) {
cv.visitIincInsn(readUnsignedShort(v + 2), readShort(v + 4));
v += 6;
} else {
cv.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;
}
cv.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;
}
cv.visitLookupSwitchInsn(labels[label], keys, values);
break;
case ClassWriter.VAR_INSN:
cv.visitVarInsn(opcode, b[v + 1] & 0xFF);
v += 2;
break;
case ClassWriter.SBYTE_INSN:
cv.visitIntInsn(opcode, b[v + 1]);
v += 2;
break;
case ClassWriter.SHORT_INSN:
cv.visitIntInsn(opcode, readShort(v + 1));
v += 3;
break;
case ClassWriter.LDC_INSN:
cv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c));
v += 2;
break;
case ClassWriter.LDCW_INSN:
cv.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 < Constants.INVOKEVIRTUAL) {
cv.visitFieldInsn(opcode, iowner, iname, idesc);
} else {
cv.visitMethodInsn(opcode, iowner, iname, idesc);
}
if (opcode == Constants.INVOKEINTERFACE) {
v += 5;
} else {
v += 3;
}
break;
case ClassWriter.TYPE_INSN:
cv.visitTypeInsn(opcode, readClass(v + 1, c));
v += 3;
break;
case ClassWriter.IINC_INSN:
cv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]);
v += 3;
break;
// case MANA_INSN:
default:
cv.visitMultiANewArrayInsn(readClass(v + 1, c), b[v + 3] & 0xFF);
v += 4;
break;
}
}
l = labels[codeEnd - codeStart];
if (l != null) {
cv.visitLabel(l);
}
// visits the try catch entries
j = readUnsignedShort(v); v += 2;
for ( ; j > 0; --j) {
Label start = labels[readUnsignedShort(v)];
Label end = labels[readUnsignedShort(v + 2)];
Label handler = labels[readUnsignedShort(v + 4)];
int type = readUnsignedShort(v + 6);
if (type == 0) {
cv.visitTryCatchBlock(start, end, handler, null);
} else {
cv.visitTryCatchBlock(start, end, handler, readUTF8(items[type], c));
}
v += 8;
}
// visits the local variable table
j = readUnsignedShort(v); v += 2;
if (!skipDebug) {
for ( ; j > 0; --j) {
String attrName = readUTF8(v, c);
if (attrName.equals("LocalVariableTable")) {
k = readUnsignedShort(v + 6);
w = v + 8;
for ( ; k > 0; --k) {
label = readUnsignedShort(w);
Label start = labels[label];
label += readUnsignedShort(w + 2);
Label end = labels[label];
cv.visitLocalVariable(
readUTF8(w + 4, c),
readUTF8(w + 6, c),
start,
end,
readUnsignedShort(w + 8));
w += 10;
}
}
v += 6 + readInt(v + 2);
}
}
// visits the other attributes
while (cattrs != null) {
attr = cattrs.next;
cattrs.next = null;
cv.visitAttribute(cattrs);
cattrs = attr;
}
// visits the max stack and max locals values
cv.visitMaxs(maxStack, maxLocals);
}
}
// visits the class attributes
Attribute last = null;
attr = clattrs;
while (attr != null) {
Attribute next = attr.next;
attr.next = last;
last = attr;
attr = next;
}
while (last != null) {
attr = last.next;
last.next = null;
classVisitor.visitAttribute(last);
last = attr;
}
// visits the end of the class
classVisitor.visitEnd();
| public int | getItem(int item)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.
return items[item];
| protected oracle.toplink.libraries.asm.Attribute | readAttribute(oracle.toplink.libraries.asm.Attribute[] attrs, java.lang.String type, int off, int len, char[] buf, int codeOff, oracle.toplink.libraries.asm.Label[] labels)Reads an attribute in {@link #b b}.
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);
| public int | readByte(int index)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.
return b[index] & 0xFF;
| public java.lang.String | readClass(int index, char[] buf)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.
// 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);
| private static byte[] | readClass(java.io.InputStream is)Reads the bytecode of a class.
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;
} else {
len += n;
if (len == b.length) {
byte[] c = new byte[b.length + 1000];
System.arraycopy(b, 0, c, 0, len);
b = c;
}
}
}
| public java.lang.Object | readConst(int item, char[] 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.
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:
default:
return readUTF8(index, buf);
}
| public int | readInt(int index)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.
byte[] b = this.b;
return ((b[index] & 0xFF) << 24) |
((b[index + 1] & 0xFF) << 16) |
((b[index + 2] & 0xFF) << 8) |
(b[index + 3] & 0xFF);
| public long | readLong(int index)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.
long l1 = readInt(index);
long l0 = readInt(index + 4) & 0xFFFFFFFFL;
return (l1 << 32) | l0;
| public short | readShort(int index)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.
byte[] b = this.b;
return (short)(((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
| public java.lang.String | readUTF8(int index, char[] buf)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.
// consults cache
int item = readUnsignedShort(index);
String s = strings[item];
if (s != null) {
return s;
}
// computes the start index of the CONSTANT_Utf8 item in b
index = items[item];
// reads the length of the string (in bytes, not characters)
int utfLen = readUnsignedShort(index);
index += 2;
// parses the string bytes
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;
}
}
s = new String(buf, 0, strLen);
strings[item] = s;
return s;
| public int | readUnsignedShort(int index)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.
byte[] b = this.b;
return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
|
|