/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.bytecode; import static com.sun.cri.bytecode.Bytecodes.*; import static com.sun.max.vm.classfile.ErrorContext.*; import com.sun.cri.bytecode.*; import com.sun.max.annotate.*; import com.sun.max.program.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; /** * Visitor dispatch over byte codes. */ public final class BytecodeScanner { final BytecodeVisitor bytecodeVisitor; public BytecodeScanner(BytecodeVisitor bytecodeVisitor) { this.bytecodeVisitor = bytecodeVisitor; bytecodeVisitor.setBytecodeScanner(this); } protected BytecodeBlock bytecodeBlock; public BytecodeBlock bytecodeBlock() { return bytecodeBlock; } private int currentBCI; public int currentBCI() { return currentBCI; } protected boolean stopped; /** * Stop the scanning. */ public void stop() { stopped = true; } public boolean wasStopped() { return stopped; } protected int currentOpcode = -1; /** * Gets the most recently scanned opcode. Note that if this is called while in * {@link BytecodeVisitor#opcodeDecoded()} and the current instruction is widened then the {@link Bytecodes#WIDE} * opcode will be returned. If it's called while in the visitor method for the widened instruction then the widened * opcode is returned and {@link #isCurrentOpcodeWidened()} will return true. If it's called while in * {@link BytecodeVisitor#instructionDecoded()} then the opcode is returned and {@link #isCurrentOpcodeWidened()} * will return false. */ public int currentOpcode() { return currentOpcode; } protected int currentOpcodeBCI; @INLINE public int currentOpcodeBCI() { return currentOpcodeBCI; } protected boolean currentOpcodeWidened; public boolean canRunOffEnd; /** * @see #currentOpcode() */ public boolean isCurrentOpcodeWidened() { return currentOpcodeWidened; } /** * Gets a description of the current scan location in a string that resembles a line in a standard stack trace. * * @param classMethodActor the context of the bytecode being scanned */ public String getCurrentLocationAsString(ClassMethodActor classMethodActor) { final int lineNumber = classMethodActor.codeAttribute().lineNumberTable().findLineNumber(currentOpcodeBCI); final StringBuilder buf = new StringBuilder(); if (lineNumber != -1) { final ClassActor holder = classMethodActor.holder(); String sourceFileName = holder.sourceFileName; if (sourceFileName == null) { sourceFileName = "<unknown>"; } buf.append(classMethodActor.format("%H.%n(" + sourceFileName + ":" + lineNumber + ")")); } else { buf.append(classMethodActor.format("%H.%n(%p)")); } return buf.append(" [bytecode index=" + currentOpcodeBCI + ", opcode=" + currentOpcode + "]").toString(); } public byte readByte() { return bytecodeBlock.code()[currentBCI++]; } public int readUnsigned1() { return readByte() & 0xff; } public int readSigned1() { return readByte(); } public int readUnsigned2() { final int high = readByte() & 0xff; final int low = readByte() & 0xff; return (high << 8) | low; } public int readSigned2() { final int high = readByte(); final int low = readByte() & 0xff; return (high << 8) | low; } public int readSigned4() { final int b3 = readByte() << 24; final int b2 = (readByte() & 0xff) << 16; final int b1 = (readByte() & 0xff) << 8; final int b0 = readByte() & 0xff; return b3 | b2 | b1 | b0; } public int readSwitchCase() { return readSigned4(); } public int readSwitchOffset() { return readSigned4(); } private void alignAddress() { final int remainder = currentBCI % 4; if (remainder != 0) { currentBCI += 4 - remainder; } } private void wide() { currentOpcodeWidened = true; currentOpcode = readUnsigned1(); switch (currentOpcode) { case ILOAD: { bytecodeVisitor.iload(readUnsigned2()); break; } case LLOAD: { bytecodeVisitor.lload(readUnsigned2()); break; } case FLOAD: { bytecodeVisitor.fload(readUnsigned2()); break; } case DLOAD: { bytecodeVisitor.dload(readUnsigned2()); break; } case ALOAD: { bytecodeVisitor.aload(readUnsigned2()); break; } case ISTORE: { bytecodeVisitor.istore(readUnsigned2()); break; } case LSTORE: { bytecodeVisitor.lstore(readUnsigned2()); break; } case FSTORE: { bytecodeVisitor.fstore(readUnsigned2()); break; } case DSTORE: { bytecodeVisitor.dstore(readUnsigned2()); break; } case ASTORE: { bytecodeVisitor.astore(readUnsigned2()); break; } case IINC: { final int index2 = readUnsigned2(); final int addend = readSigned2(); bytecodeVisitor.iinc(index2, addend); break; } case RET: { bytecodeVisitor.ret(readUnsigned2()); break; } default: { int opcode = currentOpcode; if (!Bytecodes.isStandard(opcode)) { int length = Bytecodes.lengthOf(opcode); assert length != 0; boolean parsedAllBytes = bytecodeVisitor.extension(opcode, true); int endPos = currentOpcodeBCI + length - 1; if (parsedAllBytes) { assert currentBCI == endPos; } else { assert currentBCI <= endPos; currentBCI = endPos; } } else { bytecodeVisitor.unknown(opcode); } break; } } currentOpcodeWidened = false; } protected void scanInstruction() { currentOpcode = readUnsigned1(); bytecodeVisitor.opcodeDecoded(); if (stopped) { return; } switch (currentOpcode) { case NOP: { bytecodeVisitor.nop(); break; } case ACONST_NULL: { bytecodeVisitor.aconst_null(); break; } case ICONST_M1: { bytecodeVisitor.iconst_m1(); break; } case ICONST_0: { bytecodeVisitor.iconst_0(); break; } case ICONST_1: { bytecodeVisitor.iconst_1(); break; } case ICONST_2: { bytecodeVisitor.iconst_2(); break; } case ICONST_3: { bytecodeVisitor.iconst_3(); break; } case ICONST_4: { bytecodeVisitor.iconst_4(); break; } case ICONST_5: { bytecodeVisitor.iconst_5(); break; } case LCONST_0: { bytecodeVisitor.lconst_0(); break; } case LCONST_1: { bytecodeVisitor.lconst_1(); break; } case FCONST_0: { bytecodeVisitor.fconst_0(); break; } case FCONST_1: { bytecodeVisitor.fconst_1(); break; } case FCONST_2: { bytecodeVisitor.fconst_2(); break; } case DCONST_0: { bytecodeVisitor.dconst_0(); break; } case DCONST_1: { bytecodeVisitor.dconst_1(); break; } case BIPUSH: { final int operand = readSigned1(); bytecodeVisitor.bipush(operand); break; } case SIPUSH: { final int operand = readSigned2(); bytecodeVisitor.sipush(operand); break; } case LDC: { final int index = readUnsigned1(); bytecodeVisitor.ldc(index); break; } case LDC_W: { final int index = readUnsigned2(); bytecodeVisitor.ldc_w(index); break; } case LDC2_W: { final int index = readUnsigned2(); bytecodeVisitor.ldc2_w(index); break; } case ILOAD: { final int index = readUnsigned1(); bytecodeVisitor.iload(index); break; } case LLOAD: { final int index = readUnsigned1(); bytecodeVisitor.lload(index); break; } case FLOAD: { final int index = readUnsigned1(); bytecodeVisitor.fload(index); break; } case DLOAD: { final int index = readUnsigned1(); bytecodeVisitor.dload(index); break; } case ALOAD: { final int index = readUnsigned1(); bytecodeVisitor.aload(index); break; } case ILOAD_0: { bytecodeVisitor.iload_0(); break; } case ILOAD_1: { bytecodeVisitor.iload_1(); break; } case ILOAD_2: { bytecodeVisitor.iload_2(); break; } case ILOAD_3: { bytecodeVisitor.iload_3(); break; } case LLOAD_0: { bytecodeVisitor.lload_0(); break; } case LLOAD_1: { bytecodeVisitor.lload_1(); break; } case LLOAD_2: { bytecodeVisitor.lload_2(); break; } case LLOAD_3: { bytecodeVisitor.lload_3(); break; } case FLOAD_0: { bytecodeVisitor.fload_0(); break; } case FLOAD_1: { bytecodeVisitor.fload_1(); break; } case FLOAD_2: { bytecodeVisitor.fload_2(); break; } case FLOAD_3: { bytecodeVisitor.fload_3(); break; } case DLOAD_0: { bytecodeVisitor.dload_0(); break; } case DLOAD_1: { bytecodeVisitor.dload_1(); break; } case DLOAD_2: { bytecodeVisitor.dload_2(); break; } case DLOAD_3: { bytecodeVisitor.dload_3(); break; } case ALOAD_0: { bytecodeVisitor.aload_0(); break; } case ALOAD_1: { bytecodeVisitor.aload_1(); break; } case ALOAD_2: { bytecodeVisitor.aload_2(); break; } case ALOAD_3: { bytecodeVisitor.aload_3(); break; } case IALOAD: { bytecodeVisitor.iaload(); break; } case LALOAD: { bytecodeVisitor.laload(); break; } case FALOAD: { bytecodeVisitor.faload(); break; } case DALOAD: { bytecodeVisitor.daload(); break; } case AALOAD: { bytecodeVisitor.aaload(); break; } case BALOAD: { bytecodeVisitor.baload(); break; } case CALOAD: { bytecodeVisitor.caload(); break; } case SALOAD: { bytecodeVisitor.saload(); break; } case ISTORE: { final int index = readUnsigned1(); bytecodeVisitor.istore(index); break; } case LSTORE: { final int index = readUnsigned1(); bytecodeVisitor.lstore(index); break; } case FSTORE: { final int index = readUnsigned1(); bytecodeVisitor.fstore(index); break; } case DSTORE: { final int index = readUnsigned1(); bytecodeVisitor.dstore(index); break; } case ASTORE: { final int index = readUnsigned1(); bytecodeVisitor.astore(index); break; } case ISTORE_0: { bytecodeVisitor.istore_0(); break; } case ISTORE_1: { bytecodeVisitor.istore_1(); break; } case ISTORE_2: { bytecodeVisitor.istore_2(); break; } case ISTORE_3: { bytecodeVisitor.istore_3(); break; } case LSTORE_0: { bytecodeVisitor.lstore_0(); break; } case LSTORE_1: { bytecodeVisitor.lstore_1(); break; } case LSTORE_2: { bytecodeVisitor.lstore_2(); break; } case LSTORE_3: { bytecodeVisitor.lstore_3(); break; } case FSTORE_0: { bytecodeVisitor.fstore_0(); break; } case FSTORE_1: { bytecodeVisitor.fstore_1(); break; } case FSTORE_2: { bytecodeVisitor.fstore_2(); break; } case FSTORE_3: { bytecodeVisitor.fstore_3(); break; } case DSTORE_0: { bytecodeVisitor.dstore_0(); break; } case DSTORE_1: { bytecodeVisitor.dstore_1(); break; } case DSTORE_2: { bytecodeVisitor.dstore_2(); break; } case DSTORE_3: { bytecodeVisitor.dstore_3(); break; } case ASTORE_0: { bytecodeVisitor.astore_0(); break; } case ASTORE_1: { bytecodeVisitor.astore_1(); break; } case ASTORE_2: { bytecodeVisitor.astore_2(); break; } case ASTORE_3: { bytecodeVisitor.astore_3(); break; } case IASTORE: { bytecodeVisitor.iastore(); break; } case LASTORE: { bytecodeVisitor.lastore(); break; } case FASTORE: { bytecodeVisitor.fastore(); break; } case DASTORE: { bytecodeVisitor.dastore(); break; } case AASTORE: { bytecodeVisitor.aastore(); break; } case BASTORE: { bytecodeVisitor.bastore(); break; } case CASTORE: { bytecodeVisitor.castore(); break; } case SASTORE: { bytecodeVisitor.sastore(); break; } case POP: { bytecodeVisitor.pop(); break; } case POP2: { bytecodeVisitor.pop2(); break; } case DUP: { bytecodeVisitor.dup(); break; } case DUP_X1: { bytecodeVisitor.dup_x1(); break; } case DUP_X2: { bytecodeVisitor.dup_x2(); break; } case DUP2: { bytecodeVisitor.dup2(); break; } case DUP2_X1: { bytecodeVisitor.dup2_x1(); break; } case DUP2_X2: { bytecodeVisitor.dup2_x2(); break; } case SWAP: { bytecodeVisitor.swap(); break; } case IADD: { bytecodeVisitor.iadd(); break; } case LADD: { bytecodeVisitor.ladd(); break; } case FADD: { bytecodeVisitor.fadd(); break; } case DADD: { bytecodeVisitor.dadd(); break; } case ISUB: { bytecodeVisitor.isub(); break; } case LSUB: { bytecodeVisitor.lsub(); break; } case FSUB: { bytecodeVisitor.fsub(); break; } case DSUB: { bytecodeVisitor.dsub(); break; } case IMUL: { bytecodeVisitor.imul(); break; } case LMUL: { bytecodeVisitor.lmul(); break; } case FMUL: { bytecodeVisitor.fmul(); break; } case DMUL: { bytecodeVisitor.dmul(); break; } case IDIV: { bytecodeVisitor.idiv(); break; } case LDIV: { bytecodeVisitor.ldiv(); break; } case FDIV: { bytecodeVisitor.fdiv(); break; } case DDIV: { bytecodeVisitor.ddiv(); break; } case IREM: { bytecodeVisitor.irem(); break; } case LREM: { bytecodeVisitor.lrem(); break; } case FREM: { bytecodeVisitor.frem(); break; } case DREM: { bytecodeVisitor.drem(); break; } case INEG: { bytecodeVisitor.ineg(); break; } case LNEG: { bytecodeVisitor.lneg(); break; } case FNEG: { bytecodeVisitor.fneg(); break; } case DNEG: { bytecodeVisitor.dneg(); break; } case ISHL: { bytecodeVisitor.ishl(); break; } case LSHL: { bytecodeVisitor.lshl(); break; } case ISHR: { bytecodeVisitor.ishr(); break; } case LSHR: { bytecodeVisitor.lshr(); break; } case IUSHR: { bytecodeVisitor.iushr(); break; } case LUSHR: { bytecodeVisitor.lushr(); break; } case IAND: { bytecodeVisitor.iand(); break; } case LAND: { bytecodeVisitor.land(); break; } case IOR: { bytecodeVisitor.ior(); break; } case LOR: { bytecodeVisitor.lor(); break; } case IXOR: { bytecodeVisitor.ixor(); break; } case LXOR: { bytecodeVisitor.lxor(); break; } case IINC: { final int index = readUnsigned1(); final int addend = readSigned1(); bytecodeVisitor.iinc(index, addend); break; } case I2L: { bytecodeVisitor.i2l(); break; } case I2F: { bytecodeVisitor.i2f(); break; } case I2D: { bytecodeVisitor.i2d(); break; } case L2I: { bytecodeVisitor.l2i(); break; } case L2F: { bytecodeVisitor.l2f(); break; } case L2D: { bytecodeVisitor.l2d(); break; } case F2I: { bytecodeVisitor.f2i(); break; } case F2L: { bytecodeVisitor.f2l(); break; } case F2D: { bytecodeVisitor.f2d(); break; } case D2I: { bytecodeVisitor.d2i(); break; } case D2L: { bytecodeVisitor.d2l(); break; } case D2F: { bytecodeVisitor.d2f(); break; } case I2B: { bytecodeVisitor.i2b(); break; } case I2C: { bytecodeVisitor.i2c(); break; } case I2S: { bytecodeVisitor.i2s(); break; } case LCMP: { bytecodeVisitor.lcmp(); break; } case FCMPL: { bytecodeVisitor.fcmpl(); break; } case FCMPG: { bytecodeVisitor.fcmpg(); break; } case DCMPL: { bytecodeVisitor.dcmpl(); break; } case DCMPG: { bytecodeVisitor.dcmpg(); break; } case IFEQ: { final int offset = readSigned2(); bytecodeVisitor.ifeq(offset); break; } case IFNE: { final int offset = readSigned2(); bytecodeVisitor.ifne(offset); break; } case IFLT: { final int offset = readSigned2(); bytecodeVisitor.iflt(offset); break; } case IFGE: { final int offset = readSigned2(); bytecodeVisitor.ifge(offset); break; } case IFGT: { final int offset = readSigned2(); bytecodeVisitor.ifgt(offset); break; } case IFLE: { final int offset = readSigned2(); bytecodeVisitor.ifle(offset); break; } case IF_ICMPEQ: { final int offset = readSigned2(); bytecodeVisitor.if_icmpeq(offset); break; } case IF_ICMPNE: { final int offset = readSigned2(); bytecodeVisitor.if_icmpne(offset); break; } case IF_ICMPLT: { final int offset = readSigned2(); bytecodeVisitor.if_icmplt(offset); break; } case IF_ICMPGE: { final int offset = readSigned2(); bytecodeVisitor.if_icmpge(offset); break; } case IF_ICMPGT: { final int offset = readSigned2(); bytecodeVisitor.if_icmpgt(offset); break; } case IF_ICMPLE: { final int offset = readSigned2(); bytecodeVisitor.if_icmple(offset); break; } case IF_ACMPEQ: { final int offset = readSigned2(); bytecodeVisitor.if_acmpeq(offset); break; } case IF_ACMPNE: { final int offset = readSigned2(); bytecodeVisitor.if_acmpne(offset); break; } case GOTO: { final int offset = readSigned2(); bytecodeVisitor.goto_(offset); break; } case JSR: { final int offset = readSigned2(); bytecodeVisitor.jsr(offset); break; } case RET: { final int index = readUnsigned1(); bytecodeVisitor.ret(index); break; } case TABLESWITCH: { alignAddress(); final int defaultOffset = readSigned4(); final int lowMatch = readSigned4(); final int highMatch = readSigned4(); if (lowMatch > highMatch) { throw verifyError("Low must be less than or equal to high in TABLESWITCH"); } final int numberOfCases = highMatch - lowMatch + 1; final int start = currentBCI; bytecodeVisitor.tableswitch(defaultOffset, lowMatch, highMatch, numberOfCases); final int caseBytesRead = currentBCI - start; if ((caseBytesRead % 4) != 0 || (caseBytesRead >> 2) != numberOfCases) { throw ProgramError.unexpected("Bytecodes visitor did not consume exactly the offset operands of the tableswitch instruction at " + currentOpcodeBCI); } break; } case LOOKUPSWITCH: { alignAddress(); final int defaultOffset = readSigned4(); final int numberOfCases = readSigned4(); if (numberOfCases < 0) { throw verifyError("Number of keys in LOOKUPSWITCH less than 0"); } final int start = currentBCI; bytecodeVisitor.lookupswitch(defaultOffset, numberOfCases); final int caseBytesRead = currentBCI - start; if ((caseBytesRead % 8) != 0 || (caseBytesRead >> 3) != numberOfCases) { throw ProgramError.unexpected("Bytecodes visitor did not consume exactly the offset operands of the tableswitch instruction at " + currentOpcodeBCI); } break; } case IRETURN: { bytecodeVisitor.ireturn(); break; } case LRETURN: { bytecodeVisitor.lreturn(); break; } case FRETURN: { bytecodeVisitor.freturn(); break; } case DRETURN: { bytecodeVisitor.dreturn(); break; } case ARETURN: { bytecodeVisitor.areturn(); break; } case RETURN: { bytecodeVisitor.vreturn(); break; } case GETSTATIC: { final int index = readUnsigned2(); bytecodeVisitor.getstatic(index); break; } case PUTSTATIC: { final int index = readUnsigned2(); bytecodeVisitor.putstatic(index); break; } case GETFIELD: { final int index = readUnsigned2(); bytecodeVisitor.getfield(index); break; } case PUTFIELD: { final int index = readUnsigned2(); bytecodeVisitor.putfield(index); break; } case INVOKEVIRTUAL: { final int index = readUnsigned2(); bytecodeVisitor.invokevirtual(index); break; } case INVOKESPECIAL: { final int index = readUnsigned2(); bytecodeVisitor.invokespecial(index); break; } case INVOKESTATIC: { final int index = readUnsigned2(); bytecodeVisitor.invokestatic(index); break; } case INVOKEINTERFACE: { final int index = readUnsigned2(); final int count = readUnsigned1(); final byte zero = readByte(); if (zero != 0) { throw verifyError("Fourth operand byte of invokeinterface must be zero"); } bytecodeVisitor.invokeinterface(index, count); break; } case NEW: { final int index = readUnsigned2(); bytecodeVisitor.new_(index); break; } case NEWARRAY: { final int tag = readByte(); bytecodeVisitor.newarray(tag); break; } case ANEWARRAY: { final int index = readUnsigned2(); bytecodeVisitor.anewarray(index); break; } case ARRAYLENGTH: { bytecodeVisitor.arraylength(); break; } case ATHROW: { bytecodeVisitor.athrow(); break; } case CHECKCAST: { final int index = readUnsigned2(); bytecodeVisitor.checkcast(index); break; } case INSTANCEOF: { final int index = readUnsigned2(); bytecodeVisitor.instanceof_(index); break; } case MONITORENTER: { bytecodeVisitor.monitorenter(); break; } case MONITOREXIT: { bytecodeVisitor.monitorexit(); break; } case WIDE: { bytecodeVisitor.wide(); wide(); break; } case MULTIANEWARRAY: { final int index = readUnsigned2(); final int nDimensions = readUnsigned1(); bytecodeVisitor.multianewarray(index, nDimensions); break; } case IFNULL: { final int offset = readSigned2(); bytecodeVisitor.ifnull(offset); break; } case IFNONNULL: { final int offset = readSigned2(); bytecodeVisitor.ifnonnull(offset); break; } case GOTO_W: { final int offset = readSigned4(); bytecodeVisitor.goto_w(offset); break; } case JSR_W: { final int offset = readSigned4(); bytecodeVisitor.jsr_w(offset); break; } case BREAKPOINT: { bytecodeVisitor.breakpoint(); break; } case JNICALL: { final int nativeFunctionDescriptorIndex = readUnsigned2(); bytecodeVisitor.jnicall(nativeFunctionDescriptorIndex); break; } default: { int opcode = currentOpcode; if (!Bytecodes.isStandard(opcode)) { int length = Bytecodes.lengthOf(opcode); assert length != 0; boolean parsedAllBytes = bytecodeVisitor.extension(opcode, false); int endPos = currentOpcodeBCI + length; if (parsedAllBytes) { assert currentBCI == endPos; } else { assert currentBCI <= endPos; currentBCI = endPos; } } else { bytecodeVisitor.unknown(opcode); } break; } } bytecodeVisitor.instructionDecoded(); } public int scanInstruction(BytecodeBlock block) { this.bytecodeBlock = block; try { currentBCI = block.start; currentOpcodeBCI = currentBCI; scanInstruction(); return currentBCI; } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) { if (currentBCI > block.end) { throw verifyError("Ran off end of code"); } throw arrayIndexOutOfBoundsException; } } public int scanInstruction(byte[] bytecode, int startAddress) { return scanInstruction(new BytecodeBlock(bytecode, startAddress, bytecode.length)); } public void scan(BytecodeBlock block) { this.bytecodeBlock = block; try { currentBCI = block.start; currentOpcodeBCI = currentBCI; bytecodeVisitor.prologue(); while (!stopped && currentBCI <= block.end) { currentOpcodeBCI = currentBCI; scanInstruction(); } } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) { if (currentBCI > block.end) { throw verifyError("Ran off end of code"); } throw arrayIndexOutOfBoundsException; } } public void scan(ClassMethodActor classMethodActor) { scan(new BytecodeBlock(classMethodActor.codeAttribute().code())); } public void skipBytes(int numBytes) { currentBCI += numBytes; if (currentBCI > bytecodeBlock.end + 1) { if (!canRunOffEnd) { throw verifyError("Ran off end of code: " + currentBCI); } } } }