javassist.bytecode.InstructionPrinter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javassist Show documentation
Show all versions of javassist Show documentation
Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation
simple. It is a class library for editing bytecodes in Java.
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode;
import java.io.PrintStream;
import javassist.CtMethod;
/**
* Simple utility class for printing the bytecode instructions of a method.
*
* @author Jason T. Greene
*/
public class InstructionPrinter implements Opcode {
private final static String opcodes[] = Mnemonic.OPCODE;
private final PrintStream stream;
/**
* Constructs a InstructionPrinter
object.
*/
public InstructionPrinter(PrintStream stream) {
this.stream = stream;
}
/**
* Prints the bytecode instructions of a given method.
*/
public static void print(CtMethod method, PrintStream stream) {
(new InstructionPrinter(stream)).print(method);
}
/**
* Prints the bytecode instructions of a given method.
*/
public void print(CtMethod method) {
MethodInfo info = method.getMethodInfo2();
ConstPool pool = info.getConstPool();
CodeAttribute code = info.getCodeAttribute();
if (code == null)
return;
CodeIterator iterator = code.iterator();
while (iterator.hasNext()) {
int pos;
try {
pos = iterator.next();
} catch (BadBytecode e) {
throw new RuntimeException(e);
}
stream.println(pos + ": " + instructionString(iterator, pos, pool));
}
}
/**
* Gets a string representation of the bytecode instruction at the specified
* position.
*/
public static String instructionString(CodeIterator iter, int pos, ConstPool pool) {
int opcode = iter.byteAt(pos);
if (opcode > opcodes.length || opcode < 0)
throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: "+ pos);
String opstring = opcodes[opcode];
switch (opcode) {
case BIPUSH:
return opstring + " " + iter.byteAt(pos + 1);
case SIPUSH:
return opstring + " " + iter.s16bitAt(pos + 1);
case LDC:
return opstring + " " + ldc(pool, iter.byteAt(pos + 1));
case LDC_W :
case LDC2_W :
return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1));
case ILOAD:
case LLOAD:
case FLOAD:
case DLOAD:
case ALOAD:
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
case ASTORE:
return opstring + " " + iter.byteAt(pos + 1);
case IFEQ:
case IFGE:
case IFGT:
case IFLE:
case IFLT:
case IFNE:
case IFNONNULL:
case IFNULL:
case IF_ACMPEQ:
case IF_ACMPNE:
case IF_ICMPEQ:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ICMPLT:
case IF_ICMPNE:
return opstring + " " + (iter.s16bitAt(pos + 1) + pos);
case IINC:
return opstring + " " + iter.byteAt(pos + 1) + ", " + iter.signedByteAt(pos + 2);
case GOTO:
case JSR:
return opstring + " " + (iter.s16bitAt(pos + 1) + pos);
case RET:
return opstring + " " + iter.byteAt(pos + 1);
case TABLESWITCH:
return tableSwitch(iter, pos);
case LOOKUPSWITCH:
return lookupSwitch(iter, pos);
case GETSTATIC:
case PUTSTATIC:
case GETFIELD:
case PUTFIELD:
return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1));
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKESTATIC:
return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1));
case INVOKEINTERFACE:
return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1));
case INVOKEDYNAMIC:
return opstring + " " + iter.u16bitAt(pos + 1);
case NEW:
return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
case NEWARRAY:
return opstring + " " + arrayInfo(iter.byteAt(pos + 1));
case ANEWARRAY:
case CHECKCAST:
return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
case WIDE:
return wide(iter, pos);
case MULTIANEWARRAY:
return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
case GOTO_W:
case JSR_W:
return opstring + " " + (iter.s32bitAt(pos + 1)+ pos);
default:
return opstring;
}
}
private static String wide(CodeIterator iter, int pos) {
int opcode = iter.byteAt(pos + 1);
int index = iter.u16bitAt(pos + 2);
switch (opcode) {
case ILOAD:
case LLOAD:
case FLOAD:
case DLOAD:
case ALOAD:
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
case ASTORE:
case IINC:
case RET:
return opcodes[opcode] + " " + index;
default:
throw new RuntimeException("Invalid WIDE operand");
}
}
private static String arrayInfo(int type) {
switch (type) {
case T_BOOLEAN:
return "boolean";
case T_CHAR:
return "char";
case T_BYTE:
return "byte";
case T_SHORT:
return "short";
case T_INT:
return "int";
case T_LONG:
return "long";
case T_FLOAT:
return "float";
case T_DOUBLE:
return "double";
default:
throw new RuntimeException("Invalid array type");
}
}
private static String classInfo(ConstPool pool, int index) {
return "#" + index + " = Class " + pool.getClassInfo(index);
}
private static String interfaceMethodInfo(ConstPool pool, int index) {
return "#" + index + " = Method "
+ pool.getInterfaceMethodrefClassName(index) + "."
+ pool.getInterfaceMethodrefName(index) + "("
+ pool.getInterfaceMethodrefType(index) + ")";
}
private static String methodInfo(ConstPool pool, int index) {
return "#" + index + " = Method "
+ pool.getMethodrefClassName(index) + "."
+ pool.getMethodrefName(index) + "("
+ pool.getMethodrefType(index) + ")";
}
private static String fieldInfo(ConstPool pool, int index) {
return "#" + index + " = Field "
+ pool.getFieldrefClassName(index) + "."
+ pool.getFieldrefName(index) + "("
+ pool.getFieldrefType(index) + ")";
}
private static String lookupSwitch(CodeIterator iter, int pos) {
StringBuffer buffer = new StringBuffer("lookupswitch {\n");
int index = (pos & ~3) + 4;
// default
buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n");
int npairs = iter.s32bitAt(index += 4);
int end = npairs * 8 + (index += 4);
for (; index < end; index += 8) {
int match = iter.s32bitAt(index);
int target = iter.s32bitAt(index + 4) + pos;
buffer.append("\t\t").append(match).append(": ").append(target).append("\n");
}
buffer.setCharAt(buffer.length() - 1, '}');
return buffer.toString();
}
private static String tableSwitch(CodeIterator iter, int pos) {
StringBuffer buffer = new StringBuffer("tableswitch {\n");
int index = (pos & ~3) + 4;
// default
buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n");
int low = iter.s32bitAt(index += 4);
int high = iter.s32bitAt(index += 4);
int end = (high - low + 1) * 4 + (index += 4);
// Offset table
for (int key = low; index < end; index += 4, key++) {
int target = iter.s32bitAt(index) + pos;
buffer.append("\t\t").append(key).append(": ").append(target).append("\n");
}
buffer.setCharAt(buffer.length() - 1, '}');
return buffer.toString();
}
private static String ldc(ConstPool pool, int index) {
int tag = pool.getTag(index);
switch (tag) {
case ConstPool.CONST_String:
return "#" + index + " = \"" + pool.getStringInfo(index) + "\"";
case ConstPool.CONST_Integer:
return "#" + index + " = int " + pool.getIntegerInfo(index);
case ConstPool.CONST_Float:
return "#" + index + " = float " + pool.getFloatInfo(index);
case ConstPool.CONST_Long:
return "#" + index + " = long " + pool.getLongInfo(index);
case ConstPool.CONST_Double:
return "#" + index + " = int " + pool.getDoubleInfo(index);
case ConstPool.CONST_Class:
return classInfo(pool, index);
default:
throw new RuntimeException("bad LDC: " + tag);
}
}
}