1 /*
   2  * Copyright (c) 2009, 2018, 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 
  24 
  25 package org.graalvm.compiler.bytecode;
  26 
  27 /**
  28  * A utility class that makes iterating over bytecodes and reading operands simpler and less error
  29  * prone. For example, it handles the {@link Bytecodes#WIDE} instruction and wide variants of
  30  * instructions internally.
  31  */
  32 public final class BytecodeStream {
  33 
  34     private final byte[] code;
  35     private int opcode;
  36     private int curBCI;
  37     private int nextBCI;
  38 
  39     /**
  40      * Creates a new {@code BytecodeStream} for the specified bytecode.
  41      *
  42      * @param code the array of bytes that contains the bytecode
  43      */
  44     public BytecodeStream(byte[] code) {
  45         assert code != null;
  46         this.code = code;
  47         setBCI(0);
  48     }
  49 
  50     /**
  51      * Advances to the next bytecode.
  52      */
  53     public void next() {
  54         setBCI(nextBCI);
  55     }
  56 
  57     /**
  58      * Gets the next bytecode index (no side-effects).
  59      *
  60      * @return the next bytecode index
  61      */
  62     public int nextBCI() {
  63         return nextBCI;
  64     }
  65 
  66     /**
  67      * Gets the current bytecode index.
  68      *
  69      * @return the current bytecode index
  70      */
  71     public int currentBCI() {
  72         return curBCI;
  73     }
  74 
  75     /**
  76      * Gets the bytecode index of the end of the code.
  77      *
  78      * @return the index of the end of the code
  79      */
  80     public int endBCI() {
  81         return code.length;
  82     }
  83 
  84     /**
  85      * Gets the current opcode. This method will never return the {@link Bytecodes#WIDE WIDE}
  86      * opcode, but will instead return the opcode that is modified by the {@code WIDE} opcode.
  87      *
  88      * @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code
  89      */
  90     public int currentBC() {
  91         if (opcode == Bytecodes.WIDE) {
  92             return Bytes.beU1(code, curBCI + 1);
  93         } else {
  94             return opcode;
  95         }
  96     }
  97 
  98     /**
  99      * Reads the index of a local variable for one of the load or store instructions. The WIDE
 100      * modifier is handled internally.
 101      *
 102      * @return the index of the local variable
 103      */
 104     public int readLocalIndex() {
 105         // read local variable index for load/store
 106         if (opcode == Bytecodes.WIDE) {
 107             return Bytes.beU2(code, curBCI + 2);
 108         }
 109         return Bytes.beU1(code, curBCI + 1);
 110     }
 111 
 112     /**
 113      * Read the delta for an {@link Bytecodes#IINC} bytecode.
 114      *
 115      * @return the delta for the {@code IINC}
 116      */
 117     public int readIncrement() {
 118         // read the delta for the iinc bytecode
 119         if (opcode == Bytecodes.WIDE) {
 120             return Bytes.beS2(code, curBCI + 4);
 121         }
 122         return Bytes.beS1(code, curBCI + 2);
 123     }
 124 
 125     /**
 126      * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions.
 127      *
 128      * @return the destination bytecode index
 129      */
 130     public int readBranchDest() {
 131         // reads the destination for a branch bytecode
 132         if (opcode == Bytecodes.GOTO_W || opcode == Bytecodes.JSR_W) {
 133             return curBCI + Bytes.beS4(code, curBCI + 1);
 134         } else {
 135             return curBCI + Bytes.beS2(code, curBCI + 1);
 136         }
 137     }
 138 
 139     /**
 140      * Read a signed 4-byte integer from the bytecode stream at the specified bytecode index.
 141      *
 142      * @param bci the bytecode index
 143      * @return the integer value
 144      */
 145     public int readInt(int bci) {
 146         // reads a 4-byte signed value
 147         return Bytes.beS4(code, bci);
 148     }
 149 
 150     /**
 151      * Reads an unsigned, 1-byte value from the bytecode stream at the specified bytecode index.
 152      *
 153      * @param bci the bytecode index
 154      * @return the byte
 155      */
 156     public int readUByte(int bci) {
 157         return Bytes.beU1(code, bci);
 158     }
 159 
 160     /**
 161      * Reads a constant pool index for the current instruction.
 162      *
 163      * @return the constant pool index
 164      */
 165     public char readCPI() {
 166         if (opcode == Bytecodes.LDC) {
 167             return (char) Bytes.beU1(code, curBCI + 1);
 168         }
 169         return (char) Bytes.beU2(code, curBCI + 1);
 170     }
 171 
 172     /**
 173      * Reads a constant pool index for an invokedynamic instruction.
 174      *
 175      * @return the constant pool index
 176      */
 177     public int readCPI4() {
 178         assert opcode == Bytecodes.INVOKEDYNAMIC;
 179         return Bytes.beS4(code, curBCI + 1);
 180     }
 181 
 182     /**
 183      * Reads a signed, 1-byte value for the current instruction (e.g. BIPUSH).
 184      *
 185      * @return the byte
 186      */
 187     public byte readByte() {
 188         return code[curBCI + 1];
 189     }
 190 
 191     /**
 192      * Reads a signed, 2-byte short for the current instruction (e.g. SIPUSH).
 193      *
 194      * @return the short value
 195      */
 196     public short readShort() {
 197         return (short) Bytes.beS2(code, curBCI + 1);
 198     }
 199 
 200     /**
 201      * Sets the bytecode index to the specified value. If {@code bci} is beyond the end of the
 202      * array, {@link #currentBC} will return {@link Bytecodes#END} and other methods may throw
 203      * {@link ArrayIndexOutOfBoundsException}.
 204      *
 205      * @param bci the new bytecode index
 206      */
 207     public void setBCI(int bci) {
 208         curBCI = bci;
 209         if (curBCI < code.length) {
 210             opcode = Bytes.beU1(code, bci);
 211             assert opcode < Bytecodes.BREAKPOINT : "illegal bytecode";
 212             nextBCI = bci + lengthOf();
 213         } else {
 214             opcode = Bytecodes.END;
 215             nextBCI = curBCI;
 216         }
 217     }
 218 
 219     /**
 220      * Gets the length of the current bytecode.
 221      */
 222     private int lengthOf() {
 223         int length = Bytecodes.lengthOf(opcode);
 224         if (length == 0) {
 225             switch (opcode) {
 226                 case Bytecodes.TABLESWITCH: {
 227                     return new BytecodeTableSwitch(this, curBCI).size();
 228                 }
 229                 case Bytecodes.LOOKUPSWITCH: {
 230                     return new BytecodeLookupSwitch(this, curBCI).size();
 231                 }
 232                 case Bytecodes.WIDE: {
 233                     int opc = Bytes.beU1(code, curBCI + 1);
 234                     if (opc == Bytecodes.RET) {
 235                         return 4;
 236                     } else if (opc == Bytecodes.IINC) {
 237                         return 6;
 238                     } else {
 239                         return 4; // a load or store bytecode
 240                     }
 241                 }
 242                 default:
 243                     throw new Error("unknown variable-length bytecode: " + opcode);
 244             }
 245         }
 246         return length;
 247     }
 248 }