1/*
2 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.classfile;
27
28import java.util.Locale;
29
30/**
31 * See JVMS, chapter 6.
32 *
33 *  <p><b>This is NOT part of any supported API.
34 *  If you write code that depends on this, you do so at your own risk.
35 *  This code and its internal interfaces are subject to change or
36 *  deletion without notice.</b>
37 *
38 * @see Code_attribute#getInstructions
39 */
40public class Instruction {
41    /** The kind of an instruction, as determined by the position, size and
42     *  types of its operands. */
43    public static enum Kind {
44        /** Opcode is not followed by any operands. */
45        NO_OPERANDS(1),
46        /** Opcode is followed by a byte indicating a type. */
47        ATYPE(2),
48        /** Opcode is followed by a 2-byte branch offset. */
49        BRANCH(3),
50        /** Opcode is followed by a 4-byte branch offset. */
51        BRANCH_W(5),
52        /** Opcode is followed by a signed byte value. */
53        BYTE(2),
54        /** Opcode is followed by a 1-byte index into the constant pool. */
55        CPREF(2),
56        /** Opcode is followed by a 2-byte index into the constant pool. */
57        CPREF_W(3),
58        /** Opcode is followed by a 2-byte index into the constant pool,
59         *  an unsigned byte value. */
60        CPREF_W_UBYTE(4),
61        /** Opcode is followed by a 2-byte index into the constant pool.,
62         *  an unsigned byte value, and a zero byte. */
63        CPREF_W_UBYTE_ZERO(5),
64        /** Opcode is followed by variable number of operands, depending
65         * on the instruction.*/
66        DYNAMIC(-1),
67        /** Opcode is followed by a 1-byte reference to a local variable. */
68        LOCAL(2),
69        /** Opcode is followed by a 1-byte reference to a local variable,
70         *  and a signed byte value. */
71        LOCAL_BYTE(3),
72        /** Opcode is followed by a signed short value. */
73        SHORT(3),
74        /** Wide opcode is not followed by any operands. */
75        WIDE_NO_OPERANDS(2),
76        /** Wide opcode is followed by a 2-byte index into the local variables array. */
77        WIDE_LOCAL(4),
78        /** Wide opcode is followed by a 2-byte index into the constant pool. */
79        WIDE_CPREF_W(4),
80        /** Wide opcode is followed by a 2-byte index into the constant pool,
81         *  and a signed short value. */
82        WIDE_CPREF_W_SHORT(6),
83        /** Wide opcode is followed by a 2-byte reference to a local variable,
84         *  and a signed short value. */
85        WIDE_LOCAL_SHORT(6),
86        /** Opcode was not recognized. */
87        UNKNOWN(1);
88
89        Kind(int length) {
90            this.length = length;
91        }
92
93        /** The length, in bytes, of this kind of instruction, or -1 is the
94         *  length depends on the specific instruction. */
95        public final int length;
96    }
97
98    /** A utility visitor to help decode the operands of an instruction.
99     *  @see Instruction#accept */
100    public interface KindVisitor<R,P> {
101        /** See {@link Kind#NO_OPERANDS}, {@link Kind#WIDE_NO_OPERANDS}. */
102        R visitNoOperands(Instruction instr, P p);
103        /** See {@link Kind#ATYPE}. */
104        R visitArrayType(Instruction instr, TypeKind kind, P p);
105        /** See {@link Kind#BRANCH}, {@link Kind#BRANCH_W}. */
106        R visitBranch(Instruction instr, int offset, P p);
107        /** See {@link Kind#CPREF}, {@link Kind#CPREF_W}, {@link Kind#WIDE_CPREF_W}. */
108        R visitConstantPoolRef(Instruction instr, int index, P p);
109        /** See {@link Kind#CPREF_W_UBYTE}, {@link Kind#CPREF_W_UBYTE_ZERO}, {@link Kind#WIDE_CPREF_W_SHORT}. */
110        R visitConstantPoolRefAndValue(Instruction instr, int index, int value, P p);
111        /** See {@link Kind#LOCAL}, {@link Kind#WIDE_LOCAL}. */
112        R visitLocal(Instruction instr, int index, P p);
113        /** See {@link Kind#LOCAL_BYTE}. */
114        R visitLocalAndValue(Instruction instr, int index, int value, P p);
115        /** See {@link Kind#DYNAMIC}. */
116        R visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets, P p);
117        /** See {@link Kind#DYNAMIC}. */
118        R visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets, P p);
119        /** See {@link Kind#BYTE}, {@link Kind#SHORT}. */
120        R visitValue(Instruction instr, int value, P p);
121        /** Instruction is unrecognized. */
122        R visitUnknown(Instruction instr, P p);
123
124    }
125
126    /** The kind of primitive array type to create.
127     *  See JVMS chapter 6, newarray. */
128    public static enum TypeKind {
129        T_BOOLEAN(4, "boolean"),
130        T_CHAR(5, "char"),
131        T_FLOAT(6, "float"),
132        T_DOUBLE(7, "double"),
133        T_BYTE(8, "byte"),
134        T_SHORT(9, "short"),
135        T_INT (10, "int"),
136        T_LONG (11, "long");
137        TypeKind(int value, String name) {
138            this.value = value;
139            this.name = name;
140        }
141
142        public static TypeKind get(int value) {
143            switch (value) {
144                case  4: return T_BOOLEAN;
145                case  5: return T_CHAR;
146                case  6: return T_FLOAT;
147                case  7: return T_DOUBLE;
148                case  8: return T_BYTE;
149                case  9: return T_SHORT;
150                case  10: return T_INT;
151                case  11: return T_LONG;
152                default: return null;
153            }
154        }
155
156        public final int value;
157        public final String name;
158    }
159
160    /** An instruction is defined by its position in a bytecode array. */
161    public Instruction(byte[] bytes, int pc) {
162        this.bytes = bytes;
163        this.pc = pc;
164    }
165
166    /** Get the position of the instruction within the bytecode array. */
167    public int getPC() {
168        return pc;
169    }
170
171    /** Get a byte value, relative to the start of this instruction. */
172    public int getByte(int offset) {
173        return bytes[pc + offset];
174    }
175
176    /** Get an unsigned byte value, relative to the start of this instruction. */
177    public int getUnsignedByte(int offset) {
178        return getByte(offset) & 0xff;
179    }
180
181    /** Get a 2-byte value, relative to the start of this instruction. */
182    public int getShort(int offset) {
183        return (getByte(offset) << 8) | getUnsignedByte(offset + 1);
184    }
185
186    /** Get a unsigned 2-byte value, relative to the start of this instruction. */
187    public int getUnsignedShort(int offset) {
188        return getShort(offset) & 0xFFFF;
189    }
190
191    /** Get a 4-byte value, relative to the start of this instruction. */
192    public int getInt(int offset) {
193        return (getShort(offset) << 16) | (getUnsignedShort(offset + 2));
194    }
195
196    /** Get the Opcode for this instruction, or null if the instruction is
197     * unrecognized. */
198    public Opcode getOpcode() {
199        int b = getUnsignedByte(0);
200        switch (b) {
201            case Opcode.NONPRIV:
202            case Opcode.PRIV:
203            case Opcode.WIDE:
204                return Opcode.get(b, getUnsignedByte(1));
205        }
206        return Opcode.get(b);
207    }
208
209    /** Get the mnemonic for this instruction, or a default string if the
210     * instruction is unrecognized. */
211    public String getMnemonic() {
212        Opcode opcode = getOpcode();
213        if (opcode == null)
214            return "bytecode " + getUnsignedByte(0);
215        else
216            return opcode.toString().toLowerCase(Locale.US);
217    }
218
219    /** Get the length, in bytes, of this instruction, including the opcode
220     * and all its operands. */
221    public int length() {
222        Opcode opcode = getOpcode();
223        if (opcode == null)
224            return 1;
225
226        switch (opcode) {
227            case TABLESWITCH: {
228                int pad = align(pc + 1) - pc;
229                int low = getInt(pad + 4);
230                int high = getInt(pad + 8);
231                return pad + 12 + 4 * (high - low + 1);
232            }
233            case LOOKUPSWITCH: {
234                int pad = align(pc + 1) - pc;
235                int npairs = getInt(pad + 4);
236                return pad + 8 + 8 * npairs;
237
238            }
239            default:
240                return opcode.kind.length;
241        }
242    }
243
244    /** Get the {@link Kind} of this instruction. */
245    public Kind getKind() {
246        Opcode opcode = getOpcode();
247        return (opcode != null ? opcode.kind : Kind.UNKNOWN);
248    }
249
250    /** Invoke a method on the visitor according to the kind of this
251     * instruction, passing in the decoded operands for the instruction. */
252    public <R,P> R accept(KindVisitor<R,P> visitor, P p) {
253        switch (getKind()) {
254            case NO_OPERANDS:
255                return visitor.visitNoOperands(this, p);
256
257            case ATYPE:
258                return visitor.visitArrayType(
259                        this, TypeKind.get(getUnsignedByte(1)), p);
260
261            case BRANCH:
262                return visitor.visitBranch(this, getShort(1), p);
263
264            case BRANCH_W:
265                return visitor.visitBranch(this, getInt(1), p);
266
267            case BYTE:
268                return visitor.visitValue(this, getByte(1), p);
269
270            case CPREF:
271                return visitor.visitConstantPoolRef(this, getUnsignedByte(1), p);
272
273            case CPREF_W:
274                return visitor.visitConstantPoolRef(this, getUnsignedShort(1), p);
275
276            case CPREF_W_UBYTE:
277            case CPREF_W_UBYTE_ZERO:
278                return visitor.visitConstantPoolRefAndValue(
279                        this, getUnsignedShort(1), getUnsignedByte(3), p);
280
281            case DYNAMIC: {
282                switch (getOpcode()) {
283                    case TABLESWITCH: {
284                        int pad = align(pc + 1) - pc;
285                        int default_ = getInt(pad);
286                        int low = getInt(pad + 4);
287                        int high = getInt(pad + 8);
288                        int[] values = new int[high - low + 1];
289                        for (int i = 0; i < values.length; i++)
290                            values[i] = getInt(pad + 12 + 4 * i);
291                        return visitor.visitTableSwitch(
292                                this, default_, low, high, values, p);
293                    }
294                    case LOOKUPSWITCH: {
295                        int pad = align(pc + 1) - pc;
296                        int default_ = getInt(pad);
297                        int npairs = getInt(pad + 4);
298                        int[] matches = new int[npairs];
299                        int[] offsets = new int[npairs];
300                        for (int i = 0; i < npairs; i++) {
301                            matches[i] = getInt(pad +  8 + i * 8);
302                            offsets[i] = getInt(pad + 12 + i * 8);
303                        }
304                        return visitor.visitLookupSwitch(
305                                this, default_, npairs, matches, offsets, p);
306                    }
307                    default:
308                        throw new IllegalStateException();
309                }
310            }
311
312            case LOCAL:
313                return visitor.visitLocal(this, getUnsignedByte(1), p);
314
315            case LOCAL_BYTE:
316                return visitor.visitLocalAndValue(
317                        this, getUnsignedByte(1), getByte(2), p);
318
319            case SHORT:
320                return visitor.visitValue(this, getShort(1), p);
321
322            case WIDE_NO_OPERANDS:
323                return visitor.visitNoOperands(this, p);
324
325            case WIDE_LOCAL:
326                return visitor.visitLocal(this, getUnsignedShort(2), p);
327
328            case WIDE_CPREF_W:
329                return visitor.visitConstantPoolRef(this, getUnsignedShort(2), p);
330
331            case WIDE_CPREF_W_SHORT:
332                return visitor.visitConstantPoolRefAndValue(
333                        this, getUnsignedShort(2), getUnsignedByte(4), p);
334
335            case WIDE_LOCAL_SHORT:
336                return visitor.visitLocalAndValue(
337                        this, getUnsignedShort(2), getShort(4), p);
338
339            case UNKNOWN:
340                return visitor.visitUnknown(this, p);
341
342            default:
343                throw new IllegalStateException();
344        }
345    }
346
347    private static int align(int n) {
348        return (n + 3) & ~3;
349    }
350
351    private byte[] bytes;
352    private int pc;
353}
354