/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim.tools;
import static kilim.analysis.Utils.dedent;
import static kilim.analysis.Utils.indent;
import static kilim.analysis.Utils.p;
import static kilim.analysis.Utils.pn;
import static kilim.analysis.Utils.resetIndentation;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.Formatter;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import kilim.analysis.TypeDesc;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Equivalent to javap -c -l -private, but the output is in jasmin's format
* meant to be parseable by Asm.
* @author sriram
*/
public class DumpClass extends ClassVisitor implements Opcodes {
static boolean lineNumbers = true;
public static void main(String[] args) throws IOException {
String name = args.length == 2 ? args[1] : args[0];
if (name.endsWith(".jar")) {
try {
Enumeration<JarEntry> e = new JarFile(name).entries();
while (e.hasMoreElements()) {
ZipEntry en = (ZipEntry) e.nextElement();
String n = en.getName();
if (!n.endsWith(".class")) continue;
n = n.substring(0, n.length() - 6).replace('/','.');
new DumpClass(n);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
new DumpClass(name);
}
}
public DumpClass(InputStream is) throws IOException {
super(ASM4);
ClassReader cr = new ClassReader(is);
cr.accept(this, /*flags*/ 0);
}
public DumpClass(String className) throws IOException {
super(ASM4);
ClassReader cr;
if (className.endsWith(".class")) {
FileInputStream fis = new FileInputStream(className);
cr = new ClassReader(fis);
} else {
cr = new ClassReader(className);
}
cr.accept(this, /*flags*/ClassReader.EXPAND_FRAMES);
}
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces)
{
p(".class ");
p(Modifier.toString(access));
p(" ");
pn(name);
if (superName != null)
pn(".super " + superName);
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
p(".implements ");
pn(interfaces[i]);
}
}
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
pn(".annotation " + (visible ? "visible " : "") + desc);
pn(".end annotation");
return new DummyAnnotationVisitor();
}
public void visitAttribute(Attribute attr) {}
public void visitEnd() {}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
p(".field ");
p(Modifier.toString(access));
p(" ");
p(name);
p(" ");
p(desc);
if (value != null) {
p(" = ");
if (value instanceof String) {
pn("\"" + value + "\"");
} else {
pn(value.toString());
}
} else {
pn();
}
return null;
}
public void visitInnerClass(String name, String outerName, String innerName, int access) {
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
pn("");
pn("; -------------------------------------------------------------");
p(".method ");
p(Modifier.toString(access));
p(" ");
p(name);
pn(desc);
pn("; signature = " + signature);
pn("; -------------------------------------------------------------\n");
if (exceptions != null) {
for (int i = 0; i < exceptions.length; i++) {
p(".throws ");
pn(exceptions[i]);
}
}
return new DumpMethodVisitor();
}
public void visitOuterClass(String owner, String name, String desc) {
}
public void visitSource(String source, String debug) {}
}
class DummyAnnotationVisitor extends AnnotationVisitor {
public DummyAnnotationVisitor() {
super(Opcodes.ASM4);
// TODO Auto-generated constructor stub
}
public void visit(String name, Object value) {
// System.out.println("visit: name = " + name + ", value = " + value);
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
// System.out.println("visitAnnotation: name = " + name + ", desc = " + desc);
return this;
}
public AnnotationVisitor visitArray(String name) {
// System.out.println("visitArray: name = " + name);
return this;
}
public void visitEnd() {
// System.out.println("visitEnd");
}
public void visitEnum(String name, String desc, String value) {
// System.out.println("visitEnum: " + name + ", desc = " + desc + ", value = " + value);
}
}
class DumpMethodVisitor extends MethodVisitor implements Opcodes {
public DumpMethodVisitor() {
super(Opcodes.ASM4);
}
static String[] os = {
"nop","aconst_null","iconst_m1","iconst_0","iconst_1","iconst_2",
"iconst_3","iconst_4","iconst_5","lconst_0","lconst_1","fconst_0",
"fconst_1","fconst_2","dconst_0","dconst_1","bipush","sipush",
"ldc","ldc_w","ldc_w","iload","lload","fload","dload","aload",
"iload_0","iload_1","iload_2","iload_3","lload_0","lload_1","lload_2",
"lload_3","fload_0","fload_1","fload_2","fload_3","dload_0","dload_1",
"dload_2","dload_3","aload_0","aload_1","aload_2","aload_3","iaload",
"laload","faload","daload","aaload","baload","caload","saload",
"istore","lstore","fstore","dstore","astore","istore_0","istore_1",
"istore_2","istore_3","lstore_0","lstore_1","lstore_2","lstore_3",
"fstore_0","fstore_1","fstore_2","fstore_3","dstore_0","dstore_1",
"dstore_2","dstore_3","astore_0","astore_1","astore_2","astore_3",
"iastore","lastore","fastore","dastore","aastore","bastore",
"castore","sastore","pop","pop2","dup","dup_x1","dup_x2","dup2",
"dup2_x1","dup2_x2","swap","iadd","ladd","fadd","dadd","isub",
"lsub","fsub","dsub","imul","lmul","fmul","dmul","idiv","ldiv",
"fdiv","ddiv","irem","lrem","frem","drem","ineg","lneg","fneg","dneg",
"ishl","lshl","ishr","lshr","iushr","lushr","iand","land","ior","lor",
"ixor","lxor","iinc","i2l","i2f","i2d","l2i","l2f","l2d","f2i","f2l",
"f2d","d2i","d2l","d2f","i2b","i2c","i2s","lcmp","fcmpl","fcmpg",
"dcmpl","dcmpg","ifeq","ifne","iflt","ifge","ifgt","ifle","if_icmpeq",
"if_icmpne","if_icmplt","if_icmpge","if_icmpgt","if_icmple","if_acmpeq",
"if_acmpne","goto","jsr","ret","tableswitch","lookupswitch","ireturn",
"lreturn","freturn","dreturn","areturn","return","getstatic",
"putstatic","getfield","putfield","invokevirtual","invokespecial",
"invokestatic","invokeinterface","unused","new","newarray","anewarray",
"arraylength","athrow","checkcast","instanceof","monitorenter",
"monitorexit","wide","multianewarray","ifnull","ifnonnull","goto_w","jsr_w"
};
int line = 0;
static StringBuilder fsb = new StringBuilder(100);
static Formatter formatter = new Formatter(fsb);
public void ppn(String s) {
if (DumpClass.lineNumbers) {
fsb.setLength(0);
formatter.format("%-70s ; %d", s, (line++));
pn(fsb.toString());
} else {
pn(s);
}
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
ppn(os[opcode] + " " + owner + "/" + name + " " + desc);
}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
pn("; Frame " + type);
p ("; Locals - ");
for(int i = 0; i < nLocal; i++) {
Object o = local[i];
System.out.print("#" + i + "." + type(o) + " ");
}
System.out.println();
p("; Stack - ");
for(int i = 0; i < nStack; i++) {
Object o = stack[i];
System.out.print("#" + i + "." + type(o) + " ");
}
System.out.println("");
}
private String type(Object o) {
if (o == null) {
return "null";
} else if (o instanceof Integer) {
switch (((Integer)o).intValue()) {
case 0: return "Top";
case 1: return "Integer";
case 2: return "Float";
case 3: return "Double";
case 4: return "Long";
case 5: return "Null";
case 6: return "Uninitialized_This"; }
} else if (o instanceof String) {
return (String)o;
}
return "??UNKNOWN??" + o.getClass() + ":" + o;
}
public void visitIincInsn(int var, int increment) {
ppn("iinc " + var + " " + increment);
}
public void visitInsn(int opcode) {
ppn(os[opcode]);
}
public void visitIntInsn(int opcode, int operand) {
if (opcode == NEWARRAY) {
String t = "UNDEFINED";
switch (operand) {
case T_BOOLEAN: t = " boolean"; break;
case T_CHAR: t = " char"; break;
case T_FLOAT: t = " float"; break;
case T_DOUBLE: t = " double"; break;
case T_BYTE: t = " byte"; break;
case T_SHORT: t = " short"; break;
case T_INT: t = " int"; break;
case T_LONG: t = " long"; break;
}
ppn(os[opcode] + t);
} else {
ppn(os[opcode] + " " +operand);
}
}
public void visitJumpInsn(int opcode, Label label) {
ppn(os[opcode] + " " + lab(label));
}
public void visitLabel(Label label) {
dedent(2);
pn(lab(label) + ":");
indent(2);
}
public void visitLdcInsn(Object cst) {
String op = (cst instanceof Double) || (cst instanceof Long) ? "ldc2_w " : "ldc ";
String type = (cst instanceof String) ? "\"" + esc((String)cst) + "\"" : cst.toString();
ppn(op + type);
}
public void visitLineNumber(int line, Label start) {
pn(".line " + line);
}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
pn(".var " + index + " is "+ name + " " + desc + " from " + lab(start) + " to " + lab(end));
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
ppn("lookupswitch");
indent(4);
for (int i= 0; i < keys.length; i++) {
pn(keys[i] + ": " + lab(labels[i]));
}
pn("default: " + lab(dflt));
dedent(4);
}
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
String str = os[opcode] + " " + owner + "/" + name + desc;
if (opcode == INVOKEINTERFACE) {
ppn(str + ", " + (TypeDesc.getNumArgumentTypes(desc)+1));
} else {
ppn(str);
}
}
public void visitMultiANewArrayInsn(String desc, int dims) {
ppn("multinewarray " + desc + " " + dims) ;
}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
ppn("tableswitch " + min);
indent(4);
for (int i = min; i <= max; i++) {
pn(lab(labels[i - min]));
}
pn("default: " + lab(dflt));
dedent(4);
}
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
pn(".catch " + type + " from " + lab(start) + " to " + lab(end) +
" using " + lab(handler));
}
public void visitTypeInsn(int opcode, String desc) {
ppn(os[opcode] + " " +desc);
}
public void visitVarInsn(int opcode, int var) {
ppn(os[opcode] + " " + var);
}
HashMap<Label,String> labels = new HashMap<Label, String>();
int labCount = 1;
private String lab(Label label) {
String ret = labels.get(label);
if (ret == null) {
ret = "L"+ labCount++;
labels.put(label, ret);
}
return ret;
}
public AnnotationVisitor visitAnnotationDefault() {
return new DummyAnnotationVisitor();
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
pn(".annotation " + (visible ? "visible " : "") + desc);
pn(".end annotation");
return new DummyAnnotationVisitor();
}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return new DummyAnnotationVisitor();
}
public void visitAttribute(Attribute attr) {
}
public void visitCode() {
indent(4);
}
public void visitMaxs(int maxStack, int maxLocals) {
pn(".limit stack " + maxStack);
pn(".limit locals " + maxLocals);
}
public void visitEnd() {
resetIndentation();
pn(".end method");
}
private static String esc(String s) {
return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n");
}
}