/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.bytecode; import java.io.*; import com.sun.cri.bytecode.*; import com.sun.max.program.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.type.*; /** * A utility for disassembling bytecode. */ public class BytecodePrinter extends BytecodeVisitor { /** * A constant denoting that the raw bytes of a bytecode instruction are to be included as a suffix of the * instruction's disassembly. */ public static final int PRINT_BYTES = 0x00000001; /** * A constant denoting that the string "cp[i]:" is to be prefixed to the disassembly of an instruction operand that * is a constant pool index. */ public static final int PRINT_CONSTANT_POOL_INDICES = 0x00000002; private static final int VALID_FLAGS_MASK = PRINT_BYTES | PRINT_CONSTANT_POOL_INDICES; /** * Disassembles a bytecode instruction stream and returns the disassembly as a string. Each disassembled instruction * will be suffixed by the {@linkplain #PRINT_BYTES raw bytes} of the instruction followed by the platform specific * new line character(s). * * @param constantPool the constant pool referred by instructions in the bytecode instruction stream * @param bytecodeBlock contains the bytecode stream to be disassembled * @return the disassembly of the stream denoted by {@code bytecodeBlock} or "" if there was an error during * disassembly */ public static String toString(ConstantPool constantPool, BytecodeBlock bytecodeBlock) { return toString(constantPool, bytecodeBlock, "", "\n", PRINT_BYTES); } /** * Disassembles a bytecode instruction stream and returns the disassembly as a string. * * @param constantPool the constant pool referred by instructions in the bytecode instruction stream * @param bytecodeBlock contains the bytecode stream to be disassembled * @param instructionPrefix the string to be written to the stream before each disassembled instruction * @param instructionSuffix the string to be written to the stream after each disassembled instruction * @param flags a mask composed of zero or more of the following constants describing extra information to be * included for each disassembled instruction: {@value #PRINT_BYTES}, * {@value #PRINT_CONSTANT_POOL_INDICES} * @return the disassembly of the stream denoted by {@code bytecodeBlock} or "" if there was an error during * disassembly */ public static String toString(ConstantPool constantPool, BytecodeBlock bytecodeBlock, String instructionPrefix, String instructionSuffix, int flags) { final ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { final PrintWriter writer = new PrintWriter(stream); final BytecodePrinter bytecodePrinter = new BytecodePrinter(writer, constantPool, instructionPrefix, instructionSuffix, flags); final BytecodeScanner bytecodeScanner = new BytecodeScanner(bytecodePrinter); bytecodeScanner.scan(bytecodeBlock); writer.flush(); return stream.toString(); } catch (Throwable throwable) { ProgramWarning.message("could not print bytecodes: " + throwable); return ""; } } private final PrintWriter writer; private final ConstantPool constantPool; private LineNumberTable lineNumberTable; public ConstantPool constantPool() { return constantPool; } private final String instructionPrefix; private final String instructionSuffix; private final boolean printBytes; private final boolean printConstantIndices; /** * Creates an object for disassembling a bytecode instruction stream. Each disassembled instruction will be suffixed * by the {@linkplain #PRINT_BYTES raw bytes} of the instruction followed by the platform specific new line * character(s). * * @param writer where to print the disassembled bytecode instructions * @param constantPool the constant pool referred by instructions in the bytecode instruction stream */ public BytecodePrinter(PrintWriter writer, ConstantPool constantPool) { this(writer, constantPool, "", "\n", PRINT_BYTES); } /** * Creates an object for disassembling a bytecode instruction stream. * * @param writer where to print the disassembled bytecode instructions * @param constantPool the constant pool referred by instructions in the bytecode instruction stream * @param instructionPrefix the string to be written to the stream before each disassembled instruction * @param instructionSuffix the string to be written to the stream after each disassembled instruction * @param flags a mask composed of zero or more of the following constants describing extra information to be * included for each disassembled instruction: {@value #PRINT_BYTES}, * {@value #PRINT_CONSTANT_POOL_INDICES} */ public BytecodePrinter(PrintWriter writer, ConstantPool constantPool, String instructionPrefix, String instructionSuffix, int flags) { final int unrecognizedFlags = flags & ~VALID_FLAGS_MASK; if (unrecognizedFlags != 0) { ProgramWarning.message("Unrecognized bytecode disassembly flags will be ignored: 0x" + Integer.toHexString(unrecognizedFlags)); } this.writer = writer; this.constantPool = constantPool; this.instructionPrefix = instructionPrefix; this.instructionSuffix = instructionSuffix; this.printBytes = (flags & PRINT_BYTES) != 0; this.printConstantIndices = (flags & PRINT_CONSTANT_POOL_INDICES) != 0; } /** * Sets the line number table this printer will use to prefix each instruction with its source line number. */ public void setLineNumberTable(LineNumberTable lnt) { this.lineNumberTable = lnt; } protected void printOpcode() { int currentOpcode = currentOpcode(); writer.print(Bytecodes.nameOf(currentOpcode)); } protected void printImmediate(int immediate) { printImmediate(" ", immediate); } protected void printImmediate(String prefix, int immediate) { writer.print(prefix + immediate); } protected void printConstant(int index) { writer.print(' '); if (printConstantIndices) { writer.print("cp[" + index + "]:"); } try { writer.print(constantPool.at(index).valueString(constantPool)); } catch (ClassFormatError classFormatError) { writer.print(" ***ERROR***"); } } protected void printKind(Kind kind) { writer.print(" " + kind); } protected void prolog() { if (instructionPrefix != null && !instructionPrefix.isEmpty()) { writer.print(instructionPrefix); } int bci = currentOpcodeBCI(); if (lineNumberTable != null) { int lineNo = lineNumberTable.findLineNumber(bci); if (lineNo == -1) { writer.print(" "); } else { String l = "[" + String.valueOf(lineNo) + "] "; writer.print(l); int pad = 7 - l.length(); while (pad > 0) { writer.print(' '); pad--; } } } writer.print(bci + ": "); if (isCurrentOpcodeWidened()) { writer.print("wide "); } } protected void epilog() { if (printBytes) { final int endAddress = currentBCI(); writer.print(" |"); final byte[] bytes = code(); for (int i = currentOpcodeBCI(); i < endAddress; i++) { writer.print(" " + (bytes[i] & 0xff)); } } if (instructionSuffix != null && !instructionSuffix.isEmpty()) { writer.print(instructionSuffix); } } public void printInstruction() { prolog(); printOpcode(); epilog(); } public void printInstructionWithImmediate(int operand) { prolog(); printOpcode(); printImmediate(operand); epilog(); } public void printInstructionWithOffset(int offset) { prolog(); printOpcode(); printImmediate(currentOpcodeBCI() + offset); epilog(); } public void printInstructionWithConstant(int index) { prolog(); printOpcode(); printConstant(index); epilog(); } @Override public void nop() { printInstruction(); } @Override public void aconst_null() { printInstruction(); } @Override public void iconst_m1() { printInstruction(); } @Override public void iconst_0() { printInstruction(); } @Override public void iconst_1() { printInstruction(); } @Override public void iconst_2() { printInstruction(); } @Override public void iconst_3() { printInstruction(); } @Override public void iconst_4() { printInstruction(); } @Override public void iconst_5() { printInstruction(); } @Override public void lconst_0() { printInstruction(); } @Override public void lconst_1() { printInstruction(); } @Override public void fconst_0() { printInstruction(); } @Override public void fconst_1() { printInstruction(); } @Override public void fconst_2() { printInstruction(); } @Override public void dconst_0() { printInstruction(); } @Override public void dconst_1() { printInstruction(); } @Override public void bipush(int operand) { printInstructionWithImmediate(operand); } @Override public void sipush(int operand) { printInstructionWithImmediate(operand); } @Override public void ldc(int index) { printInstructionWithConstant(index); } @Override public void ldc_w(int index) { printInstructionWithConstant(index); } @Override public void ldc2_w(int index) { printInstructionWithConstant(index); } @Override public void iload(int index) { printInstructionWithImmediate(index); } @Override public void lload(int index) { printInstructionWithImmediate(index); } @Override public void fload(int index) { printInstructionWithImmediate(index); } @Override public void dload(int index) { printInstructionWithImmediate(index); } @Override public void aload(int index) { printInstructionWithImmediate(index); } @Override public void iload_0() { printInstruction(); } @Override public void iload_1() { printInstruction(); } @Override public void iload_2() { printInstruction(); } @Override public void iload_3() { printInstruction(); } @Override public void lload_0() { printInstruction(); } @Override public void lload_1() { printInstruction(); } @Override public void lload_2() { printInstruction(); } @Override public void lload_3() { printInstruction(); } @Override public void fload_0() { printInstruction(); } @Override public void fload_1() { printInstruction(); } @Override public void fload_2() { printInstruction(); } @Override public void fload_3() { printInstruction(); } @Override public void dload_0() { printInstruction(); } @Override public void dload_1() { printInstruction(); } @Override public void dload_2() { printInstruction(); } @Override public void dload_3() { printInstruction(); } @Override public void aload_0() { printInstruction(); } @Override public void aload_1() { printInstruction(); } @Override public void aload_2() { printInstruction(); } @Override public void aload_3() { printInstruction(); } @Override public void iaload() { printInstruction(); } @Override public void laload() { printInstruction(); } @Override public void faload() { printInstruction(); } @Override public void daload() { printInstruction(); } @Override public void aaload() { printInstruction(); } @Override public void baload() { printInstruction(); } @Override public void caload() { printInstruction(); } @Override public void saload() { printInstruction(); } @Override public void istore(int index) { printInstructionWithImmediate(index); } @Override public void lstore(int index) { printInstructionWithImmediate(index); } @Override public void fstore(int index) { printInstructionWithImmediate(index); } @Override public void dstore(int index) { printInstructionWithImmediate(index); } @Override public void astore(int index) { printInstructionWithImmediate(index); } @Override public void istore_0() { printInstruction(); } @Override public void istore_1() { printInstruction(); } @Override public void istore_2() { printInstruction(); } @Override public void istore_3() { printInstruction(); } @Override public void lstore_0() { printInstruction(); } @Override public void lstore_1() { printInstruction(); } @Override public void lstore_2() { printInstruction(); } @Override public void lstore_3() { printInstruction(); } @Override public void fstore_0() { printInstruction(); } @Override public void fstore_1() { printInstruction(); } @Override public void fstore_2() { printInstruction(); } @Override public void fstore_3() { printInstruction(); } @Override public void dstore_0() { printInstruction(); } @Override public void dstore_1() { printInstruction(); } @Override public void dstore_2() { printInstruction(); } @Override public void dstore_3() { printInstruction(); } @Override public void astore_0() { printInstruction(); } @Override public void astore_1() { printInstruction(); } @Override public void astore_2() { printInstruction(); } @Override public void astore_3() { printInstruction(); } @Override public void iastore() { printInstruction(); } @Override public void lastore() { printInstruction(); } @Override public void fastore() { printInstruction(); } @Override public void dastore() { printInstruction(); } @Override public void aastore() { printInstruction(); } @Override public void bastore() { printInstruction(); } @Override public void castore() { printInstruction(); } @Override public void sastore() { printInstruction(); } @Override public void pop() { printInstruction(); } @Override public void pop2() { printInstruction(); } @Override public void dup() { printInstruction(); } @Override public void dup_x1() { printInstruction(); } @Override public void dup_x2() { printInstruction(); } @Override public void dup2() { printInstruction(); } @Override public void dup2_x1() { printInstruction(); } @Override public void dup2_x2() { printInstruction(); } @Override public void swap() { printInstruction(); } @Override public void iadd() { printInstruction(); } @Override public void ladd() { printInstruction(); } @Override public void fadd() { printInstruction(); } @Override public void dadd() { printInstruction(); } @Override public void isub() { printInstruction(); } @Override public void lsub() { printInstruction(); } @Override public void fsub() { printInstruction(); } @Override public void dsub() { printInstruction(); } @Override public void imul() { printInstruction(); } @Override public void lmul() { printInstruction(); } @Override public void fmul() { printInstruction(); } @Override public void dmul() { printInstruction(); } @Override public void idiv() { printInstruction(); } @Override public void ldiv() { printInstruction(); } @Override public void fdiv() { printInstruction(); } @Override public void ddiv() { printInstruction(); } @Override public void irem() { printInstruction(); } @Override public void lrem() { printInstruction(); } @Override public void frem() { printInstruction(); } @Override public void drem() { printInstruction(); } @Override public void ineg() { printInstruction(); } @Override public void lneg() { printInstruction(); } @Override public void fneg() { printInstruction(); } @Override public void dneg() { printInstruction(); } @Override public void ishl() { printInstruction(); } @Override public void lshl() { printInstruction(); } @Override public void ishr() { printInstruction(); } @Override public void lshr() { printInstruction(); } @Override public void iushr() { printInstruction(); } @Override public void lushr() { printInstruction(); } @Override public void iand() { printInstruction(); } @Override public void land() { printInstruction(); } @Override public void ior() { printInstruction(); } @Override public void lor() { printInstruction(); } @Override public void ixor() { printInstruction(); } @Override public void lxor() { printInstruction(); } @Override public void iinc(int index, int addend) { prolog(); printOpcode(); printImmediate(index); printImmediate(addend); epilog(); } @Override public void i2l() { printInstruction(); } @Override public void i2f() { printInstruction(); } @Override public void i2d() { printInstruction(); } @Override public void l2i() { printInstruction(); } @Override public void l2f() { printInstruction(); } @Override public void l2d() { printInstruction(); } @Override public void f2i() { printInstruction(); } @Override public void f2l() { printInstruction(); } @Override public void f2d() { printInstruction(); } @Override public void d2i() { printInstruction(); } @Override public void d2l() { printInstruction(); } @Override public void d2f() { printInstruction(); } @Override public void i2b() { printInstruction(); } @Override public void i2c() { printInstruction(); } @Override public void i2s() { printInstruction(); } @Override public void lcmp() { printInstruction(); } @Override public void fcmpl() { printInstruction(); } @Override public void fcmpg() { printInstruction(); } @Override public void dcmpl() { printInstruction(); } @Override public void dcmpg() { printInstruction(); } @Override public void ifeq(int offset) { printInstructionWithOffset(offset); } @Override public void ifne(int offset) { printInstructionWithOffset(offset); } @Override public void iflt(int offset) { printInstructionWithOffset(offset); } @Override public void ifge(int offset) { printInstructionWithOffset(offset); } @Override public void ifgt(int offset) { printInstructionWithOffset(offset); } @Override public void ifle(int offset) { printInstructionWithOffset(offset); } @Override public void if_icmpeq(int offset) { printInstructionWithOffset(offset); } @Override public void if_icmpne(int offset) { printInstructionWithOffset(offset); } @Override public void if_icmplt(int offset) { printInstructionWithOffset(offset); } @Override public void if_icmpge(int offset) { printInstructionWithOffset(offset); } @Override public void if_icmpgt(int offset) { printInstructionWithOffset(offset); } @Override public void if_icmple(int offset) { printInstructionWithOffset(offset); } @Override public void if_acmpeq(int offset) { printInstructionWithOffset(offset); } @Override public void if_acmpne(int offset) { printInstructionWithOffset(offset); } @Override public void goto_(int offset) { printInstructionWithOffset(offset); } @Override public void goto_w(int offset) { printInstructionWithOffset(offset); } @Override public void jsr(int offset) { printInstructionWithOffset(offset); } @Override public void jsr_w(int offset) { printInstructionWithOffset(offset); } @Override public void ret(int index) { printInstructionWithImmediate(index); } @Override public void tableswitch(int defaultOffset, int lowMatch, int highMatch, int numberOfCases) { prolog(); printOpcode(); printImmediate(" default:", currentOpcodeBCI() + defaultOffset); printImmediate(" low:", lowMatch); printImmediate(" high:", highMatch); for (int i = 0; i < numberOfCases; i++) { final int key = lowMatch + i; printImmediate(" " + key + ":", currentOpcodeBCI() + bytecodeScanner().readSwitchOffset()); } epilog(); } @Override public void lookupswitch(int defaultOffset, int numberOfCases) { prolog(); printOpcode(); printImmediate(" default:", currentOpcodeBCI() + defaultOffset); for (int i = 0; i < numberOfCases; i++) { printImmediate(bytecodeScanner().readSwitchCase()); printImmediate(":", currentOpcodeBCI() + bytecodeScanner().readSwitchOffset()); } epilog(); } @Override public void ireturn() { printInstruction(); } @Override public void lreturn() { printInstruction(); } @Override public void freturn() { printInstruction(); } @Override public void dreturn() { printInstruction(); } @Override public void areturn() { printInstruction(); } @Override public void vreturn() { printInstruction(); } @Override public void getstatic(int index) { printInstructionWithConstant(index); } @Override public void putstatic(int index) { printInstructionWithConstant(index); } @Override public void getfield(int index) { printInstructionWithConstant(index); } @Override public void putfield(int index) { printInstructionWithConstant(index); } @Override public void invokevirtual(int index) { printInstructionWithConstant(index); } @Override public void invokespecial(int index) { printInstructionWithConstant(index); } @Override public void invokestatic(int index) { printInstructionWithConstant(index); } @Override public void jnicall(int nativeFunctionDescriptorIndex) { printInstructionWithConstant(nativeFunctionDescriptorIndex); } @Override public void invokeinterface(int index, int count) { printInstructionWithConstant(index); } @Override public void new_(int index) { printInstructionWithConstant(index); } @Override public void newarray(int tag) { prolog(); printOpcode(); printKind(Kind.fromNewArrayTag(tag)); epilog(); } @Override public void anewarray(int index) { printInstructionWithConstant(index); } @Override public void arraylength() { printInstruction(); } @Override public void athrow() { printInstruction(); } @Override public void checkcast(int index) { printInstructionWithConstant(index); } @Override public void instanceof_(int index) { printInstructionWithConstant(index); } @Override public void monitorenter() { printInstruction(); } @Override public void monitorexit() { printInstruction(); } @Override public void multianewarray(int index, int nDimensions) { prolog(); printOpcode(); printConstant(index); printImmediate(nDimensions); epilog(); } @Override public void ifnull(int offset) { printInstructionWithOffset(offset); } @Override public void ifnonnull(int offset) { printInstructionWithOffset(offset); } @Override public void breakpoint() { printInstruction(); } @Override protected void wide() { } @Override protected boolean extension(int opcode, boolean isWide) { int length = Bytecodes.lengthOf(opcode); if (length == 2) { int index = bytecodeScanner().readUnsigned1(); printInstructionWithImmediate(index); } else if (length == 3) { int index = bytecodeScanner().readUnsigned2(); printInstructionWithImmediate(index); } else { assert length == 1; printInstruction(); } return true; } }