001/*
002 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.bytecode;
024
025/**
026 * A utility class that makes iterating over bytecodes and reading operands simpler and less error
027 * prone. For example, it handles the {@link Bytecodes#WIDE} instruction and wide variants of
028 * instructions internally.
029 */
030public final class BytecodeStream {
031
032    private final byte[] code;
033    private int opcode;
034    private int curBCI;
035    private int nextBCI;
036
037    /**
038     * Creates a new {@code BytecodeStream} for the specified bytecode.
039     *
040     * @param code the array of bytes that contains the bytecode
041     */
042    public BytecodeStream(byte[] code) {
043        assert code != null;
044        this.code = code;
045        setBCI(0);
046    }
047
048    /**
049     * Advances to the next bytecode.
050     */
051    public void next() {
052        setBCI(nextBCI);
053    }
054
055    /**
056     * Gets the next bytecode index (no side-effects).
057     *
058     * @return the next bytecode index
059     */
060    public int nextBCI() {
061        return nextBCI;
062    }
063
064    /**
065     * Gets the current bytecode index.
066     *
067     * @return the current bytecode index
068     */
069    public int currentBCI() {
070        return curBCI;
071    }
072
073    /**
074     * Gets the bytecode index of the end of the code.
075     *
076     * @return the index of the end of the code
077     */
078    public int endBCI() {
079        return code.length;
080    }
081
082    /**
083     * Gets the current opcode. This method will never return the {@link Bytecodes#WIDE WIDE}
084     * opcode, but will instead return the opcode that is modified by the {@code WIDE} opcode.
085     *
086     * @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code
087     */
088    public int currentBC() {
089        if (opcode == Bytecodes.WIDE) {
090            return Bytes.beU1(code, curBCI + 1);
091        } else {
092            return opcode;
093        }
094    }
095
096    /**
097     * Reads the index of a local variable for one of the load or store instructions. The WIDE
098     * modifier is handled internally.
099     *
100     * @return the index of the local variable
101     */
102    public int readLocalIndex() {
103        // read local variable index for load/store
104        if (opcode == Bytecodes.WIDE) {
105            return Bytes.beU2(code, curBCI + 2);
106        }
107        return Bytes.beU1(code, curBCI + 1);
108    }
109
110    /**
111     * Read the delta for an {@link Bytecodes#IINC} bytecode.
112     *
113     * @return the delta for the {@code IINC}
114     */
115    public int readIncrement() {
116        // read the delta for the iinc bytecode
117        if (opcode == Bytecodes.WIDE) {
118            return Bytes.beS2(code, curBCI + 4);
119        }
120        return Bytes.beS1(code, curBCI + 2);
121    }
122
123    /**
124     * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions.
125     *
126     * @return the destination bytecode index
127     */
128    public int readBranchDest() {
129        // reads the destination for a branch bytecode
130        if (opcode == Bytecodes.GOTO_W || opcode == Bytecodes.JSR_W) {
131            return curBCI + Bytes.beS4(code, curBCI + 1);
132        } else {
133            return curBCI + Bytes.beS2(code, curBCI + 1);
134        }
135    }
136
137    /**
138     * Read a signed 4-byte integer from the bytecode stream at the specified bytecode index.
139     *
140     * @param bci the bytecode index
141     * @return the integer value
142     */
143    public int readInt(int bci) {
144        // reads a 4-byte signed value
145        return Bytes.beS4(code, bci);
146    }
147
148    /**
149     * Reads an unsigned, 1-byte value from the bytecode stream at the specified bytecode index.
150     *
151     * @param bci the bytecode index
152     * @return the byte
153     */
154    public int readUByte(int bci) {
155        return Bytes.beU1(code, bci);
156    }
157
158    /**
159     * Reads a constant pool index for the current instruction.
160     *
161     * @return the constant pool index
162     */
163    public char readCPI() {
164        if (opcode == Bytecodes.LDC) {
165            return (char) Bytes.beU1(code, curBCI + 1);
166        }
167        return (char) Bytes.beU2(code, curBCI + 1);
168    }
169
170    /**
171     * Reads a constant pool index for an invokedynamic instruction.
172     *
173     * @return the constant pool index
174     */
175    public int readCPI4() {
176        assert opcode == Bytecodes.INVOKEDYNAMIC;
177        return Bytes.beS4(code, curBCI + 1);
178    }
179
180    /**
181     * Reads a signed, 1-byte value for the current instruction (e.g. BIPUSH).
182     *
183     * @return the byte
184     */
185    public byte readByte() {
186        return code[curBCI + 1];
187    }
188
189    /**
190     * Reads a signed, 2-byte short for the current instruction (e.g. SIPUSH).
191     *
192     * @return the short value
193     */
194    public short readShort() {
195        return (short) Bytes.beS2(code, curBCI + 1);
196    }
197
198    /**
199     * Sets the bytecode index to the specified value. If {@code bci} is beyond the end of the
200     * array, {@link #currentBC} will return {@link Bytecodes#END} and other methods may throw
201     * {@link ArrayIndexOutOfBoundsException}.
202     *
203     * @param bci the new bytecode index
204     */
205    public void setBCI(int bci) {
206        curBCI = bci;
207        if (curBCI < code.length) {
208            opcode = Bytes.beU1(code, bci);
209            assert opcode < Bytecodes.BREAKPOINT : "illegal bytecode";
210            nextBCI = bci + lengthOf();
211        } else {
212            opcode = Bytecodes.END;
213            nextBCI = curBCI;
214        }
215    }
216
217    /**
218     * Gets the length of the current bytecode.
219     */
220    private int lengthOf() {
221        int length = Bytecodes.lengthOf(opcode);
222        if (length == 0) {
223            switch (opcode) {
224                case Bytecodes.TABLESWITCH: {
225                    return new BytecodeTableSwitch(this, curBCI).size();
226                }
227                case Bytecodes.LOOKUPSWITCH: {
228                    return new BytecodeLookupSwitch(this, curBCI).size();
229                }
230                case Bytecodes.WIDE: {
231                    int opc = Bytes.beU1(code, curBCI + 1);
232                    if (opc == Bytecodes.RET) {
233                        return 4;
234                    } else if (opc == Bytecodes.IINC) {
235                        return 6;
236                    } else {
237                        return 4; // a load or store bytecode
238                    }
239                }
240                default:
241                    throw new Error("unknown variable-length bytecode: " + opcode);
242            }
243        }
244        return length;
245    }
246}