netscape-revival
sun-java/classsrc/netscape/tools/DumpClass/DumpClass.java
// Copyright (c) 1995 Netscape Communications Corporation. All rights reserved.
package netscape.tools.DumpClass;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.IOException;
public class DumpClass {
public static void main(String[] argv) throws Exception {
if (argv.length != 1) {
System.out.println("usage: java DumpClass <file.class>");
return;
}
new DumpClass().dump(argv[0]);
}
static final int MAGIC = 0xCAFEBABE;
static final int MAJOR_VERSION = 45;
static final int MINOR_VERSION = 3;
// access_flags
static final int ACC_PUBLIC = 0x0001;
static final int ACC_PRIVATE = 0x0002;
static final int ACC_PROTECTED = 0x0004;
static final int ACC_STATIC = 0x0008;
static final int ACC_FINAL = 0x0010;
static final int ACC_SYNCHRONIZED = 0x0020;
static final int ACC_THREADSAFE = 0x0040;
static final int ACC_TRANSIENT = 0x0080;
static final int ACC_NATIVE = 0x0100;
static final int ACC_INTERFACE = 0x0200;
static final int ACC_ABSTRACT = 0x0400;
// 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;
Indenter out = new Indenter();
Object[] cpool;
public void dump(String filename) throws Exception {
LoggingInputStream s = new LoggingInputStream(new FileInputStream(filename));
int magic = s.readInt("magic number");
if (magic != MAGIC)
throw new Exception("The file " + filename + " 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 Exception("The file " + filename +
" 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 ClassStruct(name_index);
break;
}
case CONSTANT_Fieldref: {
short class_index = s.readShort("Fieldref class_index");
short name_and_type_index = s.readShort("Fieldref name_and_type_index");
cpool[i] = new FieldrefStruct(class_index, name_and_type_index);
break;
}
case CONSTANT_Methodref: {
short class_index = s.readShort("Methodref class_index");
short name_and_type_index = s.readShort("Methodref name_and_type_index");
cpool[i] = new MethodrefStruct(class_index, name_and_type_index);
break;
}
case CONSTANT_InterfaceMethodref: {
short class_index = s.readShort("InterfaceMethodref class_index");
short name_and_type_index = s.readShort("InterfaceMethodref name_and_type_index");
cpool[i] = new InterfaceMethodrefStruct(class_index, name_and_type_index);
break;
}
case CONSTANT_String: {
short string_index = s.readShort("String string_index");
cpool[i] = new StringStruct(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 NameAndTypeStruct(name_index, signature_index);
break;
}
case CONSTANT_Asciz: {
cpool[i] = s.readUTF("Asciz value");
break;
}
default:
throw new Exception("Unrecognized constant pool type: " + type);
}
}
out.println("// DumpClass: " + filename);
short access_flags = s.readShort("access_flags");
if ((access_flags & ACC_PUBLIC) != 0)
out.print("public ");
if ((access_flags & ACC_FINAL) != 0)
out.print("final ");
if ((access_flags & ACC_INTERFACE) != 0)
out.print("interface ");
out.nl();
out.print("class ");
short this_class = s.readShort("this_class");
ClassStruct myClass = (ClassStruct)cpool[this_class];
if (myClass != null) printName(myClass.name_index);
else out.print("<unknown>");
out.print(" extends ");
short super_class = s.readShort("super_class");
ClassStruct mySuper = (ClassStruct)cpool[super_class];
if (mySuper != null) printName(mySuper.name_index);
else out.print("<unknown>");
short interfaces_count = s.readShort("interfaces_count");
if (interfaces_count != 0) {
out.nl();
out.print("\timplements ");
readInterface(s);
for (int i = 1; i < interfaces_count; i++) {
out.print(", ");
readInterface(s);
}
}
out.println(" {");
out.incr();
short fields_count = s.readShort("fields_count");
for (int i = 0; i < fields_count; i++) {
readField(s);
}
short methods_count = s.readShort("methods_count");
for (int i = 0; i < methods_count; i++) {
readMethod(s);
}
short attributes_count = s.readShort("attributes_count");
for (int i = 0; i < attributes_count; i++) {
readAttribute(s);
}
out.decr();
out.println("}");
}
void readInterface(LoggingInputStream s) throws IOException {
short index = s.readShort("Interface index");
printName(((ClassStruct)cpool[index]).name_index);
}
void readField(LoggingInputStream s) throws Exception {
short access_flags = s.readShort("Field access_flags");
short name_index = s.readShort("Field name_index");
short signature_index = s.readShort("Field signature_index");
short attributes_count = s.readShort("Field attributes_count");
if ((access_flags & ACC_PUBLIC) != 0) out.print("public ");
if ((access_flags & ACC_PRIVATE) != 0) out.print("private ");
if ((access_flags & ACC_PROTECTED) != 0) out.print("protected ");
if ((access_flags & ACC_STATIC) != 0) out.print("static ");
if ((access_flags & ACC_FINAL) != 0) out.print("final ");
if ((access_flags & ACC_THREADSAFE) != 0) out.print("threadsafe ");
if ((access_flags & ACC_TRANSIENT) != 0) out.print("transient ");
printName(name_index);
out.print(" : ");
printName(signature_index);
out.incr();
for (int i = 0; i < attributes_count; i++) {
readAttribute(s);
}
out.decr();
out.nl();
}
void readMethod(LoggingInputStream s) throws Exception {
short access_flags = s.readShort("Method access_flags");
short name_index = s.readShort("Method name_index");
short signature_index = s.readShort("Method signature_index");
short attributes_count = s.readShort("Method attributes_count");
if ((access_flags & ACC_PUBLIC) != 0) out.print("public ");
if ((access_flags & ACC_PRIVATE) != 0) out.print("private ");
if ((access_flags & ACC_PROTECTED) != 0) out.print("protected ");
if ((access_flags & ACC_STATIC) != 0) out.print("static ");
if ((access_flags & ACC_FINAL) != 0) out.print("final ");
if ((access_flags & ACC_SYNCHRONIZED) != 0) out.print("synchronized ");
if ((access_flags & ACC_NATIVE) != 0) out.print("native ");
if ((access_flags & ACC_ABSTRACT) != 0) out.print("abstract ");
printName(name_index);
printName(signature_index);
out.nl();
out.incr();
for (int i = 0; i < attributes_count; i++) {
readAttribute(s);
}
out.decr();
}
void readAttribute(LoggingInputStream s) throws Exception {
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".equals(name)) {
short sourcefile_index = s.readShort("Attribute SourceFile sourcefile_index");
out.print("// Source file: ");
printName(sourcefile_index);
out.nl();
}
else if ("ConstantValue".equals(name)) {
short constantvalue_index = s.readShort("Attribute ConstantValue constantvalue_index");
out.print(" = " + cpool[constantvalue_index]);
}
else if ("Code".equals(name)) {
short max_stack = s.readShort("Attribute Code max_stack");
short max_locals = s.readShort("Attribute Code max_locals");
int code_length = s.readInt("Attribute Code code_length");
out.println("// Code: length: " + code_length +
", max_stack: " + max_stack +
", max_locals: " + max_locals);
byte[] code = new byte[code_length];
s.read(code, "Attribute Code bytecodes");
printCode(code);
short exception_table_length = s.readShort("Attribute Code exception_table_length");
if (exception_table_length != 0) {
out.println("// Exception handlers:");
for (int i = 0; i < exception_table_length; i++) {
short start_pc = s.readShort("Attribute Code ExceptionTable start_pc");
short end_pc = s.readShort("Attribute Code ExceptionTable end_pc");
short handler_pc = s.readShort("Attribute Code ExceptionTable handler_pc");
short catch_type = s.readShort("Attribute Code ExceptionTable catch_type");
out.print("// start_pc: " + start_pc +
", end_pc: " + end_pc +
", handler_pc: " + handler_pc +
", catch_type: ");
printName(catch_type);
out.nl();
}
}
short attributes_count = s.readShort("Attribute Code attributes_count");
for (int i = 0; i < attributes_count; i++) {
readAttribute(s);
}
}
else if ("LineNumberTable".equals(name)) {
short line_number_table_length = s.readShort("Attribute LineNumberTable line_number_table_length");
if (line_number_table_length != 0) {
out.println("// Line numbers:");
for (int i = 0; i < line_number_table_length; i++) {
short start_pc = s.readShort("Attribute LineNumberTable start_pc");
short line_number = s.readShort("Attribute LineNumberTable line_number");
out.println("// line_number: " + line_number +
" start_pc: " + start_pc);
}
}
}
else if ("LocalVariableTable".equals(name)) {
short local_variable_table_length = s.readShort("Attribute LocalVariableNumberTable local_variable_table_length");
if (local_variable_table_length != 0) {
out.println("// Local variables:");
for (int i = 0; i < local_variable_table_length; i++) {
short start_pc = s.readShort("Attribute LocalVariableNumberTable start_pc");
short length = s.readShort("Attribute LocalVariableNumberTable length");
short name_index = s.readShort("Attribute LocalVariableNumberTable name_index");
short signature_index = s.readShort("Attribute LocalVariableNumberTable signature_index");
short slot = s.readShort("Attribute LocalVariableNumberTable slot");
out.print("// ");
printName(name_index);
out.print(" : ");
printName(signature_index);
out.println("\t(start_pc: " + start_pc +
", length: " + length +
", slot: " + slot +
")");
}
}
}
else {
out.print("// Unrecognized attribute ");
printName(attribute_name_index);
out.println(", length: " + attribute_length);
s.skipBytes(attribute_length, "Unrecognized attribute");
}
}
void printName(int index) {
Object obj = cpool[index];
if (obj == null) out.print("null");
out.print(obj.toString());
}
void printCode(byte[] bytes) {
int i = 0;
int length = bytes.length;
for (i = 0; i < length; i++) {
byte b = bytes[i];
out.println("// " + i + ": " + b);
}
/*
int i = 0, j = 0, index = 0;
int length = bytes.length;
char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
while (true) {
out.print("// " + index + ":\t");
while (j++ < 4) {
byte b = bytes[index++];
System.out.print(hex[b >> 16]);
System.out.print(hex[b & 0xF]);
if (index == length) {
out.nl();
return;
}
}
j = 0;
i++;
out.nl();
}
*/
}
}
class Indenter {
int depth = 0;
boolean atStart = true;
public void incr() {
depth++;
}
public void decr() {
depth--;
}
public void nl() {
System.out.println("");
atStart = true;
}
public void print(String s) {
if (atStart) {
for (int i = 0; i < depth; i++)
System.out.print("\t");
atStart = false;
}
System.out.print(s);
}
public void println(String s) {
print(s);
nl();
}
}
class ConstantPoolStruct {
}
class ClassStruct extends ConstantPoolStruct {
public short name_index;
ClassStruct(short name_index) {
this.name_index = name_index;
}
}
class FieldrefStruct extends ConstantPoolStruct {
public short class_index;
public short name_and_type_index;
FieldrefStruct(short class_index, short name_and_type_index) {
this.class_index = class_index;
this.name_and_type_index = name_and_type_index;
}
}
class MethodrefStruct extends ConstantPoolStruct {
public short class_index;
public short name_and_type_index;
MethodrefStruct(short class_index, short name_and_type_index) {
this.class_index = class_index;
this.name_and_type_index = name_and_type_index;
}
}
class InterfaceMethodrefStruct extends ConstantPoolStruct {
public short class_index;
public short name_and_type_index;
InterfaceMethodrefStruct(short class_index, short name_and_type_index) {
this.class_index = class_index;
this.name_and_type_index = name_and_type_index;
}
}
class StringStruct extends ConstantPoolStruct {
public short string_index;
StringStruct(short string_index) {
this.string_index = string_index;
}
}
class NameAndTypeStruct extends ConstantPoolStruct {
public short name_index;
public short signature_index;
NameAndTypeStruct(short name_index, short signature_index) {
this.name_index = name_index;
this.signature_index = signature_index;
}
}
class LoggingInputStream {
public boolean trace = false;
DataInputStream s;
LoggingInputStream(InputStream s) {
super();
this.s = new DataInputStream(s);
}
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 int read(byte[] b, String reason) throws IOException {
if (trace) System.err.print("# reading byte[" + b.length + "] for " + reason);
int result = s.read(b);
if (trace) System.err.println(" => " + result);
return result;
}
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;
}
}