1 /*
   2  * Copyright (c) 2009, 2011, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.bytecode;
  24 
  25 /**
  26  * A utility class that makes iterating over bytecodes and reading operands simpler and less error
  27  * prone. For example, it handles the {@link Bytecodes#WIDE} instruction and wide variants of
  28  * instructions internally.
  29  */
  30 public final class BytecodeStream {
  31 
  32     private final byte[] code;
  33     private int opcode;
  34     private int curBCI;
  35     private int nextBCI;
  36 
  37     /**
  38      * Creates a new {@code BytecodeStream} for the specified bytecode.
  39      *
  40      * @param code the array of bytes that contains the bytecode
  41      */
  42     public BytecodeStream(byte[] code) {
  43         assert code != null;
  44         this.code = code;
  45         setBCI(0);
  46     }
  47 
  48     /**
  49      * Advances to the next bytecode.
  50      */
  51     public void next() {
  52         setBCI(nextBCI);
  53     }
  54 
  55     /**
  56      * Gets the next bytecode index (no side-effects).
  57      *
  58      * @return the next bytecode index
  59      */
  60     public int nextBCI() {
  61         return nextBCI;
  62     }
  63 
  64     /**
  65      * Gets the current bytecode index.
  66      *
  67      * @return the current bytecode index
  68      */
  69     public int currentBCI() {
  70         return curBCI;
  71     }
  72 
  73     /**
  74      * Gets the bytecode index of the end of the code.
  75      *
  76      * @return the index of the end of the code
  77      */
  78     public int endBCI() {
  79         return code.length;
  80     }
  81 
  82     /**
  83      * Gets the current opcode. This method will never return the {@link Bytecodes#WIDE WIDE}
  84      * opcode, but will instead return the opcode that is modified by the {@code WIDE} opcode.
  85      *
  86      * @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code
  87      */
  88     public int currentBC() {
  89         if (opcode == Bytecodes.WIDE) {
  90             return Bytes.beU1(code, curBCI + 1);
  91         } else {
  92             return opcode;
  93         }
  94     }
  95 
  96     /**
  97      * Reads the index of a local variable for one of the load or store instructions. The WIDE
  98      * modifier is handled internally.
  99      *
 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 }