changeset 5569:af976ca29136

Added prototype implementation of Java bytecode interpreter - contribution by Christian Humer (chumer).
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Mon, 11 Jun 2012 17:01:02 +0200
parents fdf19fa15ce4
children 30162e74f11f
files GRAAL_AUTHORS graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/BytecodeInterpreter.java graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/Frame.java graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/InterpreterCallable.java graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/InterpreterException.java graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/InterpreterFrame.java graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/VMAdapter.java mx/projects
diffstat 8 files changed, 2543 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/GRAAL_AUTHORS	Mon Jun 11 13:51:34 2012 +0200
+++ b/GRAAL_AUTHORS	Mon Jun 11 17:01:02 2012 +0200
@@ -1,20 +1,7 @@
-Doug Simon (doug.simon@oracle.com)
-* Since December 2011
-
-Christian Wimmer (christian.wimmer@oracle.com)
-* Since December 2011
-
-Lukas Stadler (stadler@ssw.jku.at)
-* July - September 2010: Initial feature-complete implementation, remote compilation
-* Since April 2011: New IR design
-
-Gilles Duboscq (gilles.duboscq@oracle.com)
-* Since April 2011: New IR design
-
-Peter Hofer (hofer@ssw.jku.at)
-* Since May 2011: Graphviz visualization
-
-Thomas Wuerthinger (thomas.wuerthinger@oracle.com)
-* June 2011: Initial prototype
-* October 2010 - January 2011: Bug fixes (all DaCapo's pass), better performance on SciMark than C1
-* Since April 2011: New IR design
+Gilles Duboscq (gdub)
+Peter Hofer
+Christian Humer (chumer)
+Christian Wimmer (cwimmer)
+Doug Simon (dnsimon)
+Lukas Stadler (lstadler)
+Thomas Wuerthinger (thomaswue)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/BytecodeInterpreter.java	Mon Jun 11 17:01:02 2012 +0200
@@ -0,0 +1,1658 @@
+/*
+ * Copyright (c) 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.oracle.graal.interpreter;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.oracle.graal.api.interpreter.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.bytecode.*;
+
+/**
+ *
+ * <h1>Implementation notes</h1>
+ * <li>Native methods are invoked using standard java reflection.</li>
+ *
+ * <h1>Features:</h1>
+ *
+ *
+ * <h1>Limitations:</h1>
+ *
+ * <li>The interpreter is not able to recognize a native method that calls a java method.
+ *  In that case the java method will be interpreted directly on the vm interpreter.</li>
+ */
+@SuppressWarnings("static-method")
+public final class BytecodeInterpreter implements Interpreter {
+
+    private static final String OPTION_MAX_STACK_SIZE = "maxStackSize";
+    private static final boolean TRACE = false;
+    private static final boolean TRACE_BYTE_CODE = false;
+
+    private static final int DEFAULT_MAX_STACK_SIZE = 200;
+
+    private static final int NEXT = -1;
+    private static final int BRANCH = -2;
+    private static final int RETURN = -3;
+    private static final int CALL = -4;
+
+    private InterpreterFrame callFrame;
+
+    //TODO try to remove this reference
+    private ExceptionHandler catchAllExceptionHandler;
+
+    private final Map<ResolvedJavaMethod, InterpreterCallable> methodDelegates = new HashMap<>();
+
+    private int maxStackFrames = DEFAULT_MAX_STACK_SIZE;
+    private int stackFrameDepth;
+
+    private final ResolvedJavaMethod rootMethod;
+    private final VMAdapter vm;
+
+    public BytecodeInterpreter() {
+        this(VMAdapter.getInstance());
+    }
+
+    public BytecodeInterpreter(VMAdapter accessor) {
+        if (accessor == null) {
+            throw new NullPointerException();
+        }
+        this.vm = accessor;
+        this.rootMethod = resolveRootMethod();
+        registerDelegates();
+    }
+
+    @Override
+    public void setOption(String name, String value) {
+        if (name != null && name.equals(OPTION_MAX_STACK_SIZE)) {
+            this.maxStackFrames = Integer.parseInt(value);
+        }
+    }
+
+
+    private void registerDelegates() {
+       addDelegate(findMethod(Throwable.class, "fillInStackTrace"), new InterpreterCallable() {
+        @Override
+        public Object invoke(InterpreterFrame caller, ResolvedJavaMethod method, Object base, Object[] arguments) throws Throwable {
+            setBackTrace(caller, (Throwable) base, createStackTraceElements(caller));
+            return null;
+        }
+       });
+       addDelegate(findMethod(Throwable.class, "getStackTraceDepth"), new InterpreterCallable() {
+           @Override
+           public Object invoke(InterpreterFrame caller, ResolvedJavaMethod method, Object base, Object[] arguments) throws Throwable {
+               StackTraceElement[] elements = getBackTrace(caller, (Throwable) base);
+               if (elements != null) {
+                   return elements.length;
+               }
+               return 0;
+           }
+       });
+       addDelegate(findMethod(Throwable.class, "getStackTraceElement", int.class), new InterpreterCallable() {
+           @Override
+           public Object invoke(InterpreterFrame caller, ResolvedJavaMethod method, Object base, Object[] arguments) throws Throwable {
+               StackTraceElement[] elements = getBackTrace(caller, (Throwable) base);
+               if (elements != null) {
+                   Integer index = (Integer) arguments[0];
+                   if (index != null) {
+                       return elements[index];
+                   }
+               }
+               return null;
+           }
+       });
+    }
+
+    public void setMaxStackFrames(int maxStackSize) {
+        this.maxStackFrames = maxStackSize;
+    }
+
+    public int getMaxStackFrames() {
+        return maxStackFrames;
+    }
+
+    public void addDelegate(Method method, InterpreterCallable callable) {
+        ResolvedJavaMethod resolvedMethod = vm.getRuntime().getResolvedJavaMethod(method);
+        if (methodDelegates.containsKey(resolvedMethod)) {
+            throw new IllegalArgumentException("Delegate for method " + method + " already added.");
+        }
+        methodDelegates.put(resolvedMethod, callable);
+    }
+
+    public void removeDelegate(Method method) {
+        methodDelegates.remove(vm.getRuntime().getResolvedJavaMethod(method));
+    }
+
+    @Override
+    public Object execute(ResolvedJavaMethod method, Object... boxedArguments) {
+        Signature signature = method.signature();
+        assert boxedArguments != null;
+        assert signature.argumentCount(false) == boxedArguments.length;
+
+        if (!Modifier.isStatic(method.accessFlags())) {
+            throw new UnsupportedOperationException("The interpreter can currently only be started with static methods.");
+        }
+
+        InterpreterFrame rootFrame = new InterpreterFrame(rootMethod, signature.argumentSlots(true));
+        rootFrame.pushObject(this);
+        rootFrame.pushObject(method);
+        rootFrame.pushObject(boxedArguments);
+
+        for (int i = 0; i < boxedArguments.length; i++) {
+            pushAsObject(rootFrame, signature.argumentKindAt(i), boxedArguments[i]);
+        }
+
+        InterpreterFrame frame = rootFrame.create(method, false);
+
+        try {
+            executeRoot(rootFrame, frame);
+        } catch (Throwable e) {
+            // clear backtrace for compatibilty and store it in stacktrace
+            StackTraceElement[] backtrace = getBackTrace(rootFrame, e);
+            setBackTrace(rootFrame, e, null);
+            setStackTrace(rootFrame, e, backtrace);
+            throw new InterpreterException(e);
+        }
+
+        return popAsObject(rootFrame, signature.returnKind());
+    }
+
+    public Object execute(Method javaMethod, Object... boxedArguments) throws InterpreterException {
+        return execute(vm.getRuntime().getResolvedJavaMethod(javaMethod), boxedArguments);
+    }
+
+    private void executeRoot(InterpreterFrame root, InterpreterFrame frame) throws Throwable {
+        //TODO reflection redirection
+        stackFrameDepth = 0;
+
+        InterpreterFrame prevFrame = frame;
+        InterpreterFrame currentFrame = frame;
+        BytecodeStream bs = new BytecodeStream(currentFrame.getMethod().code());
+        if (TRACE) {
+            traceCall(frame, "Call");
+        }
+        while (currentFrame != root) {
+            if (prevFrame != currentFrame) {
+                bs = new BytecodeStream(currentFrame.getMethod().code());
+            }
+            bs.setBCI(currentFrame.getBCI());
+
+            prevFrame = currentFrame;
+            currentFrame = loop(root, prevFrame, bs);
+        }
+        assert callFrame == null;
+    }
+
+    private InterpreterFrame loop(InterpreterFrame root, final InterpreterFrame frame, final BytecodeStream bs) throws Throwable {
+        try {
+            while (true) {
+                int result = executeInstruction(frame, bs);
+                switch (result) {
+                    case NEXT :
+                        bs.next();
+                        break;
+                    case RETURN :
+                        return popFrame(frame);
+                    case CALL:
+                        return allocateFrame(frame, bs);
+                    case BRANCH :
+                        bs.setBCI(bs.readBranchDest());
+                        break;
+                    default :
+                        // the outcome depends on stack values
+                        assert result >= 0 : "negative branch target";
+                        bs.setBCI(result);
+                        break;
+                }
+            }
+        } catch (Throwable t) {
+            if (TRACE) {
+                traceOp("Exception " + t.toString());
+            }
+            updateStackTrace(frame, t);
+
+            // frame bci needs to be in sync when handling exceptions
+            frame.setBCI(bs.currentBCI());
+
+            InterpreterFrame handlerFrame = handleThrowable(root, frame, t);
+            if (handlerFrame == null) {
+                // matched root we just throw it again.
+                throw t;
+            } else {
+                if (TRACE) {
+                    traceOp("Handler found " + handlerFrame.getMethod() + ":" + handlerFrame.getBCI());
+                }
+                // update bci from frame
+                bs.setBCI(handlerFrame.getBCI());
+
+                // continue execution on the found frame
+                return handlerFrame;
+            }
+        } finally {
+            // TODO may be not necessary.
+            frame.setBCI(bs.currentBCI());
+        }
+    }
+
+    private int executeInstruction(InterpreterFrame frame, BytecodeStream bs) throws Throwable {
+        if (TRACE_BYTE_CODE) {
+            traceOp(bs.currentBCI() + ": " + Bytecodes.baseNameOf(bs.currentBC()));
+        }
+        switch(bs.currentBC()) {
+            case Bytecodes.NOP            : break;
+            case Bytecodes.ACONST_NULL    :
+                frame.pushObject(null);
+                break;
+            case Bytecodes.ICONST_M1      :
+                frame.pushInt(-1);
+                break;
+            case Bytecodes.ICONST_0       :
+                frame.pushInt(0);
+                break;
+            case Bytecodes.ICONST_1       :
+                frame.pushInt(1);
+                break;
+            case Bytecodes.ICONST_2       :
+                frame.pushInt(2);
+                break;
+            case Bytecodes.ICONST_3       :
+                frame.pushInt(3);
+                break;
+            case Bytecodes.ICONST_4       :
+                frame.pushInt(4);
+                break;
+            case Bytecodes.ICONST_5       :
+                frame.pushInt(5);
+                break;
+            case Bytecodes.LCONST_0       :
+                frame.pushLong(0L);
+                break;
+            case Bytecodes.LCONST_1       :
+                frame.pushLong(1L);
+                break;
+            case Bytecodes.FCONST_0       :
+                frame.pushFloat(0.0F);
+                break;
+            case Bytecodes.FCONST_1       :
+                frame.pushFloat(1.0F);
+                break;
+            case Bytecodes.FCONST_2       :
+                frame.pushFloat(2.0F);
+                break;
+            case Bytecodes.DCONST_0       :
+                frame.pushDouble(0.0D);
+                break;
+            case Bytecodes.DCONST_1       :
+                frame.pushDouble(1.0D);
+                break;
+            case Bytecodes.BIPUSH         :
+                frame.pushInt(bs.readByte());
+                break;
+            case Bytecodes.SIPUSH         :
+                frame.pushInt(bs.readShort());
+                break;
+            case Bytecodes.LDC            :
+            case Bytecodes.LDC_W          :
+            case Bytecodes.LDC2_W         :
+                pushCPConstant(frame, bs.readCPI());
+                break;
+            case Bytecodes.ILOAD          :
+                frame.pushInt(frame.getInt(frame.resolveLocalIndex(bs.readLocalIndex())));
+                break;
+            case Bytecodes.LLOAD          :
+                frame.pushLong(frame.getLong(frame.resolveLocalIndex(bs.readLocalIndex())));
+                break;
+            case Bytecodes.FLOAD          :
+                frame.pushFloat(frame.getFloat(frame.resolveLocalIndex(bs.readLocalIndex())));
+                break;
+            case Bytecodes.DLOAD          :
+                frame.pushDouble(frame.getDouble(frame.resolveLocalIndex(bs.readLocalIndex())));
+                break;
+            case Bytecodes.ALOAD          :
+                frame.pushObject(frame.getObject(frame.resolveLocalIndex(bs.readLocalIndex())));
+                break;
+            case Bytecodes.ILOAD_0:
+                frame.pushInt(frame.getInt(frame.resolveLocalIndex(0)));
+                break;
+            case Bytecodes.ILOAD_1:
+                frame.pushInt(frame.getInt(frame.resolveLocalIndex(1)));
+                break;
+            case Bytecodes.ILOAD_2:
+                frame.pushInt(frame.getInt(frame.resolveLocalIndex(2)));
+                break;
+            case Bytecodes.ILOAD_3:
+                frame.pushInt(frame.getInt(frame.resolveLocalIndex(3)));
+                break;
+            case Bytecodes.LLOAD_0        :
+                frame.pushLong(frame.getLong(frame.resolveLocalIndex(0)));
+                break;
+            case Bytecodes.LLOAD_1        :
+                frame.pushLong(frame.getLong(frame.resolveLocalIndex(1)));
+                break;
+            case Bytecodes.LLOAD_2        :
+                frame.pushLong(frame.getLong(frame.resolveLocalIndex(2)));
+                break;
+            case Bytecodes.LLOAD_3        :
+                frame.pushLong(frame.getLong(frame.resolveLocalIndex(3)));
+                break;
+            case Bytecodes.FLOAD_0        :
+                frame.pushFloat(frame.getFloat(frame.resolveLocalIndex(0)));
+                break;
+            case Bytecodes.FLOAD_1        :
+                frame.pushFloat(frame.getFloat(frame.resolveLocalIndex(1)));
+                break;
+            case Bytecodes.FLOAD_2        :
+                frame.pushFloat(frame.getFloat(frame.resolveLocalIndex(2)));
+                break;
+            case Bytecodes.FLOAD_3        :
+                frame.pushFloat(frame.getFloat(frame.resolveLocalIndex(3)));
+                break;
+            case Bytecodes.DLOAD_0        :
+                frame.pushDouble(frame.getDouble(frame.resolveLocalIndex(0)));
+                break;
+            case Bytecodes.DLOAD_1        :
+                frame.pushDouble(frame.getDouble(frame.resolveLocalIndex(1)));
+                break;
+            case Bytecodes.DLOAD_2        :
+                frame.pushDouble(frame.getDouble(frame.resolveLocalIndex(2)));
+                break;
+            case Bytecodes.DLOAD_3        :
+                frame.pushDouble(frame.getDouble(frame.resolveLocalIndex(3)));
+                break;
+            case Bytecodes.ALOAD_0        :
+                frame.pushObject(frame.getObject(frame.resolveLocalIndex(0)));
+                break;
+            case Bytecodes.ALOAD_1        :
+                frame.pushObject(frame.getObject(frame.resolveLocalIndex(1)));
+                break;
+            case Bytecodes.ALOAD_2        :
+                frame.pushObject(frame.getObject(frame.resolveLocalIndex(2)));
+                break;
+            case Bytecodes.ALOAD_3        :
+                frame.pushObject(frame.getObject(frame.resolveLocalIndex(3)));
+                break;
+            case Bytecodes.IALOAD         :
+                frame.pushInt(vm.getArrayInt(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.LALOAD         :
+                frame.pushLong(vm.getArrayLong(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.FALOAD         :
+                frame.pushFloat(vm.getArrayFloat(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.DALOAD         :
+                frame.pushDouble(vm.getArrayDouble(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.AALOAD         :
+                frame.pushObject(vm.getArrayObject(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.BALOAD         :
+                frame.pushInt(vm.getArrayByte(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.CALOAD         :
+                frame.pushInt(vm.getArrayChar(frame.popInt(), frame.popObject()));
+                break;
+             case Bytecodes.SALOAD:
+                frame.pushInt(vm.getArrayShort(frame.popInt(), frame.popObject()));
+                break;
+            case Bytecodes.ISTORE:
+                frame.setInt(frame.resolveLocalIndex(bs.readLocalIndex()), frame.popInt());
+                break;
+            case Bytecodes.LSTORE         :
+                frame.setLong(frame.resolveLocalIndex(bs.readLocalIndex()), frame.popLong());
+                break;
+            case Bytecodes.FSTORE         :
+                frame.setFloat(frame.resolveLocalIndex(bs.readLocalIndex()), frame.popFloat());
+                break;
+            case Bytecodes.DSTORE         :
+                frame.setDouble(frame.resolveLocalIndex(bs.readLocalIndex()), frame.popDouble());
+                break;
+            case Bytecodes.ASTORE         :
+                frame.setObject(frame.resolveLocalIndex(bs.readLocalIndex()), frame.popObject());
+                break;
+            case Bytecodes.ISTORE_0       :
+                frame.setInt(frame.resolveLocalIndex(0), frame.popInt());
+                break;
+            case Bytecodes.ISTORE_1       :
+                frame.setInt(frame.resolveLocalIndex(1), frame.popInt());
+                break;
+            case Bytecodes.ISTORE_2       :
+                frame.setInt(frame.resolveLocalIndex(2), frame.popInt());
+                break;
+            case Bytecodes.ISTORE_3       :
+                frame.setInt(frame.resolveLocalIndex(3), frame.popInt());
+                break;
+            case Bytecodes.LSTORE_0       :
+                frame.setLong(frame.resolveLocalIndex(0), frame.popLong());
+                break;
+            case Bytecodes.LSTORE_1       :
+                frame.setLong(frame.resolveLocalIndex(1), frame.popLong());
+                break;
+            case Bytecodes.LSTORE_2       :
+                frame.setLong(frame.resolveLocalIndex(2), frame.popLong());
+                break;
+            case Bytecodes.LSTORE_3       :
+                frame.setLong(frame.resolveLocalIndex(3), frame.popLong());
+                break;
+            case Bytecodes.FSTORE_0       :
+                frame.setFloat(frame.resolveLocalIndex(0), frame.popFloat());
+                break;
+            case Bytecodes.FSTORE_1       :
+                frame.setFloat(frame.resolveLocalIndex(1), frame.popFloat());
+                break;
+            case Bytecodes.FSTORE_2       :
+                frame.setFloat(frame.resolveLocalIndex(2), frame.popFloat());
+                break;
+            case Bytecodes.FSTORE_3       :
+                frame.setFloat(frame.resolveLocalIndex(3), frame.popFloat());
+                break;
+            case Bytecodes.DSTORE_0       :
+                frame.setDouble(frame.resolveLocalIndex(0), frame.popDouble());
+                break;
+            case Bytecodes.DSTORE_1       :
+                frame.setDouble(frame.resolveLocalIndex(1), frame.popDouble());
+                break;
+            case Bytecodes.DSTORE_2       :
+                frame.setDouble(frame.resolveLocalIndex(2), frame.popDouble());
+                break;
+            case Bytecodes.DSTORE_3       :
+                frame.setDouble(frame.resolveLocalIndex(3), frame.popDouble());
+                break;
+            case Bytecodes.ASTORE_0       :
+                frame.setObject(frame.resolveLocalIndex(0), frame.popObject());
+                break;
+            case Bytecodes.ASTORE_1       :
+                frame.setObject(frame.resolveLocalIndex(1), frame.popObject());
+                break;
+            case Bytecodes.ASTORE_2       :
+                frame.setObject(frame.resolveLocalIndex(2), frame.popObject());
+                break;
+            case Bytecodes.ASTORE_3       :
+                frame.setObject(frame.resolveLocalIndex(3), frame.popObject());
+                break;
+            case Bytecodes.IASTORE        :
+                vm.setArrayInt(frame.popInt(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.LASTORE        :
+                vm.setArrayLong(frame.popLong(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.FASTORE        :
+                vm.setArrayFloat(frame.popFloat(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.DASTORE        :
+                vm.setArrayDouble(frame.popDouble(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.AASTORE        :
+                vm.setArrayObject(frame.popObject(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.BASTORE        :
+                vm.setArrayByte((byte) frame.popInt(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.CASTORE        :
+                vm.setArrayChar((char) frame.popInt(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.SASTORE        :
+                vm.setArrayShort((short) frame.popInt(), frame.popInt(), frame.popObject());
+                break;
+            case Bytecodes.POP            :
+                frame.popVoid(1);
+                break;
+            case Bytecodes.POP2           :
+                frame.popVoid(2);
+                break;
+            case Bytecodes.DUP            :
+                frame.dup(1);
+                break;
+            case Bytecodes.DUP_X1         :
+                frame.dupx1();
+                break;
+            case Bytecodes.DUP_X2         :
+                frame.dupx2();
+                break;
+            case Bytecodes.DUP2           :
+                frame.dup(2);
+                break;
+            case Bytecodes.DUP2_X1        :
+                frame.dup2x1();
+                break;
+            case Bytecodes.DUP2_X2        :
+                frame.dup2x2();
+                break;
+            case Bytecodes.SWAP           :
+                frame.swapSingle();
+                break;
+            case Bytecodes.IADD           :
+                frame.pushInt(frame.popInt() + frame.popInt());
+                break;
+            case Bytecodes.LADD           :
+                frame.pushLong(frame.popLong() + frame.popLong());
+                break;
+            case Bytecodes.FADD           :
+                frame.pushFloat(frame.popFloat() + frame.popFloat());
+                break;
+            case Bytecodes.DADD           :
+                frame.pushDouble(frame.popDouble() + frame.popDouble());
+                break;
+            case Bytecodes.ISUB           :
+                frame.pushInt(-frame.popInt() + frame.popInt());
+                break;
+            case Bytecodes.LSUB           :
+                frame.pushLong(-frame.popLong() + frame.popLong());
+                break;
+            case Bytecodes.FSUB           :
+                frame.pushFloat(-frame.popFloat() + frame.popFloat());
+                break;
+            case Bytecodes.DSUB           :
+                frame.pushDouble(-frame.popDouble() + frame.popDouble());
+                break;
+            case Bytecodes.IMUL           :
+                frame.pushInt(frame.popInt() * frame.popInt());
+                break;
+            case Bytecodes.LMUL           :
+                frame.pushLong(frame.popLong() * frame.popLong());
+                break;
+            case Bytecodes.FMUL           :
+                frame.pushFloat(frame.popFloat() * frame.popFloat());
+                break;
+            case Bytecodes.DMUL           :
+                frame.pushDouble(frame.popDouble() * frame.popDouble());
+                break;
+            case Bytecodes.IDIV           :
+                divInt(frame);
+                break;
+            case Bytecodes.LDIV           :
+                divLong(frame);
+                break;
+            case Bytecodes.FDIV           :
+                divFloat(frame);
+                break;
+            case Bytecodes.DDIV           :
+                divDouble(frame);
+                break;
+            case Bytecodes.IREM           :
+                remInt(frame);
+                break;
+            case Bytecodes.LREM           :
+                remLong(frame);
+                break;
+            case Bytecodes.FREM           :
+                remFloat(frame);
+                break;
+            case Bytecodes.DREM           :
+                remDouble(frame);
+                break;
+            case Bytecodes.INEG           :
+                frame.pushInt(-frame.popInt());
+                break;
+            case Bytecodes.LNEG           :
+                frame.pushLong(-frame.popLong());
+                break;
+            case Bytecodes.FNEG           :
+                frame.pushFloat(-frame.popFloat());
+                break;
+            case Bytecodes.DNEG           :
+                frame.pushDouble(-frame.popDouble());
+                break;
+            case Bytecodes.ISHL           :
+                shiftLeftInt(frame);
+                break;
+            case Bytecodes.LSHL           :
+                shiftLeftLong(frame);
+                break;
+            case Bytecodes.ISHR           :
+                shiftRightSignedInt(frame);
+                break;
+            case Bytecodes.LSHR           :
+                shiftRightSignedLong(frame);
+                break;
+            case Bytecodes.IUSHR          :
+                shiftRightUnsignedInt(frame);
+                break;
+            case Bytecodes.LUSHR          :
+                shiftRightUnsignedLong(frame);
+                break;
+            case Bytecodes.IAND           :
+                frame.pushInt(frame.popInt() & frame.popInt());
+                break;
+            case Bytecodes.LAND           :
+                frame.pushLong(frame.popLong() & frame.popLong());
+                break;
+            case Bytecodes.IOR            :
+                frame.pushInt(frame.popInt() | frame.popInt());
+                break;
+            case Bytecodes.LOR            :
+                frame.pushLong(frame.popLong() | frame.popLong());
+                break;
+            case Bytecodes.IXOR           :
+                frame.pushInt(frame.popInt() ^ frame.popInt());
+                break;
+            case Bytecodes.LXOR           :
+                frame.pushLong(frame.popLong() ^ frame.popLong());
+                break;
+            case Bytecodes.IINC           :
+                iinc(frame, bs);
+                break;
+            case Bytecodes.I2L            :
+                frame.pushLong(frame.popInt());
+                break;
+            case Bytecodes.I2F            :
+                frame.pushFloat(frame.popInt());
+                break;
+            case Bytecodes.I2D            :
+                frame.pushDouble(frame.popInt());
+                break;
+            case Bytecodes.L2I            :
+                frame.pushInt((int) frame.popLong());
+                break;
+            case Bytecodes.L2F            :
+                frame.pushFloat(frame.popLong());
+                break;
+            case Bytecodes.L2D            :
+                frame.pushDouble(frame.popLong());
+                break;
+            case Bytecodes.F2I            :
+                frame.pushInt((int) frame.popFloat());
+                break;
+            case Bytecodes.F2L            :
+                frame.pushLong((long) frame.popFloat());
+                break;
+            case Bytecodes.F2D            :
+                frame.pushDouble(frame.popFloat());
+                break;
+            case Bytecodes.D2I            :
+                frame.pushInt((int) frame.popDouble());
+                break;
+            case Bytecodes.D2L            :
+                frame.pushLong((long) frame.popDouble());
+                break;
+            case Bytecodes.D2F            :
+                frame.pushFloat((float) frame.popDouble());
+                break;
+            case Bytecodes.I2B            :
+                frame.pushInt((byte) frame.popInt());
+                break;
+            case Bytecodes.I2C            :
+                frame.pushInt((char) frame.popInt());
+                break;
+            case Bytecodes.I2S            :
+                frame.pushInt((short) frame.popInt());
+                break;
+            case Bytecodes.LCMP           :
+                compareLong(frame);
+                break;
+            case Bytecodes.FCMPL          :
+                compareFloatLess(frame);
+                break;
+            case Bytecodes.FCMPG          :
+                compareFloatGreater(frame);
+                break;
+            case Bytecodes.DCMPL          :
+                compareDoubleLess(frame);
+                break;
+            case Bytecodes.DCMPG          :
+                compareDoubleGreater(frame);
+                break;
+            case Bytecodes.IFEQ           :
+                if (frame.popInt() == 0) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IFNE           :
+                if (frame.popInt() != 0) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IFLT           :
+                if (frame.popInt() < 0) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IFGE           :
+                if (frame.popInt() >= 0) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IFGT           :
+                if (frame.popInt() > 0) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IFLE           :
+                if (frame.popInt() <= 0) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ICMPEQ      :
+                if (frame.popInt() == frame.popInt()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ICMPNE      :
+                if (frame.popInt() != frame.popInt()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ICMPLT      :
+                if (frame.popInt() > frame.popInt()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ICMPGE      :
+                if (frame.popInt() <= frame.popInt()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ICMPGT      :
+                if (frame.popInt() < frame.popInt()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ICMPLE      :
+                if (frame.popInt() >= frame.popInt()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ACMPEQ      :
+                if (frame.popObject() == frame.popObject()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IF_ACMPNE      :
+                if (frame.popObject() != frame.popObject()) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.GOTO:
+            case Bytecodes.GOTO_W:
+                return BRANCH;
+            case Bytecodes.JSR            :
+            case Bytecodes.JSR_W          :
+                frame.pushInt(bs.currentBCI());
+                return BRANCH;
+            case Bytecodes.RET            :
+                return frame.getInt(frame.resolveLocalIndex(bs.readLocalIndex()));
+            case Bytecodes.TABLESWITCH    :
+                return tableSwitch(frame, bs);
+            case Bytecodes.LOOKUPSWITCH   :
+                return lookupSwitch(frame, bs);
+            case Bytecodes.IRETURN        :
+                frame.getParentFrame().pushInt(frame.popInt());
+                return RETURN;
+            case Bytecodes.LRETURN        :
+                frame.getParentFrame().pushLong(frame.popLong());
+                return RETURN;
+            case Bytecodes.FRETURN        :
+                frame.getParentFrame().pushFloat(frame.popFloat());
+                return RETURN;
+            case Bytecodes.DRETURN        :
+                frame.getParentFrame().pushDouble(frame.popDouble());
+                return RETURN;
+            case Bytecodes.ARETURN        :
+                frame.getParentFrame().pushObject(frame.popObject());
+                return RETURN;
+            case Bytecodes.RETURN         :
+                return RETURN;
+            case Bytecodes.GETSTATIC      :
+                getField(frame, null, bs.currentBC(), bs.readCPI());
+                break;
+            case Bytecodes.PUTSTATIC      :
+                putStatic(frame, bs.readCPI());
+                break;
+            case Bytecodes.GETFIELD:
+                getField(frame, nullCheck(frame.popObject()), bs.currentBC(), bs.readCPI());
+                break;
+            case Bytecodes.PUTFIELD       :
+                putField(frame, bs.readCPI());
+                break;
+            case Bytecodes.INVOKEVIRTUAL  :
+                callFrame = invokeVirtual(frame, bs.readCPI());
+                if (callFrame == null) {
+                    break;
+                }
+                return CALL;
+            case Bytecodes.INVOKESPECIAL  :
+                callFrame = invokeSpecial(frame, bs.readCPI());
+                if (callFrame == null) {
+                    break;
+                }
+                return CALL;
+            case Bytecodes.INVOKESTATIC   :
+                callFrame = invokeStatic(frame, bs.readCPI());
+                if (callFrame == null) {
+                    break;
+                }
+                return CALL;
+            case Bytecodes.INVOKEINTERFACE:
+                callFrame = invokeInterface(frame, bs.readCPI());
+                if (callFrame == null) {
+                    break;
+                }
+                return CALL;
+            case Bytecodes.XXXUNUSEDXXX   :
+                assert false : "unused bytecode used. behaviour unspecfied.";
+                // nop
+                break;
+            case Bytecodes.NEW            :
+                frame.pushObject(allocateInstance(frame, bs.readCPI()));
+                break;
+            case Bytecodes.NEWARRAY       :
+                frame.pushObject(allocateNativeArray(frame, bs.readByte()));
+                break;
+            case Bytecodes.ANEWARRAY      :
+                frame.pushObject(allocateArray(frame, bs.readCPI()));
+                break;
+            case Bytecodes.ARRAYLENGTH    :
+                frame.pushInt(Array.getLength(nullCheck(frame.popObject())));
+                break;
+            case Bytecodes.ATHROW         :
+                throw (Throwable) frame.popObject();
+            case Bytecodes.CHECKCAST      :
+                checkCast(frame, bs.readCPI());
+                break;
+            case Bytecodes.INSTANCEOF     :
+                instanceOf(frame, bs.readCPI());
+                break;
+            case Bytecodes.MONITORENTER   :
+                vm.monitorEnter(frame.popObject());
+                break;
+            case Bytecodes.MONITOREXIT    :
+                vm.monitorExit(frame.popObject());
+                break;
+            case Bytecodes.WIDE           :
+                assert false;
+                break;
+            case Bytecodes.MULTIANEWARRAY :
+                frame.pushObject(allocateMultiArray(frame, bs.readCPI(), bs.readUByte(bs.currentBCI() + 3)));
+                break;
+            case Bytecodes.IFNULL         :
+                if (frame.popObject() == null) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.IFNONNULL      :
+                if (frame.popObject() != null) {
+                    return BRANCH;
+                }
+                break;
+            case Bytecodes.BREAKPOINT     :
+                assert false : "no breakpoints supported at this time.";
+                break; // nop
+        }
+        return NEXT;
+    }
+
+    private InterpreterFrame handleThrowable(InterpreterFrame root, InterpreterFrame frame, Throwable t) {
+        ExceptionHandler handler;
+        InterpreterFrame currentFrame = frame;
+        do {
+            handler = resolveExceptionHandlers(currentFrame, currentFrame.getBCI(), t);
+            if (handler == null) {
+                // no handler found pop frame
+                // and continue searching
+                currentFrame = popFrame(currentFrame);
+            } else {
+                // found a handler -> execute it
+                if (handler.isCatchAll()) {
+                    catchAllExceptionHandler = handler;
+                } else {
+                    catchAllExceptionHandler = null;
+                }
+                currentFrame.setBCI(handler.handlerBCI());
+                currentFrame.popStack();
+                currentFrame.pushObject(t);
+                return currentFrame;
+            }
+        } while (handler == null && currentFrame != root);
+
+        // will throw exception up the interpreter
+        return null;
+    }
+
+    private void updateStackTrace(InterpreterFrame frame, Throwable t) {
+        StackTraceElement[] elements = getBackTrace(frame, t);
+        if (elements != null) {
+            setStackTrace(frame, t, elements);
+            setBackTrace(frame, t, null);
+        } else {
+            setBackTrace(frame, t, createStackTraceElements(frame));
+        }
+    }
+
+
+    private void setStackTrace(InterpreterFrame frame, Throwable t, StackTraceElement[] stackTrace) {
+      vm.setField(stackTrace, t,  findThrowableField(frame, "stackTrace"));
+    }
+
+    private StackTraceElement[] getBackTrace(InterpreterFrame frame, Throwable t) {
+        Object value = vm.getField(t, findThrowableField(frame, "backtrace"));
+        if (value instanceof StackTraceElement[]) {
+            return (StackTraceElement[]) value;
+        }
+        return null;
+    }
+
+    private void setBackTrace(InterpreterFrame frame, Throwable t, StackTraceElement[] backtrace) {
+        vm.setField(backtrace, t, findThrowableField(frame, "backtrace"));
+    }
+
+    private ExceptionHandler resolveExceptionHandlers(InterpreterFrame frame, int bci, Throwable t) {
+        ExceptionHandler[] handlers = frame.getMethod().exceptionHandlers();
+        for (int i = 0; i < handlers.length; i++) {
+            ExceptionHandler handler = handlers[i];
+            if (bci >= handler.startBCI()
+                            && bci <= handler.endBCI()
+                            && (catchAllExceptionHandler == null || !handler.isCatchAll())) {
+                ResolvedJavaType catchType = null;
+                if (!handler.isCatchAll()) {
+                    catchType = resolveType(frame, bci, (char) handler.catchTypeCPI());
+                }
+
+                if (catchType == null
+                                || catchType.toJava().isInstance(t)) {
+                    // the first found exception handler is our exception handler
+                   return handler;
+                }
+            }
+        }
+        return null;
+    }
+
+    private InterpreterFrame allocateFrame(InterpreterFrame frame, BytecodeStream bs) {
+        try {
+            stackFrameDepth++;
+
+            InterpreterFrame nextFrame = this.callFrame;
+
+            assert nextFrame != null;
+            assert nextFrame.getParentFrame() == frame;
+
+            // store bci when leaving method
+            frame.setBCI(bs.currentBCI());
+
+            if (TRACE) {
+                traceCall(nextFrame, "Call");
+            }
+            if (Modifier.isSynchronized(nextFrame.getMethod().accessFlags())) {
+                if (TRACE) {
+                    traceOp("Method monitor enter");
+                }
+                if (Modifier.isStatic(nextFrame.getMethod().accessFlags())) {
+                    vm.monitorEnter(nextFrame.getMethod().holder().toJava());
+                } else {
+                    Object enterObject = nextFrame.getObject(frame.resolveLocalIndex(0));
+                    assert enterObject != null;
+                    vm.monitorEnter(enterObject);
+                }
+            }
+
+            return nextFrame;
+        } finally {
+            // catch all exception handlers are not allowed to call any method anyway.
+            catchAllExceptionHandler = null;
+            callFrame = null;
+            bs.next();
+        }
+    }
+
+    private InterpreterFrame popFrame(InterpreterFrame frame) {
+        catchAllExceptionHandler = null;
+        InterpreterFrame parent = frame.getParentFrame();
+        if (Modifier.isSynchronized(frame.getMethod().accessFlags())) {
+            if (TRACE) {
+                traceOp("Method monitor exit");
+            }
+            if (Modifier.isStatic(frame.getMethod().accessFlags())) {
+                vm.monitorExit(frame.getMethod().holder().toJava());
+            } else {
+                Object exitObject = frame.getObject(frame.resolveLocalIndex(0));
+                if (exitObject != null) {
+                    vm.monitorExit(exitObject);
+                }
+            }
+        }
+        if (TRACE) {
+            traceCall(frame, "Ret");
+        }
+        stackFrameDepth--;
+
+        frame.dispose();
+        return parent;
+    }
+
+    private void traceOp(String opName) {
+        trace(stackFrameDepth + 1, opName);
+    }
+
+    private void traceCall(InterpreterFrame frame, String type) {
+        trace(stackFrameDepth, type + " " +
+                        frame.getMethod() + " - " + frame.getMethod().signature().asString());
+    }
+
+    private void trace(int level, String message) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < level; i++) {
+            builder.append("  ");
+        }
+        builder.append(message);
+        System.out.println(builder);
+    }
+
+
+    private void divInt(InterpreterFrame frame) {
+        int dividend = frame.popInt();
+        int divisor = frame.popInt();
+        frame.pushInt(divisor / dividend);
+    }
+
+    private void divLong(InterpreterFrame frame) {
+        long dividend = frame.popLong();
+        long divisor = frame.popLong();
+        frame.pushLong(divisor / dividend);
+    }
+
+    private void divFloat(InterpreterFrame frame) {
+        float dividend = frame.popFloat();
+        float divisor = frame.popFloat();
+        frame.pushFloat(divisor / dividend);
+    }
+
+    private void divDouble(InterpreterFrame frame) {
+        double dividend = frame.popDouble();
+        double divisor = frame.popDouble();
+        frame.pushDouble(divisor / dividend);
+    }
+
+
+    private void remInt(InterpreterFrame frame) {
+        int dividend = frame.popInt();
+        int divisor = frame.popInt();
+        frame.pushInt(divisor % dividend);
+    }
+
+    private void remLong(InterpreterFrame frame) {
+        long dividend = frame.popLong();
+        long divisor = frame.popLong();
+        frame.pushLong(divisor % dividend);
+    }
+
+    private void remFloat(InterpreterFrame frame) {
+        float dividend = frame.popFloat();
+        float divisor = frame.popFloat();
+        frame.pushFloat(divisor % dividend);
+    }
+
+    private void remDouble(InterpreterFrame frame) {
+        double dividend = frame.popDouble();
+        double divisor = frame.popDouble();
+        frame.pushDouble(divisor % dividend);
+    }
+
+    private void shiftLeftInt(InterpreterFrame frame) {
+        int bits = frame.popInt();
+        int value = frame.popInt();
+        frame.pushInt(value << bits);
+    }
+
+    private void shiftLeftLong(InterpreterFrame frame) {
+        int bits = frame.popInt();
+        long value = frame.popLong();
+        frame.pushLong(value << bits);
+    }
+
+    private void shiftRightSignedInt(InterpreterFrame frame) {
+        int bits = frame.popInt();
+        int value = frame.popInt();
+        frame.pushInt(value >> bits);
+    }
+
+    private void shiftRightSignedLong(InterpreterFrame frame) {
+        int bits = frame.popInt();
+        long value = frame.popLong();
+        frame.pushLong(value >> bits);
+    }
+
+    private void shiftRightUnsignedInt(InterpreterFrame frame) {
+        int bits = frame.popInt();
+        int value = frame.popInt();
+        frame.pushInt(value >>> bits);
+    }
+
+    private void shiftRightUnsignedLong(InterpreterFrame frame) {
+        int bits = frame.popInt();
+        long value = frame.popLong();
+        frame.pushLong(value >>> bits);
+    }
+
+    private int lookupSwitch(InterpreterFrame frame, BytecodeStream bs) {
+        return lookupSearch(
+                        new BytecodeLookupSwitch(bs, bs.currentBCI()), frame.popInt());
+    }
+
+    /**
+     * Binary search implementation for the lookup switch.
+     */
+    private int lookupSearch(BytecodeLookupSwitch switchHelper, int key) {
+        int low = 0;
+        int high = switchHelper.numberOfCases() - 1;
+        while (low <= high) {
+            int mid = (low + high) >>> 1;
+            int midVal = switchHelper.keyAt(mid);
+
+            if (midVal < key) {
+                low = mid + 1;
+            } else if (midVal > key) {
+                high = mid - 1;
+            } else {
+                return switchHelper.bci() + switchHelper.offsetAt(mid); // key found
+            }
+        }
+        return switchHelper.defaultTarget();  // key not found.
+    }
+
+    private int tableSwitch(InterpreterFrame frame, BytecodeStream bs) {
+        BytecodeTableSwitch switchHelper = new BytecodeTableSwitch(bs, bs.currentBCI());
+
+        int low = switchHelper.lowKey();
+        int high = switchHelper.highKey();
+
+        assert low <= high;
+
+        int index = frame.popInt();
+        if (index < low || index > high) {
+            return switchHelper.defaultTarget();
+        } else {
+            return switchHelper.targetAt(index - low);
+        }
+    }
+
+    private void checkCast(InterpreterFrame frame, char cpi) {
+        frame.pushObject(resolveType(frame, Bytecodes.CHECKCAST, cpi).toJava().cast(frame.popObject()));
+    }
+
+    private ResolvedJavaType resolveType(InterpreterFrame frame, int opcode, char cpi) {
+        ConstantPool constantPool = frame.getConstantPool();
+        constantPool.loadReferencedType(cpi, opcode);
+        return constantPool.lookupType(cpi, opcode).resolve(frame.getMethod().holder());
+    }
+
+    private ResolvedJavaType resolveType(InterpreterFrame frame, Class<?> javaClass) {
+        return vm.getRuntime().getResolvedJavaType(javaClass).resolve(frame.getMethod().holder());
+    }
+
+    private ResolvedJavaMethod resolveMethod(InterpreterFrame frame, int opcode, char cpi) {
+        ConstantPool constantPool = frame.getConstantPool();
+        constantPool.loadReferencedType(cpi, opcode);
+        return (ResolvedJavaMethod) constantPool.lookupMethod(cpi, opcode);
+    }
+
+    private ResolvedJavaField resolveField(InterpreterFrame frame, int opcode, char cpi) {
+        ConstantPool constantPool = frame.getConstantPool();
+        constantPool.loadReferencedType(cpi, opcode);
+        return (ResolvedJavaField) constantPool.lookupField(cpi, opcode);
+    }
+
+    private void instanceOf(InterpreterFrame frame, char cpi) {
+        frame.pushInt(resolveType(frame, Bytecodes.INSTANCEOF, cpi).
+                        toJava().isInstance(frame.popObject())
+                        ? 1 : 0);
+    }
+
+    private void pushCPConstant(InterpreterFrame frame, char cpi) {
+        ResolvedJavaMethod method = frame.getMethod();
+        Object constant = method.getConstantPool().lookupConstant(cpi);
+
+        if (constant instanceof Constant) {
+            Constant c = ((Constant) constant);
+            switch (c.kind) {
+                case Int :
+                    frame.pushInt(c.asInt());
+                    break;
+                case Float:
+                    frame.pushFloat(c.asFloat());
+                    break;
+                case Object:
+                    frame.pushObject(c.asObject());
+                    break;
+                case Double :
+                    frame.pushDouble(c.asDouble());
+                    break;
+                case Long :
+                    frame.pushLong(c.asLong());
+                    break;
+                default:
+                    assert false : "unspecified case";
+            }
+        } else if (constant instanceof JavaType) {
+            frame.pushObject(((JavaType) constant).resolve(method.holder()).toJava());
+        } else {
+            assert false : "unexpected case";
+        }
+    }
+
+    private void compareLong(InterpreterFrame frame) {
+        long y = frame.popLong();
+        long x = frame.popLong();
+        frame.pushInt((x < y) ? -1 : ((x == y) ? 0 : 1));
+    }
+
+    private void compareDoubleGreater(InterpreterFrame frame) {
+        double y = frame.popDouble();
+        double x = frame.popDouble();
+        frame.pushInt(x < y ? -1 : ((x == y) ? 0 : 1));
+    }
+
+    private void compareDoubleLess(InterpreterFrame frame) {
+        double y = frame.popDouble();
+        double x = frame.popDouble();
+        frame.pushInt(x > y ? 1 : ((x == y) ? 0 : -1));
+    }
+
+    private void compareFloatGreater(InterpreterFrame frame) {
+        float y = frame.popFloat();
+        float x = frame.popFloat();
+        frame.pushInt(x < y ? -1 : ((x == y) ? 0 : 1));
+    }
+
+    private void compareFloatLess(InterpreterFrame frame) {
+        float y = frame.popFloat();
+        float x = frame.popFloat();
+        frame.pushInt(x > y ? 1 : ((x == y) ? 0 : -1));
+    }
+
+    private Object nullCheck(Object value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+        return value;
+    }
+    private InterpreterFrame invokeStatic(InterpreterFrame frame, char cpi) throws Throwable {
+        return invoke(frame, resolveMethod(frame, Bytecodes.INVOKESTATIC, cpi), null);
+    }
+
+    private InterpreterFrame invokeInterface(InterpreterFrame frame, char cpi) throws Throwable {
+        return resolveAndInvoke(frame, resolveMethod(frame, Bytecodes.INVOKEINTERFACE, cpi));
+    }
+
+    private InterpreterFrame resolveAndInvoke(InterpreterFrame parent, ResolvedJavaMethod m) throws Throwable {
+        Object receiver = nullCheck(parent.peekReceiver(m));
+
+        ResolvedJavaMethod method = resolveType(parent, receiver.getClass()).resolveMethodImpl(m);
+
+        if (method == null) {
+            throw new AbstractMethodError();
+        }
+
+        return invoke(parent, method, receiver);
+    }
+
+    private InterpreterFrame invokeVirtual(InterpreterFrame frame, char cpi) throws Throwable {
+        ResolvedJavaMethod m = resolveMethod(frame, Bytecodes.INVOKEVIRTUAL, cpi);
+        if (Modifier.isFinal(m.accessFlags())) {
+            return invoke(frame, m, nullCheck(frame.peekReceiver(m)));
+        } else {
+            return resolveAndInvoke(frame, m);
+        }
+    }
+
+    private InterpreterFrame invokeSpecial(InterpreterFrame frame, char cpi) throws Throwable {
+        ResolvedJavaMethod m = resolveMethod(frame, Bytecodes.INVOKESPECIAL, cpi);
+        return invoke(frame, m, nullCheck(frame.peekReceiver(m)));
+    }
+
+    private void invokeReflective(InterpreterFrame frame, ResolvedJavaMethod method, boolean hasReceiver) throws Throwable {
+        Class<?>[] parameterTypes = resolveMethodArguments(method);
+        Object[] parameters = popArgumentsAsObject(frame, method);
+        Class<?> javaClass = method.holder().toJava();
+
+        Object parentObject = null;
+        if (hasReceiver) {
+            parentObject = frame.popObject();
+            nullCheck(parentObject);
+        }
+        Object returnValue = null;
+
+        if (redirect(method)) {
+            if (TRACE) {
+                traceCall(frame, "Delegate " + method);
+            }
+            try {
+                returnValue = methodDelegates.get(method).invoke(frame, method, parentObject, parameters);
+            } catch (Throwable e) {
+                throw e;
+            }
+        } else {
+            if (TRACE) {
+                traceCall(frame, "Reflective " + method);
+            }
+            try {
+                Method javaMethod = javaClass.getDeclaredMethod(method.name(), parameterTypes);
+                javaMethod.setAccessible(true);
+                returnValue = javaMethod.invoke(parentObject, parameters);
+            } catch (InvocationTargetException e) {
+                e.printStackTrace(System.out);
+                throw e.getTargetException();
+            } catch (NoSuchMethodException | SecurityException e) {
+                throw new AbstractMethodError();
+            }
+        }
+
+        pushAsObject(frame, method.signature().returnKind(), returnValue);
+    }
+
+    private Object[] popArgumentsAsObject(InterpreterFrame frame, ResolvedJavaMethod method) {
+        Signature signature = method.signature();
+        int argumentCount = method.signature().argumentCount(false);
+        Object[] parameters = new Object[argumentCount];
+        for (int i = argumentCount - 1; i >= 0; i--) {
+            ResolvedJavaType type = signature.argumentTypeAt(i, method.holder()).resolve(method.holder());
+            parameters[i] =  popAsObject(frame, type.kind());
+        }
+        return parameters;
+    }
+
+    private Class<?>[] resolveMethodArguments(ResolvedJavaMethod method) {
+        Signature signature = method.signature();
+        int argumentCount = signature.argumentCount(false);
+        Class<?>[] parameterTypes = new Class[argumentCount];
+
+        for (int i = 0; i < argumentCount; i++) {
+            JavaType type = signature.argumentTypeAt(i, method.holder());
+            ResolvedJavaType resolvedType = type.resolve(method.holder());
+            parameterTypes[i] = resolvedType.toJava();
+        }
+        return parameterTypes;
+    }
+
+    private InterpreterFrame invoke(InterpreterFrame parent, ResolvedJavaMethod method, Object receiver) throws Throwable {
+        if (stackFrameDepth >= maxStackFrames) {
+            throw new StackOverflowError("Maximum callstack of " + maxStackFrames + " exceeded.");
+        }
+        if (redirect(method)) {
+            invokeReflective(parent, method, receiver != null);
+
+            // returning null results in a jump to the next instruction
+            // since the method is already successfully invoked.
+            return null;
+        } else {
+            if (Modifier.isNative(method.accessFlags())) {
+                invokeReflective(parent, method, receiver != null);
+                return null;
+            } else {
+                return parent.create(method, receiver != null);
+            }
+        }
+    }
+
+    private boolean redirect(ResolvedJavaMethod method) {
+        return methodDelegates.containsKey(method);
+    }
+
+
+    private Object allocateMultiArray(InterpreterFrame frame, char cpi, int dimension) {
+        ResolvedJavaType type = resolveType(frame, Bytecodes.MULTIANEWARRAY, cpi);
+        int[] dimensions = new int[dimension];
+        for (int i = 0; i < dimension; i++) {
+            dimensions[i] = frame.popInt();
+        }
+        return Array.newInstance(type.toJava(), dimensions);
+    }
+
+    private Object allocateArray(InterpreterFrame frame, char cpi) {
+        ResolvedJavaType type = resolveType(frame, Bytecodes.ANEWARRAY, cpi);
+        return Array.newInstance(type.toJava(), frame.popInt());
+    }
+
+    private Object allocateNativeArray(InterpreterFrame frame, byte cpi) {
+        // the constants for the cpi are loosely defined and no real cpi indices.
+        switch (cpi) {
+            case 4 :
+                return new byte[frame.popInt()];
+            case 8 :
+                return new byte[frame.popInt()];
+            case 5 :
+                return new char[frame.popInt()];
+            case 7 :
+                return new double[frame.popInt()];
+            case 6 :
+                return new float[frame.popInt()];
+            case 10 :
+                return new int[frame.popInt()];
+            case 11 :
+                return new long[frame.popInt()];
+            case 9 :
+                return new short[frame.popInt()];
+            default:
+                assert false : "unexpected case";
+                return null;
+        }
+    }
+
+    private Object allocateInstance(InterpreterFrame frame, char cpi) throws InstantiationException {
+        return vm.newObject(resolveType(frame, Bytecodes.NEW, cpi));
+    }
+
+    private void iinc(InterpreterFrame frame, BytecodeStream bs) {
+        int index = frame.resolveLocalIndex(bs.readLocalIndex());
+        frame.setInt(index, frame.getInt(index) + bs.readIncrement());
+    }
+
+    private void putStatic(InterpreterFrame frame, char cpi) {
+        putFieldStatic(frame, resolveField(frame, Bytecodes.PUTSTATIC, cpi));
+    }
+
+    private void putField(InterpreterFrame frame, char cpi) {
+        putFieldVirtual(frame, resolveField(frame, Bytecodes.PUTFIELD, cpi));
+    }
+
+    private void putFieldStatic(InterpreterFrame frame, ResolvedJavaField field) {
+        switch (field.kind()) {
+            case Boolean :
+            case Byte :
+            case Char :
+            case Short :
+            case Int :
+                vm.setFieldInt(frame.popInt(), null, field);
+                break;
+            case Double :
+                vm.setFieldDouble(frame.popDouble(), null, field);
+                break;
+            case Float :
+                vm.setFieldFloat(frame.popFloat(), null, field);
+                break;
+            case Long :
+                vm.setFieldLong(frame.popLong(), null, field);
+                break;
+            case Object :
+                vm.setField(frame.popObject(), null, field);
+                break;
+            default :
+                assert false : "unexpected case";
+        }
+    }
+
+    private void putFieldVirtual(InterpreterFrame frame, ResolvedJavaField field) {
+        switch (field.kind()) {
+            case Boolean :
+            case Byte :
+            case Char :
+            case Short :
+            case Int :
+                vm.setFieldInt(frame.popInt(), nullCheck(frame.popObject()), field);
+                break;
+            case Double :
+                vm.setFieldDouble(frame.popDouble(), nullCheck(frame.popObject()), field);
+                break;
+            case Float :
+                vm.setFieldFloat(frame.popFloat(), nullCheck(frame.popObject()), field);
+                break;
+            case Long :
+                vm.setFieldLong(frame.popLong(), nullCheck(frame.popObject()), field);
+                break;
+            case Object :
+                vm.setField(frame.popObject(), nullCheck(frame.popObject()), field);
+                break;
+            default :
+                assert false : "unexpected case";
+        }
+    }
+
+    private void getField(InterpreterFrame frame, Object base, int opcode, char cpi) {
+        ResolvedJavaField field = resolveField(frame, opcode, cpi);
+        switch (field.kind()) {
+            case Boolean :
+            case Byte :
+            case Char :
+            case Short :
+            case Int :
+                frame.pushInt(vm.getFieldInt(base, field));
+                break;
+            case Double :
+                frame.pushDouble(vm.getFieldDouble(base, field));
+                break;
+            case Float :
+                frame.pushFloat(vm.getFieldFloat(base, field));
+                break;
+            case Long :
+                frame.pushLong(vm.getFieldLong(base, field));
+                break;
+            case Object :
+                frame.pushObject(vm.getField(base, field));
+                break;
+            default :
+                assert false : "unexpected case";
+        }
+    }
+
+
+    private int pushAsObject(InterpreterFrame frame, Kind typeKind, Object value) {
+        switch(typeKind) {
+            case Int :
+                frame.pushInt((int) value);
+                break;
+            case Long :
+                frame.pushLong((long) value);
+                return 2;
+            case Boolean :
+                frame.pushInt(((boolean) value) ? 1 : 0);
+                break;
+            case Byte :
+                frame.pushInt((byte) value);
+                break;
+            case Char :
+                frame.pushInt((char) value);
+                break;
+            case Double :
+                frame.pushDouble((double) value);
+                return 2;
+            case Float :
+                frame.pushFloat((float) value);
+                break;
+            case Short :
+                frame.pushInt((short) value);
+                break;
+            case Object :
+                frame.pushObject(value);
+                break;
+            case Void :
+                return 0;
+            default :
+                assert false : "case not specified";
+        }
+        return 1;
+    }
+
+    private Object popAsObject(InterpreterFrame frame, Kind typeKind) {
+        switch (typeKind) {
+            case Boolean :
+                return frame.popInt() == 1 ? true : false;
+            case Byte :
+                return (byte) frame.popInt();
+            case Char :
+                return (char) frame.popInt();
+            case Double :
+                return frame.popDouble();
+            case Int :
+                return frame.popInt();
+            case Float :
+                return frame.popFloat();
+            case Long :
+                return frame.popLong();
+            case Short :
+                return (short) frame.popInt();
+            case Object :
+                return frame.popObject();
+            case Void :
+                return null;
+            default :
+                assert false : "unexpected case";
+        }
+        return null;
+    }
+
+    private ResolvedJavaMethod resolveRootMethod() {
+        try {
+            return vm.getRuntime().getResolvedJavaMethod(BytecodeInterpreter.class.getDeclaredMethod("execute", Method.class, Object[].class));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Method findMethod(Class<?> clazz, String name, Class<?> ... parameters) {
+        try {
+            return clazz.getDeclaredMethod(name, parameters);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static StackTraceElement[] createStackTraceElements(InterpreterFrame frame) {
+        InterpreterFrame tmp = frame;
+        List<StackTraceElement> elements = new ArrayList<>();
+        boolean first = false; // filter only first stack elements
+        while (tmp != null) {
+            if (first || !filterStackElement(tmp)) {
+                first = true;
+                elements.add(tmp.getMethod().toStackTraceElement(tmp.getBCI()));
+            }
+            tmp = tmp.getParentFrame();
+        }
+        return elements.toArray(new StackTraceElement[elements.size()]);
+    }
+
+    private static boolean filterStackElement(InterpreterFrame frame) {
+        return Throwable.class.isAssignableFrom(frame.getMethod().holder().toJava());
+    }
+
+    private ResolvedJavaField findThrowableField(InterpreterFrame frame, String name) {
+        ResolvedJavaType throwableType = resolveType(frame, Throwable.class);
+        ResolvedJavaField[] fields = throwableType.declaredFields();
+        for (int i = 0; i < fields.length; i++) {
+            if (fields[i].name().equals(name)) {
+                return fields[i];
+            }
+        }
+        assert false;
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/Frame.java	Mon Jun 11 17:01:02 2012 +0200
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 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.oracle.graal.interpreter;
+
+import java.lang.reflect.*;
+
+import sun.misc.*;
+
+public class Frame {
+
+    public static final Object[] EMPTY_ARRAY = new Object[0];
+    public static final int PARENT_FRAME_SLOT = 0;
+    public static final int MIN_FRAME_SIZE = 1;
+    private static final Unsafe unsafe = getUnsafe();
+
+    protected final Object[] locals;
+    protected final long[] primitiveLocals;
+
+    public Frame(int numLocals, Frame parent) {
+        assert numLocals >= MIN_FRAME_SIZE;
+        this.locals = new Object[numLocals];
+        this.locals[PARENT_FRAME_SLOT] = parent;
+        this.primitiveLocals = new long[numLocals];
+    }
+
+    public Frame(int numLocals) {
+        this(numLocals, null);
+    }
+
+    public Object getObject(int index) {
+        return locals[index];
+    }
+
+    public void setObject(int index, Object value) {
+        locals[index] = value;
+    }
+
+    public float getFloat(int index) {
+        return unsafe.getFloat(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET);
+    }
+
+    public void setFloat(int index, float value) {
+        unsafe.putFloat(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET, value);
+    }
+
+    public long getLong(int index) {
+        return unsafe.getLong(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET);
+    }
+
+    public void setLong(int index, long value) {
+        unsafe.putLong(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET, value);
+    }
+
+    public int getInt(int index) {
+        return unsafe.getInt(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET);
+    }
+
+    public void setInt(int index, int value) {
+        unsafe.putInt(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET, value);
+    }
+
+    public double getDouble(int index) {
+        return unsafe.getDouble(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET);
+    }
+
+    public void setDouble(int index, double value) {
+        unsafe.putDouble(primitiveLocals, (long) index * Unsafe.ARRAY_LONG_INDEX_SCALE + Unsafe.ARRAY_LONG_BASE_OFFSET, value);
+    }
+
+    public static Frame getParentFrame(Frame frame, int level) {
+        assert level >= 0;
+        if (level == 0) {
+            return frame;
+        } else {
+            return getParentFrame((Frame) frame.getObject(PARENT_FRAME_SLOT), level - 1);
+        }
+    }
+
+    public static Frame getTopFrame(Frame frame) {
+        Frame parentFrame = (Frame) frame.getObject(PARENT_FRAME_SLOT);
+        if (parentFrame == null) {
+            return frame;
+        } else {
+            return getTopFrame(parentFrame);
+        }
+    }
+
+    public static Object[] getArguments(Frame frame, int argOffset) {
+        return (Object[]) frame.getObject(argOffset);
+    }
+
+    public int size() {
+        return locals.length;
+    }
+
+    @SuppressWarnings("unused")
+    private boolean indexExists(int index) {
+        return index >= 0 && index < locals.length;
+    }
+
+    private static Unsafe getUnsafe() {
+        try {
+            return Unsafe.getUnsafe();
+        } catch (SecurityException e) {
+        }
+        try {
+            Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
+            theUnsafeInstance.setAccessible(true);
+            return (Unsafe) theUnsafeInstance.get(Unsafe.class);
+        } catch (Exception e) {
+            throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/InterpreterCallable.java	Mon Jun 11 17:01:02 2012 +0200
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 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.oracle.graal.interpreter;
+
+import com.oracle.graal.api.meta.*;
+
+
+public interface InterpreterCallable {
+
+    Object invoke(InterpreterFrame caller, ResolvedJavaMethod method, Object base, Object[] arguments) throws Throwable;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/InterpreterException.java	Mon Jun 11 17:01:02 2012 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 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.oracle.graal.interpreter;
+
+/**
+ * Thrown if executed byte code caused an error in {@link BytecodeInterpreter}. The actual execution exception is
+ * accessible using {@link #getCause()} or {@link #getExecutionThrowable()}.
+ */
+public class InterpreterException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public InterpreterException(Throwable cause) {
+        super(cause);
+    }
+
+    public Throwable getExecutionThrowable() {
+        return getCause();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/InterpreterFrame.java	Mon Jun 11 17:01:02 2012 +0200
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 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.oracle.graal.interpreter;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+
+public class InterpreterFrame extends Frame {
+
+    public static final int BASE_LENGTH = 3;
+
+    private static final int METHOD_FRAME_SLOT = 1;
+    private static final int BCI_FRAME_SLOT = 2;
+
+    private static final int DOUBLE = 2;
+    private static final int SINGLE = 1;
+
+    /** Pointer to the top-most stack frame element. */
+    private int tos;
+
+    public InterpreterFrame(ResolvedJavaMethod method, int additionalStackSpace) {
+        this(method, null, additionalStackSpace);
+    }
+
+    private InterpreterFrame(ResolvedJavaMethod method, InterpreterFrame parent, int additionalStackSpace) {
+        super(method.maxLocals() + method.maxStackSize() + BASE_LENGTH + additionalStackSpace, parent);
+        setMethod(method);
+        setBCI(0);
+        this.tos = BASE_LENGTH;
+    }
+
+    public InterpreterFrame create(ResolvedJavaMethod method, boolean hasReceiver) {
+        InterpreterFrame frame = new InterpreterFrame(method, this, 0);
+        int length = method.signature().argumentSlots(hasReceiver);
+
+        frame.pushVoid(method.maxLocals());
+        if (length > 0) {
+            copyArguments(frame, length);
+            popVoid(length);
+        }
+
+        return frame;
+    }
+
+    public int resolveLocalIndex(int index) {
+        return BASE_LENGTH + index;
+    }
+
+    private int stackTos() {
+        return BASE_LENGTH + getMethod().maxLocals();
+    }
+
+    private void copyArguments(InterpreterFrame dest, int length) {
+        System.arraycopy(locals, tosSingle(length - 1), dest.locals,
+                        BASE_LENGTH, length);
+        System.arraycopy(primitiveLocals, tosSingle(length - 1), dest.primitiveLocals,
+                        BASE_LENGTH, length);
+    }
+
+
+    public Object peekReceiver(ResolvedJavaMethod method) {
+        return getObject(tosSingle(method.signature().argumentSlots(false)));
+    }
+
+    public void pushBoth(Object oValue, int intValue) {
+        incrementTos(SINGLE);
+        setObject(tosSingle(0), oValue);
+        setInt(tosSingle(0), intValue);
+    }
+
+    public void pushBoth(Object oValue, long longValue) {
+        incrementTos(SINGLE);
+        setObject(tosSingle(0), oValue);
+        setLong(tosSingle(0), longValue);
+    }
+
+    public void pushObject(Object value) {
+        incrementTos(SINGLE);
+        setObject(tosSingle(0), value);
+    }
+
+    public void pushInt(int value) {
+        incrementTos(SINGLE);
+        setInt(tosSingle(0), value);
+    }
+
+    public void pushDouble(double value) {
+        incrementTos(DOUBLE);
+        setDouble(tosDouble(0), value);
+    }
+
+    public void pushFloat(float value) {
+        incrementTos(SINGLE);
+        setFloat(tosSingle(0), value);
+    }
+
+    public void pushLong(long value) {
+        incrementTos(DOUBLE);
+        setLong(tosDouble(0), value);
+    }
+
+    public int popInt() {
+        int value = getInt(tosSingle(0));
+        decrementTos(SINGLE);
+        return value;
+    }
+
+    public double popDouble() {
+        double value = getDouble(tosDouble(0));
+        decrementTos(DOUBLE);
+        return value;
+    }
+
+    public float popFloat() {
+        float value = getFloat(tosSingle(0));
+        decrementTos(SINGLE);
+        return value;
+    }
+
+    public long popLong() {
+        long value = getLong(tosDouble(0));
+        decrementTos(DOUBLE);
+        return value;
+    }
+
+    public Object popObject() {
+        Object value = getObject(tosSingle(0));
+        decrementTos(SINGLE);
+        return value;
+    }
+
+    public void swapSingle() {
+        int tmpInt = getInt(tosSingle(1));
+        Object tmpObject = getObject(tosSingle(1));
+
+        setInt(tosSingle(1), getInt(tosSingle(0)));
+        setObject(tosSingle(1), getObject(tosSingle(0)));
+
+        setInt(tosSingle(0), tmpInt);
+        setObject(tosSingle(0), tmpObject);
+    }
+
+    public void dupx1() {
+        long tosLong = getLong(tosSingle(0));
+        Object tosObject = getObject(tosSingle(0));
+
+        swapSingle();
+
+        pushBoth(tosObject, tosLong);
+    }
+
+    public void dup2x1() {
+        long tosLong2 = getLong(tosSingle(2));
+        Object tosObject2 = getObject(tosSingle(2));
+        long tosLong1 = getLong(tosSingle(1));
+        Object tosObject1 = getObject(tosSingle(1));
+        long tosLong0 = getLong(tosSingle(0));
+        Object tosObject0 = getObject(tosSingle(0));
+
+        popVoid(3);
+
+        pushBoth(tosObject1, tosLong1);
+        pushBoth(tosObject0, tosLong0);
+
+        pushBoth(tosObject2, tosLong2);
+
+        pushBoth(tosObject1, tosLong1);
+        pushBoth(tosObject0, tosLong0);
+    }
+
+    public void dup2x2() {
+        long tosLong3 = getLong(tosSingle(3));
+        Object tosObject3 = getObject(tosSingle(3));
+        long tosLong2 = getLong(tosSingle(2));
+        Object tosObject2 = getObject(tosSingle(2));
+        long tosLong1 = getLong(tosSingle(1));
+        Object tosObject1 = getObject(tosSingle(1));
+        long tosLong0 = getLong(tosSingle(0));
+        Object tosObject0 = getObject(tosSingle(0));
+
+        popVoid(4);
+
+        pushBoth(tosObject1, tosLong1);
+        pushBoth(tosObject0, tosLong0);
+
+        pushBoth(tosObject3, tosLong3);
+        pushBoth(tosObject2, tosLong2);
+
+        pushBoth(tosObject1, tosLong1);
+        pushBoth(tosObject0, tosLong0);
+    }
+
+    public void dupx2() {
+        long tosLong2 = getLong(tosSingle(2));
+        Object tosObject2 = getObject(tosSingle(2));
+        long tosLong1 = getLong(tosSingle(1));
+        Object tosObject1 = getObject(tosSingle(1));
+        long tosLong0 = getLong(tosSingle(0));
+        Object tosObject0 = getObject(tosSingle(0));
+
+        popVoid(3);
+
+        pushBoth(tosObject0, tosLong0);
+        pushBoth(tosObject2, tosLong2);
+        pushBoth(tosObject1, tosLong1);
+        pushBoth(tosObject0, tosLong0);
+    }
+
+    public void dup(int length) {
+        assert length > 0;
+        for (int i = 0; i < length; i++) {
+            long valueN1 = getLong(tosSingle(length - i - 1));
+            Object valueO1 = getObject(tosSingle(length - i - 1));
+
+            pushVoid(1);
+
+            setLong(tosSingle(0), valueN1);
+            setObject(tosSingle(0), valueO1);
+        }
+    }
+
+    private void incrementTos(int size) {
+        tos += size;
+    }
+
+    private void decrementTos(int size) {
+        tos -= size;
+    }
+
+    private int tosDouble(int offset) {
+        assert offset >= 0;
+        return tos - DOUBLE - (offset * DOUBLE);
+    }
+
+    private int tosSingle(int offset) {
+        assert offset >= 0;
+        return tos - SINGLE - offset;
+    }
+
+    public int getStackTop() {
+        return tos;
+    }
+
+    public void pushVoid(int count) {
+        incrementTos(count * SINGLE);
+    }
+
+    public void popVoid(int count) {
+        decrementTos(count * SINGLE);
+    }
+
+    public ConstantPool getConstantPool() {
+        return getMethod().getConstantPool();
+    }
+
+    private void setMethod(ResolvedJavaMethod method) {
+        setObject(METHOD_FRAME_SLOT, method);
+    }
+
+    public ResolvedJavaMethod getMethod() {
+        return (ResolvedJavaMethod) getObject(METHOD_FRAME_SLOT);
+    }
+
+    public void setBCI(int bci) {
+        setInt(BCI_FRAME_SLOT, bci);
+    }
+
+    public int getBCI() {
+        return getInt(BCI_FRAME_SLOT);
+    }
+
+    public void pushTo(InterpreterFrame childFrame, int argumentSlots) {
+        System.arraycopy(locals, tos - argumentSlots, childFrame.locals,
+                        InterpreterFrame.MIN_FRAME_SIZE, argumentSlots);
+
+        System.arraycopy(primitiveLocals, tos - argumentSlots, childFrame.primitiveLocals,
+                        InterpreterFrame.MIN_FRAME_SIZE, argumentSlots);
+        popVoid(argumentSlots);
+    }
+
+    public InterpreterFrame getParentFrame() {
+        return (InterpreterFrame) getObject(PARENT_FRAME_SLOT);
+    }
+
+    public void dispose() {
+        // Clear out references in locals array.
+        Arrays.fill(locals, null);
+    }
+
+    @Override
+    public String toString() {
+        ResolvedJavaMethod method = getMethod();
+        StringBuilder b = new StringBuilder(getMethod().toStackTraceElement(getBCI()).toString());
+        for (int i = 0; i < tos; i++) {
+            Object object = getObject(tosSingle(i));
+            long primitive = getLong(tosSingle(i));
+
+            String objectString = null;
+            if (object != null) {
+                objectString = object.getClass().getSimpleName() + "@" + Integer.toHexString(object.hashCode());
+            }
+            String primitiveString = "0x" + Long.toHexString(primitive).toUpperCase();
+            String typeString;
+
+            int index = tosSingle(i);
+            if (index == METHOD_FRAME_SLOT) {
+                typeString = "method";
+            } else if (index == BCI_FRAME_SLOT) {
+                typeString = "bci";
+            } else if (index == PARENT_FRAME_SLOT) {
+                typeString = "parent";
+            } else if (index < BASE_LENGTH + method.maxLocals()) {
+                typeString = "var " + (index - BASE_LENGTH);
+            } else {
+                typeString = "local";
+            }
+            b.append(String.format("%n [%d] %7s Primitive: %10s Object: %s", index, typeString, primitiveString, objectString));
+        }
+        if (getParentFrame() != null) {
+            b.append("\n").append(getParentFrame().toString());
+        }
+        return b.toString();
+    }
+
+    public void popStack() {
+        // TODO(chumer): prevent popping local variables.
+        popVoid(tos - stackTos());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/VMAdapter.java	Mon Jun 11 17:01:02 2012 +0200
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 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.oracle.graal.interpreter;
+
+import java.lang.reflect.*;
+
+import sun.misc.*;
+
+import com.oracle.graal.api.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.hotspot.meta.*;
+
+
+/**
+ * This class encapsulates all vm specific methods for the {@link BytecodeInterpreter}.
+ */
+public class VMAdapter {
+
+    private static final Unsafe unsafe = loadUnsafe();
+
+    protected VMAdapter() {
+    }
+
+    public MetaAccessProvider getRuntime() {
+        return Graal.getRuntime().getCapability(MetaAccessProvider.class);
+    }
+
+    public void monitorEnter(Object value) {
+        nullCheck(value);
+        unsafe.monitorEnter(value);
+    }
+
+    public void monitorExit(Object value) {
+        nullCheck(value);
+        unsafe.monitorExit(value);
+    }
+
+    public Object newObject(ResolvedJavaType type) throws InstantiationException {
+        return unsafe.allocateInstance(type.toJava());
+    }
+
+    public Object getField(Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            return unsafe.getObjectVolatile(resolveBase(base, field), offset);
+        } else {
+            return unsafe.getObject(resolveBase(base, field), offset);
+        }
+    }
+
+    public int getFieldInt(Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            return unsafe.getIntVolatile(resolveBase(base, field), offset);
+        } else {
+            return unsafe.getInt(resolveBase(base, field), offset);
+        }
+    }
+
+    public long getFieldLong(Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            return unsafe.getLongVolatile(resolveBase(base, field), offset);
+        } else {
+            return unsafe.getLong(resolveBase(base, field), offset);
+        }
+    }
+
+    public double getFieldDouble(Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            return unsafe.getDoubleVolatile(resolveBase(base, field), offset);
+        } else {
+            return unsafe.getDouble(resolveBase(base, field), offset);
+        }
+    }
+
+    public float getFieldFloat(Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            return unsafe.getFloatVolatile(resolveBase(base, field), offset);
+        } else {
+            return unsafe.getFloat(resolveBase(base, field), offset);
+        }
+    }
+
+    public void setField(Object value, Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            unsafe.putObjectVolatile(resolveBase(base, field), offset, value);
+        } else {
+            unsafe.putObject(resolveBase(base, field), offset, value);
+        }
+    }
+
+    public void setFieldInt(int value, Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            unsafe.putIntVolatile(resolveBase(base, field), offset, value);
+        } else {
+            unsafe.putInt(resolveBase(base, field), offset, value);
+        }
+    }
+
+
+    public void setFieldFloat(float value, Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            unsafe.putFloatVolatile(resolveBase(base, field), offset, value);
+        } else {
+            unsafe.putFloat(resolveBase(base, field), offset, value);
+        }
+    }
+
+    public void setFieldDouble(double value, Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            unsafe.putDoubleVolatile(resolveBase(base, field), offset, value);
+        } else {
+            unsafe.putDouble(resolveBase(base, field), offset, value);
+        }
+    }
+
+    public void setFieldLong(long value, Object base, ResolvedJavaField field) {
+        long offset = resolveOffset(field);
+        if (isVolatile(field)) {
+            unsafe.putDoubleVolatile(resolveBase(base, field), offset, value);
+        } else {
+            unsafe.putDouble(resolveBase(base, field), offset, value);
+        }
+    }
+
+    public byte getArrayByte(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getByte(array, Unsafe.ARRAY_BYTE_BASE_OFFSET + Unsafe.ARRAY_BYTE_INDEX_SCALE * index);
+    }
+
+    public char getArrayChar(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getChar(array, Unsafe.ARRAY_CHAR_BASE_OFFSET + Unsafe.ARRAY_CHAR_INDEX_SCALE * index);
+    }
+
+    public short getArrayShort(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getShort(array, Unsafe.ARRAY_SHORT_BASE_OFFSET + Unsafe.ARRAY_SHORT_INDEX_SCALE * index);
+    }
+
+    public int getArrayInt(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getInt(array, Unsafe.ARRAY_INT_BASE_OFFSET + Unsafe.ARRAY_INT_INDEX_SCALE * index);
+    }
+
+    public long getArrayLong(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getLong(array, Unsafe.ARRAY_LONG_BASE_OFFSET + Unsafe.ARRAY_LONG_INDEX_SCALE * index);
+    }
+
+    public double getArrayDouble(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getDouble(array, Unsafe.ARRAY_DOUBLE_BASE_OFFSET + Unsafe.ARRAY_DOUBLE_INDEX_SCALE * index);
+    }
+
+    public float getArrayFloat(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getFloat(array, Unsafe.ARRAY_FLOAT_BASE_OFFSET + Unsafe.ARRAY_FLOAT_INDEX_SCALE * index);
+    }
+
+    public Object getArrayObject(long index, Object array) {
+        checkArray(array, index);
+        return unsafe.getObject(array, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index);
+    }
+
+    public void setArrayByte(byte value, long index, Object array) {
+        checkArray(array, index);
+        if (array instanceof boolean[]) {
+            checkArrayType(array, boolean.class);
+        } else {
+            checkArrayType(array, byte.class);
+        }
+        unsafe.putByte(array, Unsafe.ARRAY_BYTE_BASE_OFFSET + Unsafe.ARRAY_BYTE_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayChar(char value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, char.class);
+        unsafe.putChar(array, Unsafe.ARRAY_CHAR_BASE_OFFSET + Unsafe.ARRAY_CHAR_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayShort(short value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, short.class);
+        unsafe.putShort(array, Unsafe.ARRAY_SHORT_BASE_OFFSET + Unsafe.ARRAY_SHORT_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayInt(int value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, int.class);
+        unsafe.putInt(array, Unsafe.ARRAY_INT_BASE_OFFSET + Unsafe.ARRAY_INT_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayLong(long value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, long.class);
+        unsafe.putLong(array, Unsafe.ARRAY_LONG_BASE_OFFSET + Unsafe.ARRAY_LONG_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayFloat(float value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, float.class);
+        unsafe.putFloat(array, Unsafe.ARRAY_FLOAT_BASE_OFFSET + Unsafe.ARRAY_FLOAT_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayDouble(double value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, double.class);
+        unsafe.putDouble(array, Unsafe.ARRAY_DOUBLE_BASE_OFFSET + Unsafe.ARRAY_DOUBLE_INDEX_SCALE * index, value);
+    }
+
+    public void setArrayObject(Object value, long index, Object array) {
+        checkArray(array, index);
+        checkArrayType(array, value != null ? value.getClass() : null);
+        unsafe.putObject(array, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index, value);
+    }
+
+    private static void nullCheck(Object value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+    }
+
+    private void checkArrayType(Object array, Class<?> arrayType) {
+        if (arrayType == null) {
+            return;
+        }
+        ResolvedJavaType type = getRuntime().getResolvedJavaType(array.getClass()).componentType();
+        if (!type.toJava().isAssignableFrom(arrayType)) {
+            throw new ArrayStoreException(arrayType.getName());
+        }
+    }
+
+    private void checkArray(Object array, long index) {
+        nullCheck(array);
+        ResolvedJavaType type = getRuntime().getResolvedJavaType(array.getClass());
+        if (!type.isArrayClass()) {
+            throw new ArrayStoreException(array.getClass().getName());
+        }
+        if (index < 0 || index >= arrayLength(array)) {
+            throw new ArrayIndexOutOfBoundsException((int) index);
+        }
+    }
+
+    private static int arrayLength(Object array) {
+        assert array != null;
+        return Array.getLength(array);
+    }
+
+    private static boolean isVolatile(ResolvedJavaField field) {
+        return Modifier.isVolatile(field.accessFlags());
+    }
+
+    private static long resolveOffset(ResolvedJavaField field) {
+        return ((HotSpotResolvedJavaField) field).offset();
+    }
+
+    private static Object resolveBase(Object base, ResolvedJavaField field) {
+        Object accessorBase = base;
+        if (accessorBase == null) {
+            accessorBase = field.holder().toJava();
+        }
+        return accessorBase;
+    }
+
+    private static Unsafe loadUnsafe() {
+        try {
+            return Unsafe.getUnsafe();
+        } catch (SecurityException e) {
+        }
+        try {
+            Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
+            theUnsafeInstance.setAccessible(true);
+            return (Unsafe) theUnsafeInstance.get(Unsafe.class);
+        } catch (Exception e) {
+            throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e);
+        }
+    }
+
+    private static final VMAdapter instance = new VMAdapter();
+    public static VMAdapter getInstance() {
+        return instance;
+    }
+
+
+}
--- a/mx/projects	Mon Jun 11 13:51:34 2012 +0200
+++ b/mx/projects	Mon Jun 11 17:01:02 2012 +0200
@@ -130,6 +130,13 @@
 project@com.oracle.graal.nodes@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.nodes@javaCompliance=1.7
 
+# graal.interpreter
+project@com.oracle.graal.interpreter@subDir=graal
+project@com.oracle.graal.interpreter@sourceDirs=src
+project@com.oracle.graal.interpreter@dependencies=com.oracle.graal.hotspot
+project@com.oracle.graal.interpreter@checkstyle=com.oracle.graal.graph
+project@com.oracle.graal.interpreter@javaCompliance=1.7
+
 # graal.compiler
 project@com.oracle.graal.compiler@subDir=graal
 project@com.oracle.graal.compiler@sourceDirs=src