netscape-revival
sun-java/classsrc/netscape/tools/ClassFileParser/ClassFileParser.java
// Copyright (c) 1995 Netscape Communications Corporation. All rights reserved.
package netscape.tools.ClassFileParser;
import netscape.tools.ClassFileParser.*;
import java.io.*;
import java.util.Hashtable;
public class ClassFileParser {
// access_flags
public static final int ACC_PUBLIC = 0x0001;
public static final int ACC_PRIVATE = 0x0002;
public static final int ACC_PROTECTED = 0x0004;
public static final int ACC_STATIC = 0x0008;
public static final int ACC_FINAL = 0x0010;
public static final int ACC_SYNCHRONIZED = 0x0020;
public static final int ACC_THREADSAFE = 0x0040;
public static final int ACC_TRANSIENT = 0x0080;
public static final int ACC_NATIVE = 0x0100;
public static final int ACC_INTERFACE = 0x0200;
public static final int ACC_ABSTRACT = 0x0400;
// signatures
public static final char SIG_BOOLEAN = 'Z';
public static final char SIG_BYTE = 'B';
public static final char SIG_CHAR = 'C';
public static final char SIG_SHORT = 'S';
public static final char SIG_INT = 'I';
public static final char SIG_LONG = 'J';
public static final char SIG_FLOAT = 'F';
public static final char SIG_DOUBLE = 'D';
public static final char SIG_VOID = 'V';
public static final char SIG_CLASS = 'L';
public static final char SIG_ENDCLASS = ';';
public static final char SIG_ARRAY = '[';
public static final char SIG_DUMMY = ']'; // happy emacs
public static final char SIG_METHOD = '(';
public static final char SIG_ENDMETHOD = ')';
// internal class file constants
static final int MAGIC = 0xCAFEBABE;
static final int MAJOR_VERSION = 45;
static final int MINOR_VERSION = 3;
// constant types
static final int CONSTANT_Class = 7;
static final int CONSTANT_Fieldref = 9;
static final int CONSTANT_Methodref = 10;
static final int CONSTANT_String = 8;
static final int CONSTANT_Integer = 3;
static final int CONSTANT_Float = 4;
static final int CONSTANT_Long = 5;
static final int CONSTANT_Double = 6;
static final int CONSTANT_InterfaceMethodref = 11;
static final int CONSTANT_NameAndType = 12;
static final int CONSTANT_Asciz = 1;
////////////////////////////////////////////////////////////////////////////
// Global stuff:
public static String[] classpath;
static {
classpath = new String[1];
classpath[0] = ".";
}
public static boolean trace = false;
static Hashtable classes = new Hashtable();
public static ClassDef getClassDef(String classname)
throws ClassNotFoundException, IOException {
ClassDef c = (ClassDef)classes.get(classname);
if (c != null) return c;
c = loadClassDef(classname);
classes.put(classname, c);
return c;
}
static File getInputFile(String classname) throws ClassNotFoundException {
for (int i = 0; i < classpath.length; i++) {
String path = classpath[i] + "/" + classname + ".class";
if (trace)
System.err.println("looking for " + path);
File file = new File(path);
if (file.exists()) return file;
}
throw new ClassNotFoundException(classname);
}
static ClassDef loadClassDef(String classname) throws IOException, ClassNotFoundException {
File file = getInputFile(classname);
// long startTime = System.currentTimeMillis();
ClassDef clazz = new ClassFileParser().parse(classname, file);
// long stopTime = System.currentTimeMillis();
// System.err.println("Time to load " + classname + " = " + (stopTime - startTime));
return clazz;
}
////////////////////////////////////////////////////////////////////////////
// Instance stuff:
ClassDef classDef;
Object[] cpool;
ClassFileParser() {
}
ClassDef parse(String classname, File file)
throws ClassNotFoundException, IOException {
InputStream in = new BufferedInputStream(new FileInputStream(file), 8*1024);
LoggingInputStream s = new LoggingInputStream(in, trace);
int magic = s.readInt("magic number");
if (magic != MAGIC)
throw new ClassFormatError("The file " + classname + " isn't a Java .class file.");
int minorVersion = s.readShort("minorVersion");
int majorVersion = s.readShort("majorVersion");
if (majorVersion != MAJOR_VERSION || minorVersion != MINOR_VERSION)
throw new ClassFormatError("The file " + classname +
" has the wrong version (" +
majorVersion + "." + minorVersion +
" rather than " +
MAJOR_VERSION + "." + MINOR_VERSION + ")");
short constant_pool_count = s.readShort("constant_pool_count");
cpool = new Object[constant_pool_count];
// suck in the constant pool for future use
for (int i = 1; i < constant_pool_count; i++) { // constant_pool[0] always unused
byte type = s.readByte("constant pool [" + i + "] item type");
switch (type) {
case CONSTANT_Class: {
short name_index = s.readShort("Class name_index");
cpool[i] = new ClassCPValue(name_index);
break;
}
case CONSTANT_Fieldref: {
short class_index = s.readShort("Field class_index");
short name_and_type_index = s.readShort("Fieldref name_and_type_index");
cpool[i] = new FieldCPValue(class_index, name_and_type_index);
break;
}
case CONSTANT_Methodref: {
short class_index = s.readShort("Method class_index");
short name_and_type_index = s.readShort("Methodref name_and_type_index");
cpool[i] = new MethodCPValue(class_index, name_and_type_index);
break;
}
case CONSTANT_InterfaceMethodref: {
short class_index = s.readShort("InterfaceMethod class_index");
short name_and_type_index = s.readShort("InterfaceMethodref name_and_type_index");
cpool[i] = new InterfaceMethodCPValue(class_index, name_and_type_index);
break;
}
case CONSTANT_String: {
short string_index = s.readShort("String string_index");
cpool[i] = new StringCPValue(cpool, string_index);
break;
}
case CONSTANT_Integer: {
int value = s.readInt("Integer value");
cpool[i] = new Integer(value);
break;
}
case CONSTANT_Float: {
float value = s.readFloat("Float value");
cpool[i] = new Float(value);
break;
}
case CONSTANT_Long: {
long value = s.readLong("Long value");
i++; /* Argh!!!!! */
cpool[i] = new Long(value);
break;
}
case CONSTANT_Double: {
double value = s.readDouble("Double value");
i++; /* Argh!!!!! */
cpool[i] = new Double(value);
break;
}
case CONSTANT_NameAndType: {
short name_index = s.readShort("NameAndType name_index");
short signature_index = s.readShort("NameAndType signature_index");
cpool[i] = new NameAndTypeCPValue(name_index, signature_index);
break;
}
case CONSTANT_Asciz: {
cpool[i] = s.readUTF("Asciz value");
break;
}
default:
throw new ClassFormatError("Unrecognized constant pool type: " + type);
}
}
short access_flags = s.readShort("access_flags");
short this_class = s.readShort("this_class");
classDef = new ClassDef();
classDef.modDate = file.lastModified();
ClassCPValue myClass = (ClassCPValue)cpool[this_class];
classDef.name = (myClass != null) ? getName(myClass.name_index) : "<unknown>";
short super_class = s.readShort("super_class");
ClassCPValue mySuper = (ClassCPValue)cpool[super_class];
classDef.superclass = (mySuper != null) ? getClassDef(getName(mySuper.name_index)) : null;
short interfaces_count = s.readShort("interfaces_count");
classDef.interfaces = new ClassDef[interfaces_count];
for (int i = 0; i < interfaces_count; i++) {
classDef.interfaces[i] = readInterface(s);
}
short fields_count = s.readShort("fields_count");
classDef.fields = new FieldDef[fields_count];
for (int i = 0; i < fields_count; i++) {
classDef.fields[i] = readField(s);
}
short methods_count = s.readShort("methods_count");
classDef.methods = new MethodDef[methods_count];
for (int i = 0; i < methods_count; i++) {
classDef.methods[i] = readMethod(s);
}
short attributes_count = s.readShort("attributes_count");
classDef.attributes = new AttrDef[attributes_count];
for (int i = 0; i < attributes_count; i++) {
classDef.attributes[i] = readAttribute(s);
}
// success:
return classDef;
}
String getName(int index) {
Object obj = cpool[index];
if (obj == null) return "null";
return obj.toString();
}
ClassDef readInterface(LoggingInputStream s)
throws ClassNotFoundException, IOException {
short index = s.readShort("Interface index");
return getClassDef(getName(((ClassCPValue)cpool[index]).name_index));
}
FieldDef readField(LoggingInputStream s)
throws ClassNotFoundException, IOException {
FieldDef fd = new FieldDef();
fd.clazz = classDef;
fd.flags = s.readShort("Field access_flags");
fd.name = getName(s.readShort("Field name_index"));
fd.signature = getName(s.readShort("Field signature_index"));
short attributes_count = s.readShort("Field attributes_count");
fd.attributes = new AttrDef[attributes_count];
for (int i = 0; i < attributes_count; i++) {
fd.attributes[i] = readAttribute(s);
}
return fd;
}
MethodDef readMethod(LoggingInputStream s)
throws ClassNotFoundException, IOException {
MethodDef md = new MethodDef();
md.clazz = classDef;
md.flags = s.readShort("Method access_flags");
md.name = getName(s.readShort("Method name_index"));
md.signature = getName(s.readShort("Method signature_index"));
short attributes_count = s.readShort("Method attributes_count");
md.attributes = new AttrDef[attributes_count];
for (int i = 0; i < attributes_count; i++) {
md.attributes[i] = readAttribute(s);
}
return md;
}
AttrDef readAttribute(LoggingInputStream s)
throws ClassNotFoundException, IOException {
short attribute_name_index = s.readShort("Attribute attribute_name_index");
int attribute_length = s.readInt("Attribute attribute_length");
String name = (String)cpool[attribute_name_index];
if ("SourceFile".equalsIgnoreCase(name)) {
SourceFileAttrDef ad = new SourceFileAttrDef();
short sourcefile_index = s.readShort("Attribute SourceFile sourcefile_index");
ad.sourceFile = getName(sourcefile_index);
return ad;
}
else if ("ConstantValue".equalsIgnoreCase(name)) {
ConstantValueAttrDef ad = new ConstantValueAttrDef();
short constantvalue_index = s.readShort("Attribute ConstantValue constantvalue_index");
Object cpvalue = cpool[constantvalue_index];
if (cpvalue instanceof CPValue)
cpvalue = ((CPValue)cpvalue).valueOf();
ad.value = cpvalue;
return ad;
}
else if ("Code".equalsIgnoreCase(name)) {
CodeAttrDef ad = new CodeAttrDef();
ad.maxStack = s.readShort("Attribute Code max_stack");
ad.maxLocals = s.readShort("Attribute Code max_locals");
int code_length = s.readInt("Attribute Code code_length");
ad.code = new byte[code_length];
s.read(ad.code, "Attribute Code bytecodes");
short exception_table_length = s.readShort("Attribute Code exception_table_length");
ad.exceptions = new ExceptionAttrDef[exception_table_length];
for (int i = 0; i < exception_table_length; i++) {
ExceptionAttrDef e = new ExceptionAttrDef();
e.startPC = s.readShort("Attribute Code ExceptionTable start_pc");
e.endPC = s.readShort("Attribute Code ExceptionTable end_pc");
e.handlerPC = s.readShort("Attribute Code ExceptionTable handler_pc");
e.catchType = getName(s.readShort("Attribute Code ExceptionTable catch_type"));
ad.exceptions[i] = e;
}
short attributes_count = s.readShort("Attribute Code attributes_count");
ad.attributes = new AttrDef[attributes_count];
for (int i = 0; i < attributes_count; i++) {
ad.attributes[i] = readAttribute(s);
}
return ad;
}
else if ("LineNumberTable".equalsIgnoreCase(name)) {
LineNumberTableAttrDef ad = new LineNumberTableAttrDef();
short line_number_table_length = s.readShort("Attribute LineNumberTable line_number_table_length");
ad.lineNumbers = new LineNumberAttrDef[line_number_table_length];
for (int i = 0; i < line_number_table_length; i++) {
LineNumberAttrDef ln = new LineNumberAttrDef();
ln.startPC = s.readShort("Attribute LineNumberTable start_pc");
ln.lineNumber = s.readShort("Attribute LineNumberTable line_number");
ad.lineNumbers[i] = ln;
}
return ad;
}
else if ("LocalVariableTable".equalsIgnoreCase(name)) {
LocalVariableTableAttrDef ad = new LocalVariableTableAttrDef();
short local_variable_table_length = s.readShort("Attribute LocalVariableNumberTable local_variable_table_length");
ad.localVariables = new LocalVariableAttrDef[local_variable_table_length];
for (int i = 0; i < local_variable_table_length; i++) {
LocalVariableAttrDef lv = new LocalVariableAttrDef();
lv.startPC = s.readShort("Attribute LocalVariableNumberTable start_pc");
lv.length = s.readShort("Attribute LocalVariableNumberTable length");
lv.name = getName(s.readShort("Attribute LocalVariableNumberTable name_index"));
lv.signature = getName(s.readShort("Attribute LocalVariableNumberTable signature_index"));
lv.slot = s.readShort("Attribute LocalVariableNumberTable slot");
ad.localVariables[i] = lv;
}
return ad;
}
else if ("Exceptions".equalsIgnoreCase(name)) {
ExceptionsTableAttrDef ad = new ExceptionsTableAttrDef();
short number_of_exceptions = s.readShort("Attribute Exceptions number_of_exceptions");
ad.exceptions = new String[number_of_exceptions];
for (int i = 0; i < number_of_exceptions; i++) {
short index = s.readShort("Attribute Exceptions exception_table_index[i]");
if (index != 0) {
String classname = getName(((ClassCPValue)cpool[index]).name_index);
ad.exceptions[i] = classname;
}
}
return ad;
}
else {
UnknownAttrDef ad = new UnknownAttrDef();
ad.name = getName(attribute_name_index);
ad.length = attribute_length;
s.skipBytes(attribute_length, "Unrecognized attribute");
return ad;
}
}
}
class CPValue {
public Object valueOf() {
return this;
}
}
class ClassCPValue extends CPValue {
public short name_index;
ClassCPValue(short name_index) {
this.name_index = name_index;
}
}
class FieldCPValue extends CPValue {
public short class_index;
public short name_and_type_index;
FieldCPValue(short class_index, short name_and_type_index) {
this.class_index = class_index;
this.name_and_type_index = name_and_type_index;
}
}
class MethodCPValue extends CPValue {
public short class_index;
public short name_and_type_index;
MethodCPValue(short class_index, short name_and_type_index) {
this.class_index = class_index;
this.name_and_type_index = name_and_type_index;
}
}
class InterfaceMethodCPValue extends CPValue {
public short class_index;
public short name_and_type_index;
InterfaceMethodCPValue(short class_index, short name_and_type_index) {
this.class_index = class_index;
this.name_and_type_index = name_and_type_index;
}
}
class StringCPValue extends CPValue {
Object[] cpool;
public short string_index;
StringCPValue(Object[] cpool, short string_index) {
this.cpool = cpool;
this.string_index = string_index;
}
public Object valueOf() {
return cpool[string_index];
}
}
class NameAndTypeCPValue extends CPValue {
public short name_index;
public short signature_index;
NameAndTypeCPValue(short name_index, short signature_index) {
this.name_index = name_index;
this.signature_index = signature_index;
}
}
class LoggingInputStream {
public boolean trace;
DataInputStream s;
LoggingInputStream(InputStream s, boolean trace) {
super();
this.s = new DataInputStream(s);
this.trace = trace;
}
public int readInt(String reason) throws IOException {
if (trace) System.err.print("# reading int for " + reason);
int result = s.readInt();
if (trace) System.err.println(" => " + result);
return result;
}
public short readShort(String reason) throws IOException {
if (trace) System.err.print("# reading short for " + reason);
short result = s.readShort();
if (trace) System.err.println(" => " + result);
return result;
}
public byte readByte(String reason) throws IOException {
if (trace) System.err.print("# reading byte for " + reason);
byte result = s.readByte();
if (trace) System.err.println(" => " + result);
return result;
}
public float readFloat(String reason) throws IOException {
if (trace) System.err.print("# reading float for " + reason);
float result = s.readFloat();
if (trace) System.err.println(" => " + result);
return result;
}
public long readLong(String reason) throws IOException {
if (trace) System.err.print("# reading long for " + reason);
long result = s.readLong();
if (trace) System.err.println(" => " + result);
return result;
}
public double readDouble(String reason) throws IOException {
if (trace) System.err.print("# reading double for " + reason);
double result = s.readDouble();
if (trace) System.err.println(" => " + result);
return result;
}
public void read(byte[] b, String reason) throws IOException {
if (trace) System.err.println("# reading byte[" + b.length + "] for " + reason);
s.readFully(b);
}
public void skipBytes(int amount, String reason) throws IOException {
if (trace) System.err.println("# skipping " + amount + " bytes for " + reason);
s.skipBytes(amount);
}
public String readUTF(String reason) throws IOException {
if (trace) System.err.print("# reading utf for " + reason);
String result = DataInputStream.readUTF(s);
if (trace) System.err.println(" => " + result);
return result;
}
}