1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later, 9 * or the Apache License Version 2.0. 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 */ 16 package javassist.bytecode; 17 18 import java.io.PrintStream; 19 20 import javassist.CtMethod; 21 22 /** 23 * Simple utility class for printing the bytecode instructions of a method. 24 * 25 * @author Jason T. Greene 26 */ 27 public class InstructionPrinter implements Opcode { 28 29 private final static String opcodes[] = Mnemonic.OPCODE; 30 private final PrintStream stream; 31 32 /** 33 * Constructs a <code>InstructionPrinter</code> object. 34 */ InstructionPrinter(PrintStream stream)35 public InstructionPrinter(PrintStream stream) { 36 this.stream = stream; 37 } 38 39 /** 40 * Prints the bytecode instructions of a given method. 41 */ print(CtMethod method, PrintStream stream)42 public static void print(CtMethod method, PrintStream stream) { 43 (new InstructionPrinter(stream)).print(method); 44 } 45 46 /** 47 * Prints the bytecode instructions of a given method. 48 */ print(CtMethod method)49 public void print(CtMethod method) { 50 MethodInfo info = method.getMethodInfo2(); 51 ConstPool pool = info.getConstPool(); 52 CodeAttribute code = info.getCodeAttribute(); 53 if (code == null) 54 return; 55 56 CodeIterator iterator = code.iterator(); 57 while (iterator.hasNext()) { 58 int pos; 59 try { 60 pos = iterator.next(); 61 } catch (BadBytecode e) { 62 throw new RuntimeException(e); 63 } 64 65 stream.println(pos + ": " + instructionString(iterator, pos, pool)); 66 } 67 } 68 69 /** 70 * Gets a string representation of the bytecode instruction at the specified 71 * position. 72 */ instructionString(CodeIterator iter, int pos, ConstPool pool)73 public static String instructionString(CodeIterator iter, int pos, ConstPool pool) { 74 int opcode = iter.byteAt(pos); 75 76 if (opcode > opcodes.length || opcode < 0) 77 throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: "+ pos); 78 79 String opstring = opcodes[opcode]; 80 switch (opcode) { 81 case BIPUSH: 82 return opstring + " " + iter.byteAt(pos + 1); 83 case SIPUSH: 84 return opstring + " " + iter.s16bitAt(pos + 1); 85 case LDC: 86 return opstring + " " + ldc(pool, iter.byteAt(pos + 1)); 87 case LDC_W : 88 case LDC2_W : 89 return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1)); 90 case ILOAD: 91 case LLOAD: 92 case FLOAD: 93 case DLOAD: 94 case ALOAD: 95 case ISTORE: 96 case LSTORE: 97 case FSTORE: 98 case DSTORE: 99 case ASTORE: 100 return opstring + " " + iter.byteAt(pos + 1); 101 case IFEQ: 102 case IFGE: 103 case IFGT: 104 case IFLE: 105 case IFLT: 106 case IFNE: 107 case IFNONNULL: 108 case IFNULL: 109 case IF_ACMPEQ: 110 case IF_ACMPNE: 111 case IF_ICMPEQ: 112 case IF_ICMPGE: 113 case IF_ICMPGT: 114 case IF_ICMPLE: 115 case IF_ICMPLT: 116 case IF_ICMPNE: 117 return opstring + " " + (iter.s16bitAt(pos + 1) + pos); 118 case IINC: 119 return opstring + " " + iter.byteAt(pos + 1) + ", " + iter.signedByteAt(pos + 2); 120 case GOTO: 121 case JSR: 122 return opstring + " " + (iter.s16bitAt(pos + 1) + pos); 123 case RET: 124 return opstring + " " + iter.byteAt(pos + 1); 125 case TABLESWITCH: 126 return tableSwitch(iter, pos); 127 case LOOKUPSWITCH: 128 return lookupSwitch(iter, pos); 129 case GETSTATIC: 130 case PUTSTATIC: 131 case GETFIELD: 132 case PUTFIELD: 133 return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1)); 134 case INVOKEVIRTUAL: 135 case INVOKESPECIAL: 136 case INVOKESTATIC: 137 return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); 138 case INVOKEINTERFACE: 139 return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); 140 case INVOKEDYNAMIC: 141 return opstring + " " + iter.u16bitAt(pos + 1); 142 case NEW: 143 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); 144 case NEWARRAY: 145 return opstring + " " + arrayInfo(iter.byteAt(pos + 1)); 146 case ANEWARRAY: 147 case CHECKCAST: 148 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); 149 case WIDE: 150 return wide(iter, pos); 151 case MULTIANEWARRAY: 152 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); 153 case GOTO_W: 154 case JSR_W: 155 return opstring + " " + (iter.s32bitAt(pos + 1)+ pos); 156 default: 157 return opstring; 158 } 159 } 160 161 wide(CodeIterator iter, int pos)162 private static String wide(CodeIterator iter, int pos) { 163 int opcode = iter.byteAt(pos + 1); 164 int index = iter.u16bitAt(pos + 2); 165 switch (opcode) { 166 case ILOAD: 167 case LLOAD: 168 case FLOAD: 169 case DLOAD: 170 case ALOAD: 171 case ISTORE: 172 case LSTORE: 173 case FSTORE: 174 case DSTORE: 175 case ASTORE: 176 case IINC: 177 case RET: 178 return opcodes[opcode] + " " + index; 179 default: 180 throw new RuntimeException("Invalid WIDE operand"); 181 } 182 } 183 184 arrayInfo(int type)185 private static String arrayInfo(int type) { 186 switch (type) { 187 case T_BOOLEAN: 188 return "boolean"; 189 case T_CHAR: 190 return "char"; 191 case T_BYTE: 192 return "byte"; 193 case T_SHORT: 194 return "short"; 195 case T_INT: 196 return "int"; 197 case T_LONG: 198 return "long"; 199 case T_FLOAT: 200 return "float"; 201 case T_DOUBLE: 202 return "double"; 203 default: 204 throw new RuntimeException("Invalid array type"); 205 } 206 } 207 208 classInfo(ConstPool pool, int index)209 private static String classInfo(ConstPool pool, int index) { 210 return "#" + index + " = Class " + pool.getClassInfo(index); 211 } 212 213 interfaceMethodInfo(ConstPool pool, int index)214 private static String interfaceMethodInfo(ConstPool pool, int index) { 215 return "#" + index + " = Method " 216 + pool.getInterfaceMethodrefClassName(index) + "." 217 + pool.getInterfaceMethodrefName(index) + "(" 218 + pool.getInterfaceMethodrefType(index) + ")"; 219 } 220 methodInfo(ConstPool pool, int index)221 private static String methodInfo(ConstPool pool, int index) { 222 return "#" + index + " = Method " 223 + pool.getMethodrefClassName(index) + "." 224 + pool.getMethodrefName(index) + "(" 225 + pool.getMethodrefType(index) + ")"; 226 } 227 228 fieldInfo(ConstPool pool, int index)229 private static String fieldInfo(ConstPool pool, int index) { 230 return "#" + index + " = Field " 231 + pool.getFieldrefClassName(index) + "." 232 + pool.getFieldrefName(index) + "(" 233 + pool.getFieldrefType(index) + ")"; 234 } 235 236 lookupSwitch(CodeIterator iter, int pos)237 private static String lookupSwitch(CodeIterator iter, int pos) { 238 StringBuffer buffer = new StringBuffer("lookupswitch {\n"); 239 int index = (pos & ~3) + 4; 240 // default 241 buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); 242 int npairs = iter.s32bitAt(index += 4); 243 int end = npairs * 8 + (index += 4); 244 245 for (; index < end; index += 8) { 246 int match = iter.s32bitAt(index); 247 int target = iter.s32bitAt(index + 4) + pos; 248 buffer.append("\t\t").append(match).append(": ").append(target).append("\n"); 249 } 250 251 buffer.setCharAt(buffer.length() - 1, '}'); 252 return buffer.toString(); 253 } 254 255 tableSwitch(CodeIterator iter, int pos)256 private static String tableSwitch(CodeIterator iter, int pos) { 257 StringBuffer buffer = new StringBuffer("tableswitch {\n"); 258 int index = (pos & ~3) + 4; 259 // default 260 buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); 261 int low = iter.s32bitAt(index += 4); 262 int high = iter.s32bitAt(index += 4); 263 int end = (high - low + 1) * 4 + (index += 4); 264 265 // Offset table 266 for (int key = low; index < end; index += 4, key++) { 267 int target = iter.s32bitAt(index) + pos; 268 buffer.append("\t\t").append(key).append(": ").append(target).append("\n"); 269 } 270 271 buffer.setCharAt(buffer.length() - 1, '}'); 272 return buffer.toString(); 273 } 274 275 ldc(ConstPool pool, int index)276 private static String ldc(ConstPool pool, int index) { 277 int tag = pool.getTag(index); 278 switch (tag) { 279 case ConstPool.CONST_String: 280 return "#" + index + " = \"" + pool.getStringInfo(index) + "\""; 281 case ConstPool.CONST_Integer: 282 return "#" + index + " = int " + pool.getIntegerInfo(index); 283 case ConstPool.CONST_Float: 284 return "#" + index + " = float " + pool.getFloatInfo(index); 285 case ConstPool.CONST_Long: 286 return "#" + index + " = long " + pool.getLongInfo(index); 287 case ConstPool.CONST_Double: 288 return "#" + index + " = int " + pool.getDoubleInfo(index); 289 case ConstPool.CONST_Class: 290 return classInfo(pool, index); 291 default: 292 throw new RuntimeException("bad LDC: " + tag); 293 } 294 } 295 } 296