• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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