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 */
15package javassist.bytecode;
16
17import java.io.PrintStream;
18
19import javassist.CtMethod;
20
21/**
22 * Simple utility class for printing the instructions of a method.
23 *
24 * @author Jason T. Greene
25 */
26public 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