oracle.toplink.libraries.asm.ClassReader Maven / Gradle / Ivy
The newest version!
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000,2002,2003 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 oracle.toplink.libraries.asm;
import java.io.InputStream;
import java.io.IOException;
/**
* 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.
*
* @author Eric Bruneton
*/
public class ClassReader {
/**
* 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 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 String[] strings;
/**
* Maximum length of the strings contained in the constant pool of the class.
*/
private int maxStringLength;
/**
* Start index of the class header information (access, name...) in {@link #b
* b}.
*/
private int header;
// --------------------------------------------------------------------------
// Constructors
// --------------------------------------------------------------------------
/**
* Constructs a new {@link ClassReader 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 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)];
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;
}
/**
* Constructs a new {@link ClassReader 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 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;
} else {
len += n;
if (len == b.length) {
byte[] c = new byte[b.length + 1000];
System.arraycopy(b, 0, c, 0, len);
b = c;
}
}
}
}
// --------------------------------------------------------------------------
// Public methods
// --------------------------------------------------------------------------
/**
* 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}).
*
* @param classVisitor the visitor that must visit this class.
* @param skipDebug true if the debug information of the class must
* not be visited. In this case the {@link CodeVisitor#visitLocalVariable
* visitLocalVariable} and {@link CodeVisitor#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
* 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 true if the debug information of the class must
* not be visited. In this case the {@link CodeVisitor#visitLocalVariable
* visitLocalVariable} and {@link CodeVisitor#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;
// 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();
}
// --------------------------------------------------------------------------
// 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) {
// 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;
}
/**
* 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 java.lang.Integer Integer}, {@link java.lang.Float
* Float}, {@link java.lang.Long Long}, {@link java.lang.Double Double},
* {@link String String} or {@link Type 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:
default:
return readUTF8(index, buf);
}
}
/**
* 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.
*/
protected 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);
}
}