2006-11-22 17:45:24 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
* Copyright (c) 2002,2006 IBM Corporation.
|
|
|
|
* All rights reserved. This program and the accompanying materials
|
|
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
* which accompanies this distribution, and is available at
|
|
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
*
|
|
|
|
* Contributors:
|
|
|
|
* IBM Corporation - initial API and implementation
|
|
|
|
*******************************************************************************/
|
|
|
|
package com.ibm.wala.shrikeCT;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the core class for reading class file data.
|
|
|
|
*
|
|
|
|
* ClassReader performs lazy parsing, and thus most of the methods can throw an
|
|
|
|
* InvalidClassFileException.
|
|
|
|
*/
|
|
|
|
public final class ClassReader implements ClassConstants {
|
|
|
|
private byte[] bytes;
|
|
|
|
private int[] methodOffsets;
|
|
|
|
private int[] fieldOffsets;
|
|
|
|
private ConstantPoolParser cpParser;
|
|
|
|
private int classInfoOffset;
|
|
|
|
private int attrInfoOffset;
|
|
|
|
private int interfaceCount;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build a reader.
|
|
|
|
*
|
|
|
|
* If the class file data is corrupt an exception might not be thrown
|
|
|
|
* immediately. Instead an exception might be thrown later, during the
|
|
|
|
* execution of some access method. This is a consequence of the 'lazy
|
|
|
|
* parsing' performed by ClassReader.
|
|
|
|
*
|
|
|
|
* @param bytes
|
|
|
|
* the class file data
|
|
|
|
* @throws InvalidClassFileException
|
|
|
|
* the class file data is corrupt
|
|
|
|
*/
|
|
|
|
public ClassReader(byte[] bytes) throws InvalidClassFileException {
|
|
|
|
this.bytes = bytes;
|
|
|
|
parse();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkLength(int offset, int required) throws InvalidClassFileException {
|
|
|
|
if (bytes.length < offset + required) {
|
|
|
|
throw new InvalidClassFileException(offset, "file truncated, expected " + required + " bytes, saw only "
|
|
|
|
+ (bytes.length - offset));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void parse() throws InvalidClassFileException {
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
checkLength(offset, 10);
|
|
|
|
int magic = getInt(offset);
|
|
|
|
int minorVersion = getUShort(offset + 4);
|
|
|
|
int majorVersion = getUShort(offset + 6);
|
|
|
|
int constantPoolCount = getUShort(offset + 8);
|
|
|
|
offset += 10;
|
|
|
|
|
|
|
|
if (magic != MAGIC) {
|
|
|
|
throw new InvalidClassFileException(offset, "bad magic number: " + magic);
|
|
|
|
}
|
2007-02-28 14:08:50 +00:00
|
|
|
if (majorVersion < 45 || majorVersion > 50) {
|
2006-11-22 17:45:24 +00:00
|
|
|
throw new InvalidClassFileException(offset, "unknown class file version: " + majorVersion + "." + minorVersion);
|
|
|
|
}
|
|
|
|
|
|
|
|
cpParser = new ConstantPoolParser(bytes, offset, constantPoolCount);
|
|
|
|
offset += cpParser.getRawSize();
|
|
|
|
|
|
|
|
classInfoOffset = offset;
|
|
|
|
checkLength(offset, 8);
|
|
|
|
// int accessFlags = getUShort(offset);
|
|
|
|
// int thisClass = getUShort(offset + 2);
|
|
|
|
// int superClass = getUShort(offset + 4);
|
|
|
|
interfaceCount = getUShort(offset + 6);
|
|
|
|
if (interfaceCount < 0) {
|
|
|
|
throw new InvalidClassFileException(offset, "negative interface count: " + interfaceCount);
|
|
|
|
}
|
|
|
|
offset += 8;
|
|
|
|
checkLength(offset, interfaceCount * 2);
|
|
|
|
offset += interfaceCount * 2;
|
|
|
|
|
|
|
|
checkLength(offset, 2);
|
|
|
|
int fieldCount = getUShort(offset);
|
|
|
|
if (fieldCount < 0) {
|
|
|
|
throw new InvalidClassFileException(offset, "negative field count: " + interfaceCount);
|
|
|
|
}
|
|
|
|
offset = parseFields(offset + 2, fieldCount);
|
|
|
|
|
|
|
|
checkLength(offset, 2);
|
|
|
|
int methodCount = getUShort(offset);
|
|
|
|
if (methodCount < 0) {
|
|
|
|
throw new InvalidClassFileException(offset, "negative method count: " + interfaceCount);
|
|
|
|
}
|
|
|
|
offset = parseMethods(offset + 2, methodCount);
|
|
|
|
|
|
|
|
attrInfoOffset = offset;
|
|
|
|
checkLength(offset, 2);
|
|
|
|
int attrCount = getUShort(offset);
|
|
|
|
offset = skipAttributes(offset + 2, attrCount);
|
|
|
|
|
|
|
|
if (offset != bytes.length) {
|
|
|
|
throw new InvalidClassFileException(offset, "extra data in class file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private int skipAttributes(int offset, int count) throws InvalidClassFileException {
|
|
|
|
if (count < 0) {
|
|
|
|
throw new InvalidClassFileException(offset, "negative attribute count: " + interfaceCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
checkLength(offset, 6);
|
|
|
|
int size = getInt(offset + 2);
|
|
|
|
if (size < 0) {
|
|
|
|
throw new InvalidClassFileException(offset, "negative attribute size: " + size);
|
|
|
|
}
|
|
|
|
offset += 6;
|
|
|
|
checkLength(offset, size);
|
|
|
|
offset += size;
|
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int parseFields(int offset, int count) throws InvalidClassFileException {
|
|
|
|
fieldOffsets = new int[count + 1];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
fieldOffsets[i] = offset;
|
|
|
|
checkLength(offset, 8);
|
|
|
|
offset = skipAttributes(offset + 8, getUShort(offset + 6));
|
|
|
|
}
|
|
|
|
fieldOffsets[count] = offset;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int parseMethods(int offset, int count) throws InvalidClassFileException {
|
|
|
|
methodOffsets = new int[count + 1];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
methodOffsets[i] = offset;
|
|
|
|
checkLength(offset, 8);
|
|
|
|
offset = skipAttributes(offset + 8, getUShort(offset + 6));
|
|
|
|
}
|
|
|
|
methodOffsets[count] = offset;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the raw class data bytes
|
|
|
|
*/
|
|
|
|
public byte[] getBytes() {
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the magic number at the start of the class file.
|
|
|
|
*/
|
|
|
|
public int getMagic() {
|
|
|
|
return getInt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the minor version of the class file
|
|
|
|
*/
|
|
|
|
public int getMinorVersion() {
|
|
|
|
return getUShort(4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the major version of the class file
|
|
|
|
*/
|
|
|
|
public int getMajorVersion() {
|
|
|
|
return getUShort(6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the access flags for the class
|
|
|
|
*/
|
|
|
|
public int getAccessFlags() {
|
|
|
|
return getUShort(classInfoOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the index of the constant pool entry for the class name
|
|
|
|
*/
|
|
|
|
public int getNameIndex() {
|
|
|
|
return getUShort(classInfoOffset + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
String getClassFromAddress(int addr) throws InvalidClassFileException {
|
|
|
|
int c = getUShort(addr);
|
|
|
|
if (c == 0) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
return cpParser.getCPClass(c);
|
|
|
|
} catch (IllegalArgumentException ex) {
|
|
|
|
throw new InvalidClassFileException(addr, "Invalid class constant pool index: " + c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the name of the class in JVM format (e.g., java/lang/Object)
|
|
|
|
*/
|
|
|
|
public String getName() throws InvalidClassFileException {
|
|
|
|
String s = getClassFromAddress(classInfoOffset + 2);
|
|
|
|
if (s == null) {
|
|
|
|
throw new InvalidClassFileException(classInfoOffset + 2, "Null class name not allowed");
|
|
|
|
} else {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the constant pool index of the superclass name, or 0 if this is
|
|
|
|
* java.lang.Object
|
|
|
|
*/
|
|
|
|
public int getSuperNameIndex() {
|
|
|
|
return getUShort(classInfoOffset + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the superclass name in JVM format (e.g., java/lang/Object), or null
|
|
|
|
* if this class is java.lang.Object
|
|
|
|
*/
|
|
|
|
public String getSuperName() throws InvalidClassFileException {
|
|
|
|
return getClassFromAddress(classInfoOffset + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the number of interfaces this class implements
|
|
|
|
*/
|
|
|
|
public int getInterfaceCount() {
|
|
|
|
return interfaceCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyInterfaceIndex(int i) {
|
|
|
|
if (i < 0 || i >= interfaceCount) {
|
|
|
|
throw new IllegalArgumentException("Invalid interface index: " + i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the constant pool index of the name of the i'th implemented
|
|
|
|
* interface
|
|
|
|
*/
|
|
|
|
public int getInterfaceNameIndex(int i) {
|
|
|
|
verifyInterfaceIndex(i);
|
|
|
|
return getUShort(classInfoOffset + 8 + 2 * i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return an array of the constant pool indices for the names of the
|
|
|
|
* implemented interfaces
|
|
|
|
*/
|
|
|
|
public int[] getInterfaceNameIndices() {
|
|
|
|
int[] indices = new int[interfaceCount];
|
|
|
|
for (int i = 0; i < interfaceCount; i++) {
|
|
|
|
indices[i] = getUShort(classInfoOffset + 8 + 2 * i);
|
|
|
|
}
|
|
|
|
return indices;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the name of the i'th implemented interface
|
|
|
|
*/
|
|
|
|
public String getInterfaceName(int i) throws InvalidClassFileException {
|
|
|
|
verifyInterfaceIndex(i);
|
|
|
|
String s = getClassFromAddress(classInfoOffset + 8 + 2 * i);
|
|
|
|
if (s == null) {
|
|
|
|
throw new InvalidClassFileException(classInfoOffset + 8 + 2 * i, "Null interface name not allowed");
|
|
|
|
} else {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return an array of the names of the implemented interfaces
|
|
|
|
*/
|
|
|
|
public String[] getInterfaceNames() throws InvalidClassFileException {
|
|
|
|
String[] names = new String[interfaceCount];
|
|
|
|
for (int i = 0; i < interfaceCount; i++) {
|
|
|
|
String s = getClassFromAddress(classInfoOffset + 8 + 2 * i);
|
|
|
|
if (s == null) {
|
|
|
|
throw new InvalidClassFileException(classInfoOffset + 8 + 2 * i, "Null interface name not allowed");
|
|
|
|
}
|
|
|
|
names[i] = s;
|
|
|
|
}
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method allows direct read-only access to the constant pool for the
|
|
|
|
* class.
|
|
|
|
*
|
|
|
|
* @return the constant pool for the class
|
|
|
|
*/
|
|
|
|
public ConstantPoolParser getCP() {
|
|
|
|
return cpParser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the signed 32-bit value at offset i in the class data
|
|
|
|
*/
|
|
|
|
public int getInt(int i) {
|
|
|
|
return (bytes[i] << 24) + ((bytes[i + 1] & 0xFF) << 16) + ((bytes[i + 2] & 0xFF) << 8) + (bytes[i + 3] & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the unsigned 16-bit value at offset i in the class data
|
|
|
|
*/
|
|
|
|
public int getUShort(int i) {
|
|
|
|
return ((bytes[i] & 0xFF) << 8) + (bytes[i + 1] & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the signed 16-bit value at offset i in the class data
|
|
|
|
*/
|
|
|
|
public int getShort(int i) {
|
|
|
|
return (bytes[i] << 8) + (bytes[i + 1] & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the signed 8-bit value at offset i in the class data
|
|
|
|
*/
|
|
|
|
public byte getByte(int i) {
|
|
|
|
return bytes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the number of fields in the class
|
|
|
|
*/
|
|
|
|
public int getFieldCount() {
|
|
|
|
return fieldOffsets.length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyFieldIndex(int f) {
|
|
|
|
if (f < 0 || f >= fieldOffsets.length - 1) {
|
|
|
|
throw new IllegalArgumentException("Invalid field index: " + f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the access flags for the f'th field
|
|
|
|
*/
|
|
|
|
public int getFieldAccessFlags(int f) {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return getUShort(fieldOffsets[f]);
|
|
|
|
}
|
|
|
|
|
|
|
|
String getUtf8FromAddress(int addr) throws InvalidClassFileException {
|
|
|
|
int s = getUShort(addr);
|
|
|
|
if (s == 0) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
return cpParser.getCPUtf8(s);
|
|
|
|
} catch (IllegalArgumentException ex) {
|
|
|
|
throw new InvalidClassFileException(addr, "Invalid Utf8 constant pool index: " + s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the name of the f'th field
|
|
|
|
*/
|
|
|
|
public String getFieldName(int f) throws InvalidClassFileException {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return getUtf8FromAddress(fieldOffsets[f] + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the type of the f'th field, in JVM format (e.g., I, Z,
|
|
|
|
* java/lang/Object)
|
|
|
|
*/
|
|
|
|
public String getFieldType(int f) throws InvalidClassFileException {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return getUtf8FromAddress(fieldOffsets[f] + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the index of the constant pool entry for the name of the f'th
|
|
|
|
* field, in JVM format (e.g., I, Z, Ljava/lang/Object;)
|
|
|
|
*/
|
|
|
|
public int getFieldNameIndex(int f) {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return getUShort(fieldOffsets[f] + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the index of the constant pool entry for the type of the f'th
|
|
|
|
* field, in JVM format (e.g., I, Z, Ljava/lang/Object;)
|
|
|
|
*/
|
|
|
|
public int getFieldTypeIndex(int f) {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return getUShort(fieldOffsets[f] + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* AttrIterator provides access to attributes in the class file.
|
|
|
|
*
|
|
|
|
* AttrIterators can be reused for many different iterations, like this:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* AttrIterator iter = new AttrIterator();
|
|
|
|
* int fieldCount = reader.getFieldCount();
|
|
|
|
* for (int i = 0; i < fieldCount; i++) {
|
|
|
|
* reader.initFieldAttributeIterator(i, iter);
|
|
|
|
* for (; iter.isValid(); iter.advance()) {
|
|
|
|
* if (iter.getName().equals("ConstantValue")) {
|
|
|
|
* ConstantValueReader cv = new ConstantValueReader(iter);
|
|
|
|
* ...
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* </pre>
|
|
|
|
*/
|
|
|
|
public static final class AttrIterator {
|
|
|
|
ClassReader cr;
|
|
|
|
int offset;
|
|
|
|
int size;
|
|
|
|
private int remaining;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a blank iterator. The iterator is not valid until it is
|
|
|
|
* initialized by some other class.
|
|
|
|
*/
|
|
|
|
public AttrIterator() {
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setSize() {
|
|
|
|
if (remaining > 0) {
|
|
|
|
size = 6 + cr.getInt(offset + 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void init(ClassReader cr, int offset) {
|
|
|
|
this.cr = cr;
|
|
|
|
this.offset = offset + 2;
|
|
|
|
this.remaining = cr.getUShort(offset);
|
|
|
|
setSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void verifyValid() {
|
|
|
|
if (remaining <= 0) {
|
|
|
|
throw new IllegalArgumentException("Attempt to manipulate invalid AttrIterator");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public ClassReader getClassReader() {
|
|
|
|
verifyValid();
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* @return the offset of the raw attribute data (including attribute header)
|
|
|
|
* in the class file data
|
|
|
|
*/
|
|
|
|
public int getRawOffset() {
|
|
|
|
verifyValid();
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* @return the size of the raw attribute data (including attribute header)
|
|
|
|
* in the class file data
|
|
|
|
*/
|
|
|
|
public int getRawSize() {
|
|
|
|
verifyValid();
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* @return the offset of the attribute data (excluding attribute header) in
|
|
|
|
* the class file data
|
|
|
|
*/
|
|
|
|
public int getDataOffset() {
|
|
|
|
verifyValid();
|
|
|
|
return offset + 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* @return the size of the attribute data (excluding attribute header) in
|
|
|
|
* the class file data
|
|
|
|
*/
|
|
|
|
public int getDataSize() {
|
|
|
|
verifyValid();
|
|
|
|
return size - 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the number of attributes left in the list, including this
|
|
|
|
* attribute (if valid)
|
|
|
|
*/
|
|
|
|
public int getRemainingAttributesCount() {
|
|
|
|
return remaining;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* @return the constant pool index of the name of the attribute
|
|
|
|
*/
|
|
|
|
public int getNameIndex() {
|
|
|
|
verifyValid();
|
|
|
|
return cr.getUShort(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* @return the name of the attribute
|
|
|
|
*/
|
|
|
|
public String getName() throws InvalidClassFileException {
|
|
|
|
verifyValid();
|
|
|
|
String s = cr.getUtf8FromAddress(offset);
|
|
|
|
if (s == null) {
|
|
|
|
throw new InvalidClassFileException(offset, "Null attribute name");
|
|
|
|
} else {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return whether this iterator is valid
|
|
|
|
*/
|
|
|
|
public boolean isValid() {
|
|
|
|
return remaining > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attribute iterator must be valid.
|
|
|
|
*
|
|
|
|
* The iterator is advanced to the next attribute (which might not exist, so
|
|
|
|
* the iterator might become invalid).
|
|
|
|
*/
|
|
|
|
public void advance() {
|
|
|
|
verifyValid();
|
|
|
|
offset += size;
|
|
|
|
remaining--;
|
|
|
|
setSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Point iter at the list of attributes for field f.
|
2007-05-08 21:23:06 +00:00
|
|
|
* @throws IllegalArgumentException if iter is null
|
2006-11-22 17:45:24 +00:00
|
|
|
*/
|
|
|
|
public void initFieldAttributeIterator(int f, AttrIterator iter) {
|
2007-05-08 21:23:06 +00:00
|
|
|
if (iter == null) {
|
|
|
|
throw new IllegalArgumentException("iter is null");
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
verifyFieldIndex(f);
|
|
|
|
iter.init(this, fieldOffsets[f] + 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the offset of the raw class data for field f
|
|
|
|
*/
|
|
|
|
public int getFieldRawOffset(int f) {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return fieldOffsets[f];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the size of the raw class data for field f
|
|
|
|
*/
|
|
|
|
public int getFieldRawSize(int f) {
|
|
|
|
verifyFieldIndex(f);
|
|
|
|
return fieldOffsets[f + 1] - fieldOffsets[f];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the number of methods in the class
|
|
|
|
*/
|
|
|
|
public int getMethodCount() {
|
|
|
|
return methodOffsets.length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyMethodIndex(int m) {
|
|
|
|
if (m < 0 || m >= methodOffsets.length - 1) {
|
|
|
|
throw new IllegalArgumentException("Invalid method index: " + m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the offset of the raw class data for method m
|
|
|
|
*/
|
|
|
|
public int getMethodRawOffset(int m) {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return methodOffsets[m];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the size of the raw class data for method m
|
|
|
|
*/
|
|
|
|
public int getMethodRawSize(int m) {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return methodOffsets[m + 1] - methodOffsets[m];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the access flags for method m
|
|
|
|
*/
|
|
|
|
public int getMethodAccessFlags(int m) {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return getUShort(methodOffsets[m]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the name of method m
|
|
|
|
*/
|
|
|
|
public String getMethodName(int m) throws InvalidClassFileException {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return getUtf8FromAddress(methodOffsets[m] + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the method descriptor of method m in JVM format (e.g.,
|
|
|
|
* V(ILjava/lang/Object;) )
|
|
|
|
*/
|
|
|
|
public String getMethodType(int m) throws InvalidClassFileException {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return getUtf8FromAddress(methodOffsets[m] + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the constant pool index of the name of method m
|
|
|
|
*/
|
|
|
|
public int getMethodNameIndex(int m) {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return getUShort(methodOffsets[m] + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the constant pool index of the method descriptor of method m
|
|
|
|
*/
|
|
|
|
public int getMethodTypeIndex(int m) {
|
|
|
|
verifyMethodIndex(m);
|
|
|
|
return getUShort(methodOffsets[m] + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Point iter at the list of attributes for method m.
|
2007-05-08 21:23:06 +00:00
|
|
|
* @throws IllegalArgumentException if iter is null
|
2006-11-22 17:45:24 +00:00
|
|
|
*/
|
|
|
|
public void initMethodAttributeIterator(int m, AttrIterator iter) {
|
2007-05-08 21:23:06 +00:00
|
|
|
if (iter == null) {
|
|
|
|
throw new IllegalArgumentException("iter is null");
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
verifyMethodIndex(m);
|
|
|
|
iter.init(this, methodOffsets[m] + 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Point iter at the list of attributes for the class.
|
2007-05-08 21:23:06 +00:00
|
|
|
* @throws IllegalArgumentException if iter is null
|
2006-11-22 17:45:24 +00:00
|
|
|
*/
|
|
|
|
public void initClassAttributeIterator(AttrIterator iter) {
|
2007-05-08 21:23:06 +00:00
|
|
|
if (iter == null) {
|
|
|
|
throw new IllegalArgumentException("iter is null");
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
iter.init(this, attrInfoOffset);
|
|
|
|
}
|
|
|
|
}
|