blob: e45cd39d3e7ffaf6e96cef518a0932beb8eea67e [file] [log] [blame]
/* Copyright 2006 aQute SARL
* Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
package aQute.libg.classdump;
import java.io.*;
import java.lang.reflect.*;
public class ClassDumper {
/**
* <pre>
* ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its
* package.
* ACC_FINAL 0x0010 Declared final; no subclasses allowed.
* ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
* invokespecial instruction.
* ACC_INTERFACE 0x0200 Is an interface, not a
* class.
* ACC_ABSTRACT 0x0400 Declared abstract; may not be instantiated.
* </pre>
*
* @param mod
*/
final static int ACC_PUBLIC = 0x0001; // Declared public; may be accessed
// from outside its package.
final static int ACC_FINAL = 0x0010; // Declared final; no subclasses
// allowed.
final static int ACC_SUPER = 0x0020; // Treat superclass methods
// specially when invoked by the
// invokespecial instruction.
final static int ACC_INTERFACE = 0x0200; // Is an interface, not a classs
final static int ACC_ABSTRACT = 0x0400; // Declared abstract; may not be
// instantiated.
final static class Assoc {
Assoc(byte tag, int a, int b) {
this.tag = tag;
this.a = a;
this.b = b;
}
byte tag;
int a;
int b;
}
final String path;
final static String NUM_COLUMN = "%-30s %d\n";
final static String HEX_COLUMN = "%-30s %x\n";
final static String STR_COLUMN = "%-30s %s\n";
PrintStream ps = System.err;
Object[] pool;
InputStream in;
public ClassDumper(String path) throws Exception {
this(path, new FileInputStream(new File(path)));
}
public ClassDumper(String path, InputStream in) throws IOException {
this.path = path;
this.in = in;
}
public void dump(PrintStream ps) throws Exception {
if (ps != null)
this.ps = ps;
DataInputStream din = new DataInputStream(in);
parseClassFile(din);
din.close();
}
void parseClassFile(DataInputStream in) throws IOException {
int magic = in.readInt();
if (magic != 0xCAFEBABE)
throw new IOException("Not a valid class file (no CAFEBABE header)");
ps.printf(HEX_COLUMN, "magic", magic);
int minor = in.readUnsignedShort(); // minor version
int major = in.readUnsignedShort(); // major version
ps.printf(STR_COLUMN, "version", "" + major + "." + minor);
int pool_size = in.readUnsignedShort();
ps.printf(NUM_COLUMN, "pool size", pool_size);
pool = new Object[pool_size];
process: for (int poolIndex = 1; poolIndex < pool_size; poolIndex++) {
byte tag = in.readByte();
switch (tag) {
case 0:
ps.printf("%30d tag (0)\n", poolIndex);
break process;
case 1:
String name = in.readUTF();
pool[poolIndex] = name;
ps.printf("%30d tag(1) utf8 '%s'\n", poolIndex, name);
break;
case 2:
throw new IOException("Invalid tag " + tag);
case 3:
int i = in.readInt();
pool[poolIndex] = Integer.valueOf(i);
ps.printf("%30d tag(3) int %s\n", poolIndex, i);
break;
case 4:
float f = in.readFloat();
pool[poolIndex] = new Float(f);
ps.printf("%30d tag(4) float %s\n", poolIndex, f);
break;
// For some insane optimization reason are
// the long and the double two entries in the
// constant pool. See 4.4.5
case 5:
long l = in.readLong();
pool[poolIndex] = Long.valueOf(l);
ps.printf("%30d tag(5) long %s\n", poolIndex, l);
poolIndex++;
break;
case 6:
double d = in.readDouble();
pool[poolIndex] = new Double(d);
ps.printf("%30d tag(6) double %s\n", poolIndex, d);
poolIndex++;
break;
case 7:
int class_index = in.readUnsignedShort();
pool[poolIndex] = Integer.valueOf(class_index);
ps.printf("%30d tag(7) constant classs %d\n", poolIndex,
class_index);
break;
case 8:
int string_index = in.readUnsignedShort();
pool[poolIndex] = Integer.valueOf(string_index);
ps.printf("%30d tag(8) constant string %d\n", poolIndex,
string_index);
break;
case 9: // Field ref
class_index = in.readUnsignedShort();
int name_and_type_index = in.readUnsignedShort();
pool[poolIndex] = new Assoc((byte) 9, class_index,
name_and_type_index);
ps.printf("%30d tag(9) field ref %d/%d\n", poolIndex,
class_index, name_and_type_index);
break;
case 10: // Method ref
class_index = in.readUnsignedShort();
name_and_type_index = in.readUnsignedShort();
pool[poolIndex] = new Assoc((byte) 10, class_index,
name_and_type_index);
ps.printf("%30d tag(10) method ref %d/%d\n", poolIndex,
class_index, name_and_type_index);
break;
case 11: // Interface and Method ref
class_index = in.readUnsignedShort();
name_and_type_index = in.readUnsignedShort();
pool[poolIndex] = new Assoc((byte) 11, class_index,
name_and_type_index);
ps.printf("%30d tag(11) interface and method ref %d/%d\n",
poolIndex, class_index, name_and_type_index);
break;
// Name and Type
case 12:
int name_index = in.readUnsignedShort();
int descriptor_index = in.readUnsignedShort();
pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
ps.printf("%30d tag(12) name and type %d/%d\n", poolIndex,
name_index, descriptor_index);
break;
default:
throw new IllegalArgumentException("Unknown tag: " + tag);
}
}
int access = in.readUnsignedShort(); // access
printAccess(access);
int this_class = in.readUnsignedShort();
int super_class = in.readUnsignedShort();
ps.printf("%-30s %x %s(#%d)\n", "this_class", access, pool[this_class],
this_class);
ps.printf("%-30s %s(#%d)\n", "super_class", pool[super_class],
super_class);
int interfaces_count = in.readUnsignedShort();
ps.printf(NUM_COLUMN, "interface count", interfaces_count);
for (int i = 0; i < interfaces_count; i++) {
int interface_index = in.readUnsignedShort();
ps.printf("%-30s interface %s(#%d)", "interface count",
pool[interface_index], interfaces_count);
}
int field_count = in.readUnsignedShort();
ps.printf(NUM_COLUMN, "field count", field_count);
for (int i = 0; i < field_count; i++) {
access = in.readUnsignedShort(); // access
printAccess(access);
int name_index = in.readUnsignedShort();
int descriptor_index = in.readUnsignedShort();
ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "field def", access,
pool[name_index], name_index, pool[descriptor_index],
descriptor_index);
doAttributes(in, " ");
}
int method_count = in.readUnsignedShort();
ps.printf(NUM_COLUMN, "method count", method_count);
for (int i = 0; i < method_count; i++) {
int access_flags = in.readUnsignedShort();
printAccess(access_flags);
int name_index = in.readUnsignedShort();
int descriptor_index = in.readUnsignedShort();
ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "method def", access_flags,
pool[name_index], name_index, pool[descriptor_index],
descriptor_index);
doAttributes(in, " ");
}
doAttributes(in, "");
if (in.read() >= 0)
ps.printf("Extra bytes follow ...");
}
/**
* Called for each attribute in the class, field, or method.
*
* @param in
* The stream
* @throws IOException
*/
private void doAttributes(DataInputStream in, String indent)
throws IOException {
int attribute_count = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "attribute count", attribute_count);
for (int j = 0; j < attribute_count; j++) {
doAttribute(in, indent + j + ": ");
}
}
/**
* Process a single attribute, if not recognized, skip it.
*
* @param in
* the data stream
* @throws IOException
*/
private void doAttribute(DataInputStream in, String indent)
throws IOException {
int attribute_name_index = in.readUnsignedShort();
long attribute_length = in.readInt();
attribute_length &= 0xFFFF;
String attributeName = (String) pool[attribute_name_index];
ps.printf("%-30s %s(#%d)\n", indent + "attribute", attributeName,
attribute_name_index);
if ("RuntimeVisibleAnnotations".equals(attributeName))
doAnnotations(in, indent);
else if ("SourceFile".equals(attributeName))
doSourceFile(in, indent);
else if ("Code".equals(attributeName))
doCode(in, indent);
else if ("LineNumberTable".equals(attributeName))
doLineNumberTable(in, indent);
else if ("LocalVariableTable".equals(attributeName))
doLocalVariableTable(in, indent);
else if ("InnerClasses".equals(attributeName))
doInnerClasses(in, indent);
else if ("Exceptions".equals(attributeName))
doExceptions(in, indent);
else if ("EnclosingMethod".equals(attributeName))
doEnclosingMethod(in, indent);
else if ("Signature".equals(attributeName))
doSignature(in, indent);
else if ("Synthetic".equals(attributeName))
; // Is empty!
else if ("Deprecated".equals(attributeName))
; // Is Empty
else {
ps.printf("%-30s %d\n", indent + "Unknown attribute, skipping",
attribute_length);
if (attribute_length > 0x7FFFFFFF) {
throw new IllegalArgumentException("Attribute > 2Gb");
}
byte buffer[] = new byte[(int) attribute_length];
in.readFully(buffer);
printHex(buffer);
}
}
/**
* <pre>
* Signature_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 signature_index;
* }
* </pre>
*
* @param in
* @param indent
*/
void doSignature(DataInputStream in, String indent) throws IOException {
int signature_index = in.readUnsignedShort();
ps.printf("%-30s %s(#%d)\n", indent + "signature",
pool[signature_index], signature_index);
}
/**
* <pre>
* EnclosingMethod_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 class_index
* u2 method_index;
* }
*
* </pre>
*/
void doEnclosingMethod(DataInputStream in, String indent)
throws IOException {
int class_index = in.readUnsignedShort();
int method_index = in.readUnsignedShort();
ps.printf("%-30s %s(#%d/c) %s\n", //
indent + "enclosing method", //
pool[((Integer) pool[class_index]).intValue()], //
class_index, //
(method_index == 0 ? "<>" : pool[method_index]));
}
/**
* <pre>
* Exceptions_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_exceptions;
* u2 exception_index_table[number_of_exceptions];
* }
* </pre>
*
* @param in
* @param indent
*/
private void doExceptions(DataInputStream in, String indent)
throws IOException {
int number_of_exceptions = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "number of exceptions",
number_of_exceptions);
StringBuilder sb = new StringBuilder();
String del = "";
for (int i = 0; i < number_of_exceptions; i++) {
int exception_index_table = in.readUnsignedShort();
sb.append(del);
sb.append(pool[((Integer) pool[exception_index_table])]);
sb.append("(#");
sb.append(exception_index_table);
sb.append("/c)");
del = ", ";
}
ps.printf("%-30s %d: %s\n", indent + "exceptions",
number_of_exceptions, sb);
}
/**
* <pre>
* Code_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 max_stack;
* u2 max_locals;
* u4 code_length;
* u1 code[code_length];
* u2 exception_table_length;
* { u2 start_pc;
* u2 end_pc;
* u2 handler_pc;
* u2 catch_type;
* } exception_table[exception_table_length];
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
*
* @param in
* @param pool
* @throws IOException
*/
private void doCode(DataInputStream in, String indent) throws IOException {
int max_stack = in.readUnsignedShort();
int max_locals = in.readUnsignedShort();
int code_length = in.readInt();
ps.printf(NUM_COLUMN, indent + "max_stack", max_stack);
ps.printf(NUM_COLUMN, indent + "max_locals", max_locals);
ps.printf(NUM_COLUMN, indent + "code_length", code_length);
byte code[] = new byte[code_length];
in.readFully(code);
printHex(code);
int exception_table_length = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "exception_table_length",
exception_table_length);
for (int i = 0; i < exception_table_length; i++) {
int start_pc = in.readUnsignedShort();
int end_pc = in.readUnsignedShort();
int handler_pc = in.readUnsignedShort();
int catch_type = in.readUnsignedShort();
ps.printf("%-30s %d/%d/%d/%d\n", indent + "exception_table",
start_pc, end_pc, handler_pc, catch_type);
}
doAttributes(in, indent + " ");
}
/**
* We must find Class.forName references ...
*
* @param code
*/
protected void printHex(byte[] code) {
int index = 0;
while (index < code.length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16 && index < code.length; i++) {
String s = Integer.toHexString((0xFF & code[index++]))
.toUpperCase();
if (s.length() == 1)
sb.append("0");
sb.append(s);
sb.append(" ");
}
ps.printf(STR_COLUMN, "", sb.toString());
}
}
private void doSourceFile(DataInputStream in, String indent)
throws IOException {
int sourcefile_index = in.readUnsignedShort();
ps.printf("%-30s %s(#%d)\n", indent + "Source file",
pool[sourcefile_index], sourcefile_index);
}
private void doAnnotations(DataInputStream in, String indent)
throws IOException {
int num_annotations = in.readUnsignedShort(); // # of annotations
ps
.printf(NUM_COLUMN, indent + "Number of annotations",
num_annotations);
for (int a = 0; a < num_annotations; a++) {
doAnnotation(in, indent);
}
}
private void doAnnotation(DataInputStream in, String indent)
throws IOException {
int type_index = in.readUnsignedShort();
ps.printf("%-30s %s(#%d)", indent + "type", pool[type_index],
type_index);
int num_element_value_pairs = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "num_element_value_pairs",
num_element_value_pairs);
for (int v = 0; v < num_element_value_pairs; v++) {
int element_name_index = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "element_name_index",
element_name_index);
doElementValue(in, indent);
}
}
private void doElementValue(DataInputStream in, String indent)
throws IOException {
int tag = in.readUnsignedByte();
switch (tag) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 's':
int const_value_index = in.readUnsignedShort();
ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag,
pool[const_value_index], const_value_index);
break;
case 'e':
int type_name_index = in.readUnsignedShort();
int const_name_index = in.readUnsignedShort();
ps.printf("%-30s %c %s(#%d) %s(#%d)\n", indent + "type+const", tag,
pool[type_name_index], type_name_index,
pool[const_name_index], const_name_index);
break;
case 'c':
int class_info_index = in.readUnsignedShort();
ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag,
pool[class_info_index], class_info_index);
break;
case '@':
ps.printf("%-30s %c\n", indent + "sub annotation", tag);
doAnnotation(in, indent);
break;
case '[':
int num_values = in.readUnsignedShort();
ps.printf("%-30s %c num_values=%d\n", indent + "sub element value",
tag, num_values);
for (int i = 0; i < num_values; i++) {
doElementValue(in, indent);
}
break;
default:
throw new IllegalArgumentException(
"Invalid value for Annotation ElementValue tag " + tag);
}
}
/**
* <pre>
* LineNumberTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 line_number_table_length;
* { u2 start_pc;
* u2 line_number;
* } line_number_table[line_number_table_length];
* }
*
* </pre>
*/
void doLineNumberTable(DataInputStream in, String indent)
throws IOException {
int line_number_table_length = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "line number table length",
line_number_table_length);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < line_number_table_length; i++) {
int start_pc = in.readUnsignedShort();
int line_number = in.readUnsignedShort();
sb.append(start_pc);
sb.append("/");
sb.append(line_number);
sb.append(" ");
}
ps.printf("%-30s %d: %s\n", indent + "line number table",
line_number_table_length, sb);
}
/**
*
* <pre>
* LocalVariableTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 local_variable_table_length;
* { u2 start_pc;
* u2 length;
* u2 name_index;
* u2 descriptor_index;
* u2 index;
* } local_variable_table[local_variable_table_length];
* }
* </pre>
*/
void doLocalVariableTable(DataInputStream in, String indent)
throws IOException {
int local_variable_table_length = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "local variable table length",
local_variable_table_length);
for (int i = 0; i < local_variable_table_length; i++) {
int start_pc = in.readUnsignedShort();
int length = in.readUnsignedShort();
int name_index = in.readUnsignedShort();
int descriptor_index = in.readUnsignedShort();
int index = in.readUnsignedShort();
ps.printf("%-30s %d: %d/%d %s(#%d) %s(#%d)\n", indent, index,
start_pc, length, pool[name_index], name_index,
pool[descriptor_index], descriptor_index);
}
}
/**
* <pre>
* InnerClasses_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_classes;
* { u2 inner_class_info_index;
* u2 outer_class_info_index;
* u2 inner_name_index;
* u2 inner_class_access_flags;
* } classes[number_of_classes];
* }
* </pre>
*
*/
void doInnerClasses(DataInputStream in, String indent) throws IOException {
int number_of_classes = in.readUnsignedShort();
ps.printf(NUM_COLUMN, indent + "number of classes", number_of_classes);
for (int i = 0; i < number_of_classes; i++) {
int inner_class_info_index = in.readUnsignedShort();
int outer_class_info_index = in.readUnsignedShort();
int inner_name_index = in.readUnsignedShort();
int inner_class_access_flags = in.readUnsignedShort();
printAccess(inner_class_access_flags);
String iname = "<>";
String oname = iname;
if (inner_class_info_index != 0)
iname = (String) pool[((Integer) pool[inner_class_info_index])
.intValue()];
if (outer_class_info_index != 0)
oname = (String) pool[((Integer) pool[outer_class_info_index])
.intValue()];
ps.printf("%-30s %d: %x %s(#%d/c) %s(#%d/c) %s(#%d) \n", indent, i,
inner_class_access_flags, iname, inner_class_info_index,
oname, outer_class_info_index, pool[inner_name_index],
inner_name_index);
}
}
void printClassAccess(int mod) {
ps.printf("%-30s", "Class Access");
if ((ACC_PUBLIC&mod)!= 0)
ps.print(" public");
if ((ACC_FINAL&mod)!= 0)
ps.print(" final");
if ((ACC_SUPER&mod)!= 0)
ps.print(" super");
if ((ACC_INTERFACE&mod)!= 0)
ps.print(" interface");
if ((ACC_ABSTRACT&mod)!= 0)
ps.print(" abstract");
ps.println();
}
void printAccess(int mod) {
ps.printf("%-30s", "Access");
if (Modifier.isStatic(mod))
ps.print(" static");
if (Modifier.isAbstract(mod))
ps.print(" abstract");
if (Modifier.isPublic(mod))
ps.print(" public");
if (Modifier.isFinal(mod))
ps.print(" final");
if (Modifier.isInterface(mod))
ps.print(" interface");
if (Modifier.isNative(mod))
ps.print(" native");
if (Modifier.isPrivate(mod))
ps.print(" private");
if (Modifier.isProtected(mod))
ps.print(" protected");
if (Modifier.isStrict(mod))
ps.print(" strict");
if (Modifier.isSynchronized(mod))
ps.print(" synchronized");
if (Modifier.isTransient(mod))
ps.print(" transient");
if (Modifier.isVolatile(mod))
ps.print(" volatile");
ps.println();
}
public static void main(String args[]) throws Exception {
if (args.length == 0) {
System.err.println("clsd <class file>+");
}
for (int i = 0; i < args.length; i++) {
File f = new File(args[i]);
if (!f.isFile())
System.err.println("File does not exist or is directory " + f);
else {
ClassDumper cd = new ClassDumper(args[i]);
cd.dump(null);
}
}
}
}