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 }