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 }