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 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); 134 case WIDE: 135 return wide(iter, pos); 136 case MULTIANEWARRAY: 137 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); 138 case GOTO_W: 139 case JSR_W: 140 return opstring + " " + (iter.s32bitAt(pos + 1)+ pos); 141 default: 142 return opstring; 143 } 144 } 145 146 147 private static String wide(CodeIterator iter, int pos) { 148 int opcode = iter.byteAt(pos + 1); 149 int index = iter.u16bitAt(pos + 2); 150 switch (opcode) { 151 case ILOAD: 152 case LLOAD: 153 case FLOAD: 154 case DLOAD: 155 case ALOAD: 156 case ISTORE: 157 case LSTORE: 158 case FSTORE: 159 case DSTORE: 160 case ASTORE: 161 case IINC: 162 case RET: 163 return opcodes[opcode] + " " + index; 164 default: 165 throw new RuntimeException("Invalid WIDE operand"); 166 } 167 } 168 169 170 private static String arrayInfo(int type) { 171 switch (type) { 172 case T_BOOLEAN: 173 return "boolean"; 174 case T_CHAR: 175 return "char"; 176 case T_BYTE: 177 return "byte"; 178 case T_SHORT: 179 return "short"; 180 case T_INT: 181 return "int"; 182 case T_LONG: 183 return "long"; 184 case T_FLOAT: 185 return "float"; 186 case T_DOUBLE: 187 return "double"; 188 default: 189 throw new RuntimeException("Invalid array type"); 190 } 191 } 192 193 194 private static String classInfo(ConstPool pool, int index) { 195 return "#" + index + " = Class " + pool.getClassInfo(index); 196 } 197 198 199 private static String interfaceMethodInfo(ConstPool pool, int index) { 200 return "#" + index + " = Method " 201 + pool.getInterfaceMethodrefClassName(index) + "." 202 + pool.getInterfaceMethodrefName(index) + "(" 203 + pool.getInterfaceMethodrefType(index) + ")"; 204 } 205 206 private static String methodInfo(ConstPool pool, int index) { 207 return "#" + index + " = Method " 208 + pool.getMethodrefClassName(index) + "." 209 + pool.getMethodrefName(index) + "(" 210 + pool.getMethodrefType(index) + ")"; 211 } 212 213 214 private static String fieldInfo(ConstPool pool, int index) { 215 return "#" + index + " = Field " 216 + pool.getFieldrefClassName(index) + "." 217 + pool.getFieldrefName(index) + "(" 218 + pool.getFieldrefType(index) + ")"; 219 } 220 221 222 private static String lookupSwitch(CodeIterator iter, int pos) { 223 StringBuffer buffer = new StringBuffer("lookupswitch {\n"); 224 int index = (pos & ~3) + 4; 225 // default 226 buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); 227 int npairs = iter.s32bitAt(index += 4); 228 int end = npairs * 8 + (index += 4); 229 230 for (; index < end; index += 8) { 231 int match = iter.s32bitAt(index); 232 int target = iter.s32bitAt(index + 4) + pos; 233 buffer.append("\t\t").append(match).append(": ").append(target).append("\n"); 234 } 235 236 buffer.setCharAt(buffer.length() - 1, '}'); 237 return buffer.toString(); 238 } 239 240 241 private static String tableSwitch(CodeIterator iter, int pos) { 242 StringBuffer buffer = new StringBuffer("tableswitch {\n"); 243 int index = (pos & ~3) + 4; 244 // default 245 buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); 246 int low = iter.s32bitAt(index += 4); 247 int high = iter.s32bitAt(index += 4); 248 int end = (high - low + 1) * 4 + (index += 4); 249 250 // Offset table 251 for (int key = low; index < end; index += 4, key++) { 252 int target = iter.s32bitAt(index) + pos; 253 buffer.append("\t\t").append(key).append(": ").append(target).append("\n"); 254 } 255 256 buffer.setCharAt(buffer.length() - 1, '}'); 257 return buffer.toString(); 258 } 259 260 261 private static String ldc(ConstPool pool, int index) { 262 int tag = pool.getTag(index); 263 switch (tag) { 264 case ConstPool.CONST_String: 265 return "#" + index + " = \"" + pool.getStringInfo(index) + "\""; 266 case ConstPool.CONST_Integer: 267 return "#" + index + " = int " + pool.getIntegerInfo(index); 268 case ConstPool.CONST_Float: 269 return "#" + index + " = float " + pool.getFloatInfo(index); 270 case ConstPool.CONST_Long: 271 return "#" + index + " = long " + pool.getLongInfo(index); 272 case ConstPool.CONST_Double: 273 return "#" + index + " = int " + pool.getDoubleInfo(index); 274 case ConstPool.CONST_Class: 275 return classInfo(pool, index); 276 default: 277 throw new RuntimeException("bad LDC: " + tag); 278 } 279 } 280 }