sp-04/src/ch/usi/inf/sp/bytecode/Disassembler.java

266 lines
12 KiB
Java

package ch.usi.inf.sp.bytecode;
import java.io.FileInputStream;
import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.Printer;
/**
* A Disassembler can disassemble Java class files.
* It presents an output similar to javap -c.
* Given the name of the class file as a command line argument,
* it prints the name of the class, a list of all methods,
* and for each method, the list of all Java bytecode instructions.
* <p>
* The format of the disassembled bytecode includes the opcodes
* (in the form of mnemonics such as "ILOAD") and all the operands.
* Some operands can be printed as simple integers, while others have to be printed in a more understandable form
* (e.g. method or field names and descriptors).
* Operands of branch instructions are shown as an "id" of the targeted instruction.
* For this, all instructions of a method, including ASM's pseudo-instructions (LABEL, LINE, FRAME),
* are numbered, starting at 0.
* The instruction id allows you to look up the corresponding instruction object in the instruction list:
* AbstractInsnNode target = instructionList.get(targetId);
* <p>
* An example output:
*
* <pre>
* Class: ExampleClass
* ...
* Method: switchMethod2(I)I
* 0: // label
* 1: // line number information
* 2: ICONST_0
* 3: ISTORE 2
* 4: // label
* 5: // line number information
* 6: ILOAD 1
* 7: LOOKUPSWITCH 0: 8, 1000: 13, 2000: 18, default: 23
* 8: // label
* 9: // line number information
* 10: ICONST_0
* 11: ISTORE 2
* 12: GOTO 27
* 13: // label
* 14: // line number information
* 15: ICONST_1
* 16: ISTORE 2
* 17: GOTO 27
* 18: // label
* 19: // line number information
* 20: ICONST_2
* 21: ISTORE 2
* 22: GOTO 27
* 23: // label
* 24: // line number information
* 25: ICONST_M1
* 26: ISTORE 2
* 27: // label
* 28: // line number information
* 29: ILOAD 2
* 30: IRETURN
* 31: // label
* </pre>
*
* @author Matthias.Hauswirth@usi.ch
*/
public class Disassembler {
public static void main(final String[] args) throws Exception {
// create a ClassReader that loads the Java .class file specified as the command line argument
final String classFileName = args[0];
final ClassReader cr = new ClassReader(new FileInputStream(classFileName));
// create an empty ClassNode (in-memory representation of a class)
final ClassNode clazz = new ClassNode();
// have the ClassReader read the class file and populate the ClassNode with the corresponding information
cr.accept(clazz, 0);
// create a dumper and have it dump the given ClassNode
System.out.println(disassembleClass(clazz));
}
public static String disassembleClass(final ClassNode clazz) {
final StringBuffer sb = new StringBuffer("Class: ");
sb.append(clazz.name);
sb.append('\n');
// get the list of all methods in that class
final List<MethodNode> methods = clazz.methods;
for (int m = 0; m < methods.size(); m++) {
final MethodNode method = methods.get(m);
sb.append(disassembleMethod(method));
}
return sb.toString();
}
public static String disassembleMethod(final MethodNode method) {
final StringBuffer sb = new StringBuffer(" Method: ");
sb.append(method.name);
sb.append(method.desc);
sb.append('\n');
// get the list of all instructions in that method
final InsnList instructions = method.instructions;
for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode instruction = instructions.get(i);
sb.append(" ");
sb.append(Disassembler.disassembleInstruction(instruction, i, instructions));
sb.append('\n');
}
return sb.toString();
}
/**
* Hint: Check out org.objectweb.asm.MethodVisitor to determine which instructions (opcodes)
* have which instruction types (subclasses of AbstractInsnNode).
*
* @see org.objectweb.asm.MethodVisitor
* <p>
* E.g. the comment in org.objectweb.asm.MethodVisitor.visitIntInsn(int opcode, int operand)
* shows the list of all opcodes that are represented as instructions of type IntInsnNode.
* That list e.g. includes the BIPUSH opcode.
*/
public static String disassembleInstruction(final AbstractInsnNode instruction, final int i, final InsnList instructions) {
StringBuffer sb = new StringBuffer();
final int opcode = instruction.getOpcode();
final String mnemonic = opcode == -1 ? "" : Printer.OPCODES[instruction.getOpcode()];
sb.append(i + ":\t" + mnemonic + " ");
// There are different subclasses of AbstractInsnNode.
// AbstractInsnNode.getType() represents the subclass as an int.
// Note:
// to check the subclass of an instruction node, we can either use:
// if (instruction.getType()==AbstractInsnNode.LABEL)
// or we can use:
// if (instruction instanceof LabelNode)
// They give the same result, but the first one can be used in a switch statement.
switch (instruction.getType()) {
case AbstractInsnNode.LABEL:
// pseudo-instruction (branch or exception target)
sb.append("// label");
break;
case AbstractInsnNode.FRAME:
// pseudo-instruction (stack frame map)
sb.append("// stack frame map");
break;
case AbstractInsnNode.LINE:
// pseudo-instruction (line number information)
sb.append("// line number information");
case AbstractInsnNode.INSN:
// Opcodes: 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, IALOAD, LALOAD, FALOAD,
// DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, 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,
// I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B,
// I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN,
// FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW,
// MONITORENTER, or MONITOREXIT.
// zero operands, nothing to print
break;
case AbstractInsnNode.INT_INSN:
// Opcodes: NEWARRAY, BIPUSH, SIPUSH.
if (instruction.getOpcode() == Opcodes.NEWARRAY) {
// NEWARRAY
sb.append(Printer.TYPES[((IntInsnNode) instruction).operand]);
} else {
// BIPUSH or SIPUSH
sb.append(((IntInsnNode) instruction).operand);
}
break;
case AbstractInsnNode.JUMP_INSN:
// Opcodes: IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ,
// IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ,
// IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
{
final LabelNode targetInstruction = ((JumpInsnNode) instruction).label;
final int targetId = instructions.indexOf(targetInstruction);
sb.append(targetId);
break;
}
case AbstractInsnNode.LDC_INSN:
// Opcodes: LDC.
sb.append(((LdcInsnNode) instruction).cst);
break;
case AbstractInsnNode.IINC_INSN:
// Opcodes: IINC.
sb.append(((IincInsnNode) instruction).var);
sb.append(" ");
sb.append(((IincInsnNode) instruction).incr);
break;
case AbstractInsnNode.TYPE_INSN:
// Opcodes: NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
sb.append(((TypeInsnNode) instruction).desc);
break;
case AbstractInsnNode.VAR_INSN:
// Opcodes: ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE,
// LSTORE, FSTORE, DSTORE, ASTORE or RET.
sb.append(((VarInsnNode) instruction).var);
break;
case AbstractInsnNode.FIELD_INSN:
// Opcodes: GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
sb.append(((FieldInsnNode) instruction).owner);
sb.append(".");
sb.append(((FieldInsnNode) instruction).name);
sb.append(" ");
sb.append(((FieldInsnNode) instruction).desc);
break;
case AbstractInsnNode.METHOD_INSN:
// Opcodes: INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC,
// INVOKEINTERFACE or INVOKEDYNAMIC.
sb.append(((MethodInsnNode) instruction).owner);
sb.append(".");
sb.append(((MethodInsnNode) instruction).name);
sb.append(" ");
sb.append(((MethodInsnNode) instruction).desc);
break;
case AbstractInsnNode.MULTIANEWARRAY_INSN:
// Opcodes: MULTIANEWARRAY.
sb.append(((MultiANewArrayInsnNode) instruction).desc);
sb.append(" ");
sb.append(((MultiANewArrayInsnNode) instruction).dims);
break;
case AbstractInsnNode.LOOKUPSWITCH_INSN:
// Opcodes: LOOKUPSWITCH.
{
final List keys = ((LookupSwitchInsnNode) instruction).keys;
final List labels = ((LookupSwitchInsnNode) instruction).labels;
for (int t = 0; t < keys.size(); t++) {
final int key = (Integer) keys.get(t);
final LabelNode targetInstruction = (LabelNode) labels.get(t);
final int targetId = instructions.indexOf(targetInstruction);
sb.append(key + ": " + targetId + ", ");
}
final LabelNode defaultTargetInstruction = ((LookupSwitchInsnNode) instruction).dflt;
final int defaultTargetId = instructions.indexOf(defaultTargetInstruction);
sb.append("default: " + defaultTargetId);
break;
}
case AbstractInsnNode.TABLESWITCH_INSN:
// Opcodes: TABLESWITCH.
{
final int minKey = ((TableSwitchInsnNode) instruction).min;
final List labels = ((TableSwitchInsnNode) instruction).labels;
for (int t = 0; t < labels.size(); t++) {
final int key = minKey + t;
final LabelNode targetInstruction = (LabelNode) labels.get(t);
final int targetId = instructions.indexOf(targetInstruction);
sb.append(key + ": " + targetId + ", ");
}
final LabelNode defaultTargetInstruction = ((TableSwitchInsnNode) instruction).dflt;
final int defaultTargetId = instructions.indexOf(defaultTargetInstruction);
sb.append("default: " + defaultTargetId);
break;
}
}
return sb.toString();
}
}