/* * Copyright (c) 2007, 2012, 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.tele.interpreter; import static com.oracle.max.cri.intrinsics.IntrinsicIDs.*; import static com.sun.cri.bytecode.Bytecodes.*; import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*; import java.io.*; import java.lang.reflect.*; import java.util.*; import com.oracle.max.cri.intrinsics.*; import com.sun.cri.bytecode.*; import com.sun.max.lang.*; import com.sun.max.program.*; import com.sun.max.tele.*; import com.sun.max.tele.data.*; import com.sun.max.tele.reference.*; import com.sun.max.tele.util.*; import com.sun.max.tele.value.*; import com.sun.max.unsafe.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.bytecode.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.layout.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.type.*; import com.sun.max.vm.value.*; /** * Entry points and utility functions for the interpreter. * */ public final class TeleInterpreter { private final TeleVM vm; private Machine machine; private Value returnValue; private int instructionsExecuted; public TeleInterpreter(TeleVM vm) { this.vm = vm; } /** * Creates an interpreter instance and uses it to execute a given method with the given arguments. * Note that arguments must be dynamic types seen by the JavaPrototyper as legitimate VM classes. * * @param vm the remote VM * @param classMethodActor the method to be executed * @param args the arguments to passed to the method for execution * @return the result of the execution * @throws TeleInterpreterException if an uncaught exception occurs during execution of the method */ public static Value execute(TeleVM vm, ClassMethodActor classMethodActor, Value... args) throws TeleInterpreterException { return new TeleInterpreter(vm).run(classMethodActor, args); } /** * Creates an interpreter instance and uses it to execute a given method with the given arguments. * * @param vm the remote VM * @param declaringClassName the name of the class that declares the method to be executed * @param name the name of the method to be executed * @param signature the signature of the method to be executed * @param args the arguments to passed to the method for execution * @return the result of the execution * @throws TeleInterpreterException if an uncaught exception occurs during execution of the method * @throws NoSuchMethodError if the specified method cannot be found */ public static Value execute(TeleVM vm, String declaringClassName, String name, SignatureDescriptor signature, Value... args) throws TeleInterpreterException { ClassActor classActor; ClassMethodActor classMethodActor; classActor = HostedVMClassLoader.HOSTED_VM_CLASS_LOADER.mustMakeClassActor(JavaTypeDescriptor.getDescriptorForJavaString(declaringClassName)); classMethodActor = classActor.findClassMethodActor(SymbolTable.makeSymbol(name), signature); if (classMethodActor == null) { throw new NoSuchMethodError(declaringClassName + "." + name + signature); } return execute(vm, classMethodActor, args); } /** * Creates an interpreter instance and uses it to execute a given method with the given arguments. * * @param vm the remote VM * @param declaringClass the class that declares the method to be executed * @param name the name of the method to be executed * @param signature the signature of the method to be executed * @param args the arguments to passed to the method for execution * @return the result of the execution * @throws TeleInterpreterException if an uncaught exception occurs during execution of the method */ public static Value execute(TeleVM vm, Class declaringClass, String name, SignatureDescriptor signature, Value... args) throws TeleInterpreterException { ClassActor classActor; ClassMethodActor classMethodActor; classActor = HostedVMClassLoader.HOSTED_VM_CLASS_LOADER.mustMakeClassActor(JavaTypeDescriptor.forJavaClass(declaringClass)); classMethodActor = classActor.findClassMethodActor(SymbolTable.makeSymbol(name), signature); if (classMethodActor == null) { throw new NoSuchMethodError(declaringClass.getName() + "." + name + signature); } return execute(vm, classMethodActor, args); } /** * A lazy constructed cache of mappings from opcode positions to disassembled bytecode instructions. */ private Map<MethodActor, Map<Integer, String>> bytecodeTraces; /** * The last frame traced in {@link #traceExecution()}. */ private ExecutionFrame lastTracedFrame; /** * Traces the current execution point. */ private void traceExecution() { if (Trace.hasLevel(2)) { PrintStream stream = Trace.stream(); ExecutionFrame frame = machine.currentThread().frame(); int depth = frame.depth(); if (lastTracedFrame == null) { stream.println("Interpreter: " + Strings.spaces(depth * 2) + "ENTERING: " + frame.method().format("%H.%n(%p)")); } else if (lastTracedFrame != frame) { int lastFrameDepth = lastTracedFrame.depth(); if (lastFrameDepth < depth) { stream.println("Interpreter: " + Strings.spaces(depth * 2) + "ENTERING: " + frame.method().format("%H.%n(%p)")); } else { stream.println("Interpreter: " + Strings.spaces(lastFrameDepth * 2) + "EXITING: " + lastTracedFrame.method().format("%H.%n(%p)")); } } if (Trace.hasLevel(3)) { if (bytecodeTraces == null) { bytecodeTraces = new HashMap<MethodActor, Map<Integer, String>>(); } Map<Integer, String> bcpToTrace = bytecodeTraces.get(machine.currentMethod()); if (bcpToTrace == null) { bcpToTrace = new HashMap<Integer, String>(); bytecodeTraces.put(machine.currentMethod(), bcpToTrace); ConstantPool constantPool = frame.constantPool(); BytecodeBlock bytecodeBlock = new BytecodeBlock(frame.code()); String[] instructions = BytecodePrinter.toString(constantPool, bytecodeBlock, "", "\0", 0).split("\0"); for (String instruction : instructions) { int colonIndex = instruction.indexOf(':'); assert colonIndex != -1 : "instruction trace does not start with expected '<bcp>:': " + instruction; try { int bcp = Integer.parseInt(instruction.substring(0, colonIndex)); bcpToTrace.put(bcp, instruction); } catch (NumberFormatException numberFormatException) { TeleWarning.message("instruction trace does not start with expected '<bcp>:': " + instruction); } } } stream.println("Interpreter: " + Strings.spaces(depth * 2) + bcpToTrace.get(frame.currentOpcodePosition())); } lastTracedFrame = frame; stream.flush(); } } public Value run(ClassMethodActor classMethodActor, Value... arguments) throws TeleInterpreterException { machine = new Machine(vm); machine.pushFrame(classMethodActor); int j = 0; for (int i = 0; i < arguments.length; i++, j++) { setLocal(j, arguments[i]); if (arguments[i].isCategory2()) { j++; } } int opcode; MethodStatus status; instructionsExecuted = 0; while (true) { opcode = machine.readOpcode(); traceExecution(); try { boolean isWide = false; if (opcode == WIDE) { opcode = machine.readOpcode(); isWide = true; } status = interpret(opcode, isWide); if (status == MethodStatus.METHOD_END) { break; } } catch (TeleInterpreterException executionException) { ReferenceValue throwableReference = executionException.throwableReference(); boolean handled = machine.handleException(throwableReference); //if this succeeds we keep looping if (!handled) { throw executionException; } } catch (Throwable throwable) { ReferenceValue throwableReference = ReferenceValue.from(throwable); boolean handled = machine.handleException(throwableReference); //if this succeeds we keep looping if (!handled) { throw new TeleInterpreterException(throwable, machine); } } finally { instructionsExecuted++; } } if (returnValue instanceof TeleReferenceValue) { returnValue = TeleReferenceValue.from(vm, machine.makeLocalReference((RemoteReference) returnValue.asReference())); } Kind resultKind = classMethodActor.resultKind(); if (resultKind.stackKind == Kind.INT) { returnValue = resultKind.convert(returnValue); } return returnValue; } private Value pop() { return machine.pop(); } private Value popCheckZero() throws TeleInterpreterException { Value value = machine.pop(); if (value.isZero()) { machine.raiseException(new ArithmeticException("Division by zero")); } return value; } private void push(boolean value) { push(value ? IntValue.ONE : IntValue.ZERO); } private void push(Value value) { assert value.kind().stackKind == value.kind(); machine.push(value); } private void jumpIf(boolean condition, int offset) { if (condition) { machine.jump(offset); } } private void push(int value) { push(IntValue.from(value)); } private void setLocal(int index, Value value) { machine.setLocal(index, value); } private Value local(int index) { return machine.getLocal(index); } private short readS2() { return machine.readShort(); } private byte readS1() { return machine.readByte(); } private int readU2() { return machine.readShort() & 0xffff; } private int readU1() { return machine.readByte() & 0xff; } private void pointerLoad(MethodActor method) throws TeleInterpreterException { Kind kind = method.descriptor().resultKind(); if (method.descriptor().argumentCount(true) == 2) { Value offsetVal = pop(); Offset off = offsetVal.kind() == Kind.INT ? Offset.fromInt(offsetVal.asInt()) : offsetVal.asWord().asOffset(); Pointer ptr = pop().asWord().asPointer(); DataAccess dataAccess = vm.teleProcess().dataAccess(); switch (kind.asEnum) { // Checkstyle: stop case BYTE: push(IntValue.from(dataAccess.readByte(ptr, off))); break; case CHAR: push(IntValue.from(dataAccess.readChar(ptr, off))); break; case SHORT: push(IntValue.from(dataAccess.readShort(ptr, off))); break; case INT: push(IntValue.from(dataAccess.readInt(ptr, off))); break; case LONG: push(LongValue.from(dataAccess.readLong(ptr, off))); break; case FLOAT: push(FloatValue.from(dataAccess.readFloat(ptr, off))); break; case DOUBLE: push(DoubleValue.from(dataAccess.readDouble(ptr, off))); break; case WORD: push(WordValue.from(dataAccess.readWord(ptr, off))); break; case REFERENCE: push(machine.toReferenceValue(vm.referenceManager().makeReference(dataAccess.readWord(ptr, off).asAddress()))); break; default: machine.raiseException(new ClassFormatError("Invalid pointer load kind: " + kind)); // Checkstyle: resume } } else { assert method.descriptor().argumentCount(true) == 3; assert method.descriptor().parameterDescriptorAt(0).toKind() == Kind.WORD; assert method.descriptor().parameterDescriptorAt(1).toKind() == Kind.INT; assert method.descriptor().parameterDescriptorAt(2).toKind() == Kind.INT; int index = pop().asInt(); int disp = pop().asInt(); Pointer ptr = pop().asWord().asPointer(); DataAccess dataAccess = vm.teleProcess().dataAccess(); switch (kind.asEnum) { // Checkstyle: stop case BYTE: push(IntValue.from(dataAccess.getByte(ptr, disp, index))); break; case CHAR: push(IntValue.from(dataAccess.getChar(ptr, disp, index))); break; case SHORT: push(IntValue.from(dataAccess.getShort(ptr, disp, index))); break; case INT: push(IntValue.from(dataAccess.getInt(ptr, disp, index))); break; case LONG: push(LongValue.from(dataAccess.getLong(ptr, disp, index))); break; case FLOAT: push(FloatValue.from(dataAccess.getFloat(ptr, disp, index))); break; case DOUBLE: push(DoubleValue.from(dataAccess.getDouble(ptr, disp, index))); break; case WORD: push(WordValue.from(dataAccess.getWord(ptr, disp, index))); break; case REFERENCE: push(machine.toReferenceValue(vm.referenceManager().makeReference(dataAccess.getWord(ptr, disp, index).asAddress()))); break; default: machine.raiseException(new ClassFormatError("Invalid pointer load kind: " + kind)); // Checkstyle: resume } } } private void pointerGet(Kind kind) throws TeleInterpreterException { } private void arrayLoad(Kind kind) throws TeleInterpreterException { int index = machine.pop().asInt(); RemoteReference array = (RemoteReference) machine.pop().asReference(); if (array.isZero()) { machine.raiseException(new NullPointerException()); } if (Layout.readArrayLength(array) <= index || index < 0) { machine.raiseException(new ArrayIndexOutOfBoundsException()); } switch (kind.asEnum) { // Checkstyle: stop case BYTE: if (machine.toReferenceValue(array).getClassActor() == ClassRegistry.BOOLEAN_ARRAY) { push(Layout.getBoolean(array, index) ? IntValue.ONE : IntValue.ZERO); } else { push(IntValue.from(Layout.getByte(array, index))); } break; case CHAR: push(IntValue.from(Layout.getChar(array, index))); break; case SHORT: push(IntValue.from(Layout.getShort(array, index))); break; case INT: push(IntValue.from(Layout.getInt(array, index))); break; case LONG: push(LongValue.from(Layout.getLong(array, index))); break; case FLOAT: push(FloatValue.from(Layout.getFloat(array, index))); break; case DOUBLE: push(DoubleValue.from(Layout.getDouble(array, index))); break; case REFERENCE: push(machine.toReferenceValue((RemoteReference) Layout.getReference(array, index))); break; default: machine.raiseException(new ClassFormatError("Invalid array kind: " + kind)); // Checkstyle: resume } } private void arrayStore(Kind kind) throws TeleInterpreterException { Value val = pop(); int index = pop().toInt(); Reference array = pop().asReference(); if (array.isZero()) { machine.raiseException(new NullPointerException()); } if (Layout.readArrayLength(array) <= index || index < 0) { machine.raiseException(new ArrayIndexOutOfBoundsException()); } switch (kind.asEnum) { // Checkstyle: stop case BYTE: Layout.setByte(array, index, val.toByte()); break; case CHAR: Layout.setChar(array, index, val.toChar()); break; case SHORT: Layout.setShort(array, index, val.toShort()); break; case INT: Layout.setInt(array, index, val.toInt()); break; case LONG: Layout.setLong(array, index, val.toLong()); break; case FLOAT: Layout.setFloat(array, index, val.toFloat()); break; case DOUBLE: Layout.setDouble(array, index, val.toDouble()); break; case REFERENCE: Layout.setReference(array, index, val.asReference()); break; default: machine.raiseException(new ClassFormatError("Invalid array kind: " + kind)); // Checkstyle: resume } } private int minus1IfWordWidth(int bitPosition) { return bitPosition == Word.widthValue().numberOfBits ? -1 : bitPosition; } private MethodStatus interpret(int opcode, boolean isWide) throws Throwable { switch (opcode) { // Checkstyle: stop case NOP: break; case ACONST_NULL: push(ReferenceValue.NULL); break; case ICONST_M1: push(IntValue.from(-1)); break; case ICONST_0: push(IntValue.ZERO); break; case ICONST_1: push(IntValue.ONE); break; case ICONST_2: push(IntValue.TWO); break; case ICONST_3: push(IntValue.THREE); break; case ICONST_4: push(IntValue.FOUR); break; case ICONST_5: push(IntValue.FIVE); break; case LCONST_0: push(LongValue.ZERO); break; case LCONST_1: push(LongValue.ONE); break; case FCONST_0: push(FloatValue.ZERO); break; case FCONST_1: push(FloatValue.ONE); break; case FCONST_2: push(FloatValue.TWO); break; case DCONST_0: push(DoubleValue.ZERO); break; case DCONST_1: push(DoubleValue.ONE); break; case BIPUSH: push(IntValue.from(readS1())); break; case SIPUSH: push(IntValue.from(readS2())); break; case LDC: push(machine.resolveConstantReference(readU1())); break; case LDC_W: case LDC2_W: push(machine.resolveConstantReference(readU2())); break; case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: push(local(isWide ? readU2() : readU1())); break; case ILOAD_0: case ILOAD_1: case ILOAD_2: case ILOAD_3: push(local(opcode - ILOAD_0)); break; case LLOAD_0: case LLOAD_1: case LLOAD_2: case LLOAD_3: push(local(opcode - LLOAD_0)); break; case FLOAD_0: case FLOAD_1: case FLOAD_2: case FLOAD_3: push(local(opcode - FLOAD_0)); break; case DLOAD_0: case DLOAD_1: case DLOAD_2: case DLOAD_3: push(local(opcode - DLOAD_0)); break; case ALOAD_0: case ALOAD_1: case ALOAD_2: case ALOAD_3: push(local(opcode - ALOAD_0)); break; case IALOAD: arrayLoad(Kind.INT); break; case LALOAD: arrayLoad(Kind.LONG); break; case FALOAD: arrayLoad(Kind.FLOAT); break; case DALOAD: arrayLoad(Kind.DOUBLE); break; case AALOAD: arrayLoad(Kind.REFERENCE); break; case BALOAD: arrayLoad(Kind.BYTE); break; case CALOAD: arrayLoad(Kind.CHAR); break; case SALOAD: arrayLoad(Kind.SHORT); break; case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: setLocal(isWide ? readU2() : readU1(), pop()); break; case ISTORE_0: case ISTORE_1: case ISTORE_2: case ISTORE_3: setLocal(opcode - ISTORE_0, pop()); break; case LSTORE_0: case LSTORE_1: case LSTORE_2: case LSTORE_3: setLocal(opcode - LSTORE_0, pop()); break; case FSTORE_0: case FSTORE_1: case FSTORE_2: case FSTORE_3: setLocal(opcode - FSTORE_0, pop()); break; case DSTORE_0: case DSTORE_1: case DSTORE_2: case DSTORE_3: setLocal(opcode - DSTORE_0, pop()); break; case ASTORE_0: case ASTORE_1: case ASTORE_2: case ASTORE_3: setLocal(opcode - ASTORE_0, pop()); break; case IASTORE: arrayStore(Kind.INT); break; case LASTORE: arrayStore(Kind.LONG); break; case FASTORE: arrayStore(Kind.FLOAT); break; case DASTORE: arrayStore(Kind.DOUBLE); break; case AASTORE: arrayStore(Kind.REFERENCE); break; case BASTORE: arrayStore(Kind.BYTE); break; case CASTORE: arrayStore(Kind.CHAR); break; case SASTORE: arrayStore(Kind.SHORT); break; case POP: pop(); break; case POP2: if (!pop().isCategory2()) pop(); break; case DUP: push(machine.peek()); break; case DUP_X1: { Value s1 = pop(); Value s2 = pop(); push(s1); push(s2); push(s1); break; } case DUP_X2: { Value s1 = pop(); Value s2 = pop(); if (s2.isCategory2()) { push(s1); push(s2); push(s1); } else { Value s3 = pop(); push(s1); push(s3); push(s2); push(s1); } break; } case DUP2: { Value s1 = pop(); if (s1.isCategory2()) { push(s1); push(s1); } else { Value s2 = pop(); push(s2); push(s1); push(s2); push(s1); } break; } case DUP2_X1: { Value s1 = pop(); if (s1.isCategory2()) { Value s2 = pop(); push(s1); push(s2); push(s1); } else { Value s2 = pop(); Value s3 = pop(); push(s2); push(s1); push(s3); push(s2); push(s1); } break; } case DUP2_X2: { Value s1 = pop(); if (s1.isCategory2()) { Value s2 = pop(); if (s2.isCategory2()) { push(s1); push(s2); push(s1); } else { Value s3 = pop(); push(s1); push(s3); push(s2); push(s1); } } else { Value s2 = pop(); Value s3 = pop(); if (s3.isCategory2()) { push(s2); push(s1); push(s3); push(s2); push(s1); } else { Value s4 = pop(); push(s2); push(s1); push(s4); push(s3); push(s2); push(s1); } } break; } case SWAP: { Value s1 = pop(); Value s2 = pop(); push(s1); push(s2); break; } case IADD: push(IntValue.from(pop().asInt() + pop().asInt())); break; case LADD: push(LongValue.from(pop().asLong() + pop().asLong())); break; case FADD: push(FloatValue.from(pop().asFloat() + pop().asFloat())); break; case DADD: push(DoubleValue.from(pop().asDouble() + pop().asDouble())); break; case ISUB: push(IntValue.from(-pop().asInt() + pop().asInt())); break; case LSUB: push(LongValue.from(-pop().asLong() + pop().asLong())); break; case FSUB: push(FloatValue.from(-pop().asFloat() + pop().asFloat())); break; case DSUB: push(DoubleValue.from(-pop().asDouble() + pop().asDouble())); break; case IMUL: push(IntValue.from(pop().asInt() * pop().asInt())); break; case LMUL: push(LongValue.from(pop().asLong() * pop().asLong())); break; case FMUL: push(FloatValue.from(pop().asFloat() * pop().asFloat())); break; case DMUL: push(DoubleValue.from(pop().asDouble() * pop().asDouble())); break; case IDIV: { int v2 = popCheckZero().asInt(); push(IntValue.from(pop().asInt() / v2)); break; } case LDIV: { long v2 = popCheckZero().asLong(); push(LongValue.from(pop().asLong() / v2)); break; } case FDIV: { float v2 = popCheckZero().asFloat(); push(FloatValue.from(pop().asFloat() / v2)); break; } case DDIV: { double v2 = popCheckZero().asDouble(); push(DoubleValue.from(pop().asDouble() / v2)); break; } case IREM: { int v2 = popCheckZero().asInt(); push(IntValue.from(pop().asInt() % v2)); break; } case LREM: { long v2 = popCheckZero().asLong(); push(LongValue.from(pop().asLong() % v2)); break; } case FREM: { float v2 = popCheckZero().asFloat(); push(FloatValue.from(pop().asFloat() % v2)); break; } case DREM: { double v2 = popCheckZero().asDouble(); push(DoubleValue.from(pop().asDouble() % v2)); break; } case INEG: push(IntValue.from(0 - pop().asInt())); break; case LNEG: push(LongValue.from(0 - pop().asLong())); break; case FNEG: push(FloatValue.from((float) 0.0 - pop().asFloat())); break; case DNEG: push(DoubleValue.from(0.0 - pop().asDouble())); break; case ISHL: { int amount = pop().asInt(); int value = pop().asInt(); push(IntValue.from(value << (amount & 0x1F))); break; } case LSHL: { int amount = pop().asInt(); long value = pop().asLong(); push(LongValue.from(value << (amount & 0x3F))); break; } case ISHR: { int amount = pop().asInt(); int value = pop().asInt(); push(IntValue.from(value >> (amount & 0x1F))); break; } case LSHR: { int amount = pop().asInt(); long value = pop().asLong(); push(LongValue.from(value >> (amount & 0x3F))); break; } case IUSHR: { int amount = pop().asInt(); int value = pop().asInt(); push(IntValue.from(value >>> (amount & 0x1F))); break; } case LUSHR: { int amount = pop().asInt(); long value = pop().asLong(); push(LongValue.from(value >>> (amount & 0x3F))); break; } case IAND: push(IntValue.from(pop().asInt() & pop().asInt())); break; case LAND: push(LongValue.from(pop().asLong() & pop().asLong())); break; case IOR: push(IntValue.from(pop().asInt() | pop().asInt())); break; case LOR: push(LongValue.from(pop().asLong() | pop().asLong())); break; case IXOR: push(IntValue.from(pop().asInt() ^ pop().asInt())); break; case LXOR: push(LongValue.from(pop().asLong() ^ pop().asLong())); break; case IINC: { int index = isWide ? readU2() : readU1(); int increment = isWide ? readS2() : readS1(); int value = local(index).asInt(); setLocal(index, IntValue.from(value + increment)); break; } case I2L: push(LongValue.from(pop().asInt())); break; case I2F: push(FloatValue.from(pop().asInt())); break; case I2D: push(DoubleValue.from(pop().asInt())); break; case L2I: push(IntValue.from((int) pop().asLong())); break; case L2F: push(FloatValue.from(pop().asLong())); break; case L2D: push(DoubleValue.from(pop().asLong())); break; case F2I: push(IntValue.from((int) pop().asFloat())); break; case F2L: push(LongValue.from((long) pop().asFloat())); break; case F2D: push(DoubleValue.from(pop().asFloat())); break; case D2I: push(IntValue.from((int) pop().asDouble())); break; case D2L: push(LongValue.from((long) pop().asDouble())); break; case D2F: push(FloatValue.from((float) pop().asDouble())); break; case I2B: push(IntValue.from(pop().toByte())); break; case I2C: push(IntValue.from(pop().toChar())); break; case I2S: push(IntValue.from(pop().toShort())); break; case LCMP: { long right = pop().asLong(); long left = pop().asLong(); int result = (left < right) ? -1 : (left == right) ? 0 : 1; push(IntValue.from(result)); break; } case FCMPL: case FCMPG: { float right = pop().asFloat(); float left = pop().asFloat(); int result = (left < right) ? -1 : (left == right) ? 0 : 1; push(IntValue.from(result)); break; } case DCMPL: case DCMPG: { double right = pop().asDouble(); double left = pop().asDouble(); int result = (left < right) ? -1 : (left == right) ? 0 : 1; push(IntValue.from(result)); break; } case IFEQ: { final short offset = readS2(); final int s1 = machine.pop().asInt(); if (s1 == 0) { machine.jump(offset); } break; } case IFNE: { final short offset = readS2(); final int s1 = machine.pop().asInt(); if (s1 != 0) { machine.jump(offset); } break; } case IFLT: { final short offset = readS2(); final int s1 = machine.pop().asInt(); if (s1 < 0) { machine.jump(offset); } break; } case IFGE: { final short offset = readS2(); final int s1 = machine.pop().asInt(); if (s1 >= 0) { machine.jump(offset); } break; } case IFGT: { final short offset = readS2(); final int s1 = machine.pop().asInt(); if (s1 > 0) { machine.jump(offset); } break; } case IFLE: { final short offset = readS2(); final int s1 = machine.pop().asInt(); if (s1 <= 0) { machine.jump(offset); } break; } case IF_ICMPEQ: { final short offset = readS2(); final int s1 = machine.pop().asInt(); final int s2 = machine.pop().asInt(); if (s2 == s1) { machine.jump(offset); } break; } case IF_ICMPNE: { final short offset = readS2(); final int s1 = machine.pop().asInt(); final int s2 = machine.pop().asInt(); if (s2 != s1) { machine.jump(offset); } break; } case IF_ICMPLT: { final short offset = readS2(); final int s1 = machine.pop().asInt(); final int s2 = machine.pop().asInt(); if (s2 < s1) { machine.jump(offset); } break; } case IF_ICMPGE: { final short offset = readS2(); final int s1 = machine.pop().asInt(); final int s2 = machine.pop().asInt(); if (s2 >= s1) { machine.jump(offset); } break; } case IF_ICMPGT: { final short offset = readS2(); final int s1 = machine.pop().asInt(); final int s2 = machine.pop().asInt(); if (s2 > s1) { machine.jump(offset); } break; } case IF_ICMPLE: { final short offset = readS2(); final int s1 = machine.pop().asInt(); final int s2 = machine.pop().asInt(); if (s2 <= s1) { machine.jump(offset); } break; } case IF_ACMPEQ: { final short offset = readS2(); final ReferenceValue s1 = (ReferenceValue) machine.pop(); final ReferenceValue s2 = (ReferenceValue) machine.pop(); if (s2.equals(s1)) { machine.jump(offset); } break; } case IF_ACMPNE: { final short offset = readS2(); final ReferenceValue s1 = (ReferenceValue) machine.pop(); final ReferenceValue s2 = (ReferenceValue) machine.pop(); if (!s2.equals(s1)) { machine.jump(offset); } break; } case GOTO: { final short offset = readS2(); machine.jump(offset); break; } case JSR: { short offset = readS2(); int returnPosition = machine.currentThread().frame().currentBytePosition(); push(IntValue.from(returnPosition)); machine.jump(offset); break; } case RET: { int index = isWide ? readU2() : readU1(); int value = local(index).asInt(); machine.currentThread().frame().setBytecodePosition(value); break; } case TABLESWITCH: { int index = pop().asInt(); machine.alignInstructionPosition(); int defawlt = machine.readInt(); int low = machine.readInt(); int high = machine.readInt(); if (index < low || index > high) { machine.jump(defawlt); } else { int jumpTableIndex = index - low; machine.skipBytes(jumpTableIndex * 4); int offset = machine.readInt(); machine.jump(offset); } break; } case LOOKUPSWITCH: { int key = pop().asInt(); machine.alignInstructionPosition(); int defawlt = machine.readInt(); int nPairs = machine.readInt(); boolean foundMatch = false; for (int i = 0; i < nPairs; i++) { int value = machine.readInt(); int offset = machine.readInt(); if (value == key) { machine.jump(offset); foundMatch = true; break; } } if (!foundMatch) { machine.jump(defawlt); } break; } case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN: { Value result = pop(); ExecutionFrame frame = machine.popFrame(); //if this was the topmost frame on the stack if (frame == null) { returnValue = result; return MethodStatus.METHOD_END; } push(result); break; } case RETURN: { ExecutionFrame frame = machine.popFrame(); if (frame == null) { returnValue = VoidValue.VOID; return MethodStatus.METHOD_END; } break; } case GETSTATIC: push(machine.getStatic(readU2())); break; case PUTSTATIC: machine.putStatic(readU2(), pop()); break; case GETFIELD: push(machine.getField((RemoteReference) pop().asReference(), readU2())); break; case PUTFIELD: { Value value = pop(); Object instance = pop().asBoxedJavaValue(); machine.putField(instance, readU2(), value); break; } case INVOKEVIRTUAL: { int cpIndex = readU2(); ClassMethodActor resolveMethod = (ClassMethodActor) machine.resolveMethod(cpIndex); ClassMethodActor methodActor = resolveMethod; Value value = machine.peek(methodActor.descriptor().numberOfParameters() + 1); if (value instanceof ReferenceValue) { ReferenceValue receiver = (ReferenceValue) value; if (receiver.isZero()) { machine.raiseException(new NullPointerException()); } ClassActor dynamicClass = receiver.getClassActor(); assert dynamicClass != null; methodActor = dynamicClass.findVirtualMethodActor(methodActor); } if (methodActor == null) { ReferenceValue receiver = (ReferenceValue) value; ClassActor dynamicClass = receiver.getClassActor(); methodActor = dynamicClass.findVirtualMethodActor(methodActor); machine.raiseException(new AbstractMethodError()); } else if (methodActor.isAbstract()) { machine.raiseException(new AbstractMethodError()); } if (!processIntrinsic(methodActor)) { machine.invokeMethod(methodActor); } break; } case INVOKESPECIAL: { int cpIndex = readU2(); ClassMethodActor methodActor = (ClassMethodActor) machine.resolveMethod(cpIndex); Value receiver = machine.peek(methodActor.descriptor().numberOfParameters() + 1); if (receiver.isZero() && receiver instanceof ReferenceValue) { machine.raiseException(new NullPointerException()); } if (!processIntrinsic(methodActor)) { machine.invokeMethod(methodActor); } break; } case INVOKESTATIC: { int cpIndex = readU2(); ClassMethodActor methodActor = (ClassMethodActor) machine.resolveMethod(cpIndex); if (!processIntrinsic(methodActor)) { machine.invokeMethod(methodActor); } break; } case INVOKEINTERFACE: { int cpIndex = readU2(); readU2(); InterfaceMethodActor methodActor = (InterfaceMethodActor) machine.resolveMethod(cpIndex); ReferenceValue receiver = (ReferenceValue) machine.peek(methodActor.descriptor().numberOfParameters() + 1); if (receiver.isZero()) { machine.raiseException(new NullPointerException()); } ClassActor dynamicClass = receiver.getClassActor(); assert dynamicClass != null; if (!dynamicClass.getAllInterfaceActors().contains(methodActor.holder())) { machine.raiseException(new IncompatibleClassChangeError(dynamicClass + " does not implement " + methodActor.holder())); } VirtualMethodActor dynamicMethodActor = dynamicClass.findVirtualMethodActor(methodActor); if (dynamicMethodActor == null) { machine.raiseException(new AbstractMethodError("No such method " + methodActor + " found in " + dynamicClass)); } else if (dynamicMethodActor.isAbstract()) { machine.raiseException(new AbstractMethodError("Method " + dynamicMethodActor + " is abstract in " + dynamicClass)); } else if (!dynamicMethodActor.isPublic()) { machine.raiseException(new IllegalAccessError("Method " + dynamicMethodActor + " is not public in " + dynamicClass)); } if (!processIntrinsic(methodActor)) { machine.invokeMethod(dynamicMethodActor); } break; } case NEW: { int cpIndex = readU2(); ClassActor classActor = machine.resolveClassReference(cpIndex); push(ReferenceValue.from(ObjectUtils.allocateInstance(classActor.toJava()))); break; } case NEWARRAY: { int arrayType = readU1(); int arraySize = pop().asInt(); if (arraySize < 0) { machine.raiseException(new NegativeArraySizeException()); } switch (arrayType) { case 4: push(ReferenceValue.from(new boolean[arraySize])); break; case 5: push(ReferenceValue.from(new char[arraySize])); break; case 6: push(ReferenceValue.from(new float[arraySize])); break; case 7: push(ReferenceValue.from(new double[arraySize])); break; case 8: push(ReferenceValue.from(new byte[arraySize])); break; case 9: push(ReferenceValue.from(new short[arraySize])); break; case 10: push(ReferenceValue.from(new int[arraySize])); break; case 11: push(ReferenceValue.from(new long[arraySize])); break; } break; } case ANEWARRAY: { int cpIndex = readU2(); int arraySize = pop().asInt(); ClassActor classActor = machine.resolveClassReference(cpIndex); if (arraySize < 0) { machine.raiseException(new NegativeArraySizeException()); } push(ReferenceValue.from(Array.newInstance(classActor.toJava(), arraySize))); break; } case ARRAYLENGTH: { Reference array = pop().asReference(); if (array.isZero()) { machine.raiseException(new NullPointerException()); } push(IntValue.from(Layout.readArrayLength(array))); break; } case ATHROW: { ReferenceValue t = (ReferenceValue) pop(); if (t.isZero()) { throw new NullPointerException(); } else { throw machine.raiseException(t); } } case CHECKCAST: { int cpIndex = readU2(); ClassActor classActor = machine.resolveClassReference(cpIndex); ReferenceValue object = (ReferenceValue) pop(); if (!object.isZero()) { if (!classActor.isAssignableFrom(object.getClassActor())) { String message = object.getClassActor().toJava() + " is not a subclass of " + classActor; machine.raiseException(new ClassCastException(message)); } } push(object); break; } case INSTANCEOF: { int cpIndex = readU2(); ClassActor classActor = machine.resolveClassReference(cpIndex); ReferenceValue object = (ReferenceValue) pop(); if (object.isZero() || !classActor.isAssignableFrom(object.getClassActor())) { push(IntValue.from(0)); } else { push(IntValue.from(1)); } break; } case MONITORENTER: case MONITOREXIT: pop(); break; case MULTIANEWARRAY: { int cpIndex = readU2(); ClassActor arrayClassActor = machine.resolveClassReference(cpIndex); int lengthsCount = (short) (readS1() & 0x7F); if (lengthsCount < 1) { throw new ClassFormatError("dimensions operand of multianewarray is less than 1"); } int[] lengths = new int[lengthsCount]; if (lengthsCount > arrayClassActor.numberOfDimensions()) { throw new IncompatibleClassChangeError(lengthsCount + " is too many dimensions for " + arrayClassActor); } for (int i = lengthsCount - 1; i >= 0; --i) { lengths[i] = pop().asInt(); if (lengths[i] < 0) { machine.raiseException(new NegativeArraySizeException()); } } push(ReferenceValue.from(createMultiDimensionArray(arrayClassActor, 0, lengths))); break; } case IFNULL: { final int offset = machine.readShort(); final Value r = machine.pop(); if (r.isZero()) { machine.jump(offset); } break; } case IFNONNULL: { final int offset = machine.readShort(); final Value r = machine.pop(); if (!r.isZero()) { machine.jump(offset); } break; } case GOTO_W: machine.jump(machine.readInt()); break; case JSR_W: int offset = machine.readInt(); int returnPosition = machine.currentThread().frame().currentBytePosition(); push(IntValue.from(returnPosition)); machine.jump(offset); break; default: machine.raiseException(new ClassFormatError("Unsupported bytecode: " + opcode + " [" + Bytecodes.nameOf(opcode) + "]")); // Checkstyle: resume } return MethodStatus.METHOD_CONTINUE; } public static enum MethodStatus { METHOD_END, METHOD_CONTINUE, } private static Object createMultiDimensionArray(ClassActor arrayClassActor, int lengthIndex, int[] lengths) { ClassActor componentClassActor = arrayClassActor.componentClassActor(); assert componentClassActor != null : arrayClassActor + " is not an array class"; int length = lengths[lengthIndex]; assert length >= 0 : "negative array length: " + length; Object result = Array.newInstance(componentClassActor.toJava(), length); if (length > 0) { int nextLengthIndex = lengthIndex + 1; if (nextLengthIndex < lengths.length) { for (int i = 0; i < length; i++) { Object subArray = createMultiDimensionArray(componentClassActor, nextLengthIndex, lengths); Object[] array = (Object[]) result; array[i] = subArray; } } } return result; } protected boolean processIntrinsic(MethodActor method) throws Throwable { String intrinsic = method.intrinsic(); // Handle special methods that cannot be interpreted. This is effectively // TeleInterpreter specific intrinsification if (method == SafepointPoll_disable) { // Nothing to do push(false); return true; } else if (method == SafepointPoll_enable) { // Nothing to do return true; } // If the method is not intrinsified, then return now. if (intrinsic == null) { return false; } // A String-switch would be nice here, but we have to switch to Java 7 source level for that... if (intrinsic == LSB) { push(minus1IfWordWidth(Long.numberOfTrailingZeros(pop().asLong()))); } else if (intrinsic == MSB) { push(minus1IfWordWidth(Long.numberOfLeadingZeros(pop().asLong()))); } else if (intrinsic == UNSAFE_CAST) { // Nothing to do. } else if (intrinsic == UCMP_AT) { Value value2 = pop(); Value value1 = pop(); if (value1.kind() == Kind.INT) { push(UnsignedMath.aboveThan(value1.asInt(), value2.asInt())); } else { push(UnsignedMath.aboveThan(value1.asLong(), value2.asLong())); } } else if (intrinsic == UCMP_AE) { Value value2 = pop(); Value value1 = pop(); if (value1.kind() == Kind.INT) { push(UnsignedMath.aboveOrEqual(value1.asInt(), value2.asInt())); } else { push(UnsignedMath.aboveOrEqual(value1.asLong(), value2.asLong())); } } else if (intrinsic == UCMP_BT) { Value value2 = pop(); Value value1 = pop(); if (value1.kind() == Kind.INT) { push(UnsignedMath.belowThan(value1.asInt(), value2.asInt())); } else { push(UnsignedMath.belowThan(value1.asLong(), value2.asLong())); } } else if (intrinsic == UCMP_BE) { Value value2 = pop(); Value value1 = pop(); if (value1.kind() == Kind.INT) { push(UnsignedMath.belowOrEqual(value1.asInt(), value2.asInt())); } else { push(UnsignedMath.belowOrEqual(value1.asLong(), value2.asLong())); } } else if (intrinsic == IntrinsicIDs.UDIV) { Value value2 = popCheckZero(); Value value1 = pop(); if (value1.kind() == Kind.INT) { push(IntValue.from(UnsignedMath.divide(value1.asInt(), value2.asInt()))); } else { push(LongValue.from(UnsignedMath.divide(value1.asLong(), value2.asLong()))); } } else if (intrinsic == IntrinsicIDs.UREM) { Value value2 = popCheckZero(); Value value1 = pop(); if (value1.kind() == Kind.INT) { push(IntValue.from(UnsignedMath.remainder(value1.asInt(), value2.asInt()))); } else { push(LongValue.from(UnsignedMath.remainder(value1.asLong(), value2.asLong()))); } } else if (intrinsic == PREAD_OFF || intrinsic == PREAD_IDX) { pointerLoad(method); } else if (intrinsic == PWRITE_OFF || intrinsic == PWRITE_IDX || intrinsic == PCMPSWP) { throw TeleError.unexpected("Cannot interpret pointer writes remotely"); } else if (intrinsic == MEMBAR) { throw TeleError.unexpected("Unsupported intrinsic: " + intrinsic); } else if (intrinsic == PAUSE) { // Nothing to do, since it can be no-op. } else { // Could also opt to just execute the method in case it has an implementation, but for now be safe. throw ProgramError.unexpected("Unknown intrinsic: " + intrinsic); // return false } return true; } // Methods intrinsified specially by TeleInterpreter (by necessity) static final MethodActor SafepointPoll_disable = ClassRegistry.findMethod("disable", SafepointPoll.class); static final MethodActor SafepointPoll_enable = ClassRegistry.findMethod("enable", SafepointPoll.class); }