You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
10 KiB
295 lines
10 KiB
/* |
|
* 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 com.fr.third.javassist.bytecode; |
|
|
|
import java.io.PrintStream; |
|
|
|
import com.fr.third.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 <code>InstructionPrinter</code> 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(); |
|
com.fr.third.javassist.bytecode.ConstPool pool = info.getConstPool(); |
|
CodeAttribute code = info.getCodeAttribute(); |
|
if (code == null) |
|
return; |
|
|
|
com.fr.third.javassist.bytecode.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(com.fr.third.javassist.bytecode.CodeIterator iter, int pos, com.fr.third.javassist.bytecode.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); |
|
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(com.fr.third.javassist.bytecode.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(com.fr.third.javassist.bytecode.ConstPool pool, int index) { |
|
return "#" + index + " = Class " + pool.getClassInfo(index); |
|
} |
|
|
|
|
|
private static String interfaceMethodInfo(com.fr.third.javassist.bytecode.ConstPool pool, int index) { |
|
return "#" + index + " = Method " |
|
+ pool.getInterfaceMethodrefClassName(index) + "." |
|
+ pool.getInterfaceMethodrefName(index) + "(" |
|
+ pool.getInterfaceMethodrefType(index) + ")"; |
|
} |
|
|
|
private static String methodInfo(com.fr.third.javassist.bytecode.ConstPool pool, int index) { |
|
return "#" + index + " = Method " |
|
+ pool.getMethodrefClassName(index) + "." |
|
+ pool.getMethodrefName(index) + "(" |
|
+ pool.getMethodrefType(index) + ")"; |
|
} |
|
|
|
|
|
private static String fieldInfo(com.fr.third.javassist.bytecode.ConstPool pool, int index) { |
|
return "#" + index + " = Field " |
|
+ pool.getFieldrefClassName(index) + "." |
|
+ pool.getFieldrefName(index) + "(" |
|
+ pool.getFieldrefType(index) + ")"; |
|
} |
|
|
|
|
|
private static String lookupSwitch(com.fr.third.javassist.bytecode.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(com.fr.third.javassist.bytecode.ConstPool pool, int index) { |
|
int tag = pool.getTag(index); |
|
switch (tag) { |
|
case com.fr.third.javassist.bytecode.ConstPool.CONST_String: |
|
return "#" + index + " = \"" + pool.getStringInfo(index) + "\""; |
|
case com.fr.third.javassist.bytecode.ConstPool.CONST_Integer: |
|
return "#" + index + " = int " + pool.getIntegerInfo(index); |
|
case com.fr.third.javassist.bytecode.ConstPool.CONST_Float: |
|
return "#" + index + " = float " + pool.getFloatInfo(index); |
|
case com.fr.third.javassist.bytecode.ConstPool.CONST_Long: |
|
return "#" + index + " = long " + pool.getLongInfo(index); |
|
case com.fr.third.javassist.bytecode.ConstPool.CONST_Double: |
|
return "#" + index + " = int " + pool.getDoubleInfo(index); |
|
case ConstPool.CONST_Class: |
|
return classInfo(pool, index); |
|
default: |
|
throw new RuntimeException("bad LDC: " + tag); |
|
} |
|
} |
|
}
|
|
|