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