Merge "Fix a bunch of JDWP bugs." into dalvik-dev
diff --git a/src/asm_support.h b/src/asm_support.h
index d3e18bc..6ba23bc 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -24,13 +24,13 @@
 #define rSELF r9
 #define rLR r14
 // Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_SUSPEND_COUNT_OFFSET 424
+#define THREAD_SUSPEND_COUNT_OFFSET 120
 // Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 420
+#define THREAD_EXCEPTION_OFFSET 116
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 412
+#define THREAD_SELF_OFFSET 108
 #endif
 
 #endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index bc13cb2..e9a94a9 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -922,9 +922,9 @@
   EXPECT_NE(Jj2, Aj2);
   EXPECT_EQ(Kj1, Jj1);
   EXPECT_EQ(Kj2, Jj2);
-  EXPECT_EQ(Ai, A->FindVirtualMethodForInterface(Ii, true));
-  EXPECT_EQ(Aj1, A->FindVirtualMethodForInterface(Jj1, true));
-  EXPECT_EQ(Aj2, A->FindVirtualMethodForInterface(Jj2, true));
+  EXPECT_EQ(Ai, A->FindVirtualMethodForInterface(Ii));
+  EXPECT_EQ(Aj1, A->FindVirtualMethodForInterface(Jj1));
+  EXPECT_EQ(Aj2, A->FindVirtualMethodForInterface(Jj2));
   EXPECT_EQ(Ai, A->FindVirtualMethodForVirtualOrInterface(Ii));
   EXPECT_EQ(Aj1, A->FindVirtualMethodForVirtualOrInterface(Jj1));
   EXPECT_EQ(Aj2, A->FindVirtualMethodForVirtualOrInterface(Jj2));
diff --git a/src/compiler.cc b/src/compiler.cc
index c4c8424..8210c9b 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -250,20 +250,32 @@
   return referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable();
 }
 
+static Class* ComputeReferrerClass(CompilationUnit* cUnit) {
+  const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
+  return cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
+                                          cUnit->dex_cache, cUnit->class_loader);
+}
+
+static Field* ComputeReferrerField(CompilationUnit* cUnit, uint32_t field_idx) {
+  return cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
+                                           cUnit->class_loader, false);
+
+}
+
+static Method* ComputeReferrerMethod(CompilationUnit* cUnit, uint32_t method_idx) {
+  return cUnit->class_linker->ResolveMethod(*cUnit->dex_file, method_idx, cUnit->dex_cache,
+                                            cUnit->class_loader, true);
+}
+
 bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
                                         int& field_offset, bool& is_volatile) const {
   // Conservative defaults
   field_offset = -1;
   is_volatile = true;
   // Try to resolve field
-  Field* resolved_field =
-      cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
-                                        cUnit->class_loader, false);
+  Field* resolved_field = ComputeReferrerField(cUnit, field_idx);
   if (resolved_field != NULL) {
-    const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
-    Class* referrer_class =
-        cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
-                                         cUnit->dex_cache, cUnit->class_loader);
+    Class* referrer_class = ComputeReferrerClass(cUnit);
     // Try to resolve referring class then access check, failure to pass the
     Class* fields_class = resolved_field->GetDeclaringClass();
     if (referrer_class != NULL &&
@@ -292,14 +304,10 @@
   is_referrers_class = false;
   is_volatile = true;
   // Try to resolve field
-  Field* resolved_field =
-      cUnit->class_linker->ResolveField(*cUnit->dex_file, field_idx, cUnit->dex_cache,
-                                        cUnit->class_loader, true);
+  Field* resolved_field = ComputeReferrerField(cUnit, field_idx);
   if (resolved_field != NULL) {
-    const DexFile::MethodId& referrer_method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx);
-    Class* referrer_class =
-        cUnit->class_linker->ResolveType(*cUnit->dex_file, referrer_method_id.class_idx_,
-                                         cUnit->dex_cache, cUnit->class_loader);
+    DCHECK(resolved_field->IsStatic());
+    Class* referrer_class = ComputeReferrerClass(cUnit);
     if (referrer_class != NULL) {
       if (resolved_field->GetDeclaringClass() == referrer_class) {
         is_referrers_class = true;  // implies no worrying about class initialization
@@ -350,6 +358,54 @@
   return false;  // Incomplete knowledge needs slow path.
 }
 
+bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit,
+                                 bool is_interface, bool is_super,
+                                 int& vtable_idx) const {
+  vtable_idx = -1;
+  Method* resolved_method = ComputeReferrerMethod(cUnit, method_idx);
+  if (resolved_method != NULL) {
+    Class* referrer_class = ComputeReferrerClass(cUnit);
+    if (referrer_class != NULL) {
+      Class* methods_class = resolved_method->GetDeclaringClass();
+      if (!referrer_class->CanAccess(methods_class) ||
+          !referrer_class->CanAccessMember(methods_class,
+                                          resolved_method->GetAccessFlags())) {
+        // The referring class can't access the resolved method, this may occur as a result of a
+        // protected method being made public by implementing an interface that re-declares the
+        // method public. Resort to the dex file to determine the correct class for the access check
+        const DexFile& dex_file = cUnit->class_linker->FindDexFile(referrer_class->GetDexCache());
+        methods_class =
+            cUnit->class_linker->ResolveType(dex_file,
+                                             dex_file.GetMethodId(method_idx).class_idx_,
+                                             referrer_class);
+
+      }
+      if (referrer_class->CanAccess(methods_class) &&
+          referrer_class->CanAccessMember(methods_class,
+                                          resolved_method->GetAccessFlags())) {
+        vtable_idx = resolved_method->GetMethodIndex();
+        if (is_interface || is_super) {
+          // nothing left to do for virtual/interface dispatch
+          return true;
+        } else {
+          // ensure the vtable index will be correct to dispatch in the vtable of the super class
+          Class* super_class = methods_class->GetSuperClass();
+          if (super_class != NULL && vtable_idx <= super_class->GetVTable()->GetLength()) {
+            vtable_idx = resolved_method->GetMethodIndex();
+            return true;
+          }
+        }
+      }
+    }
+  }
+  // Clean up any exception left by method/type resolution
+  Thread* thread = Thread::Current();
+  if (thread->IsExceptionPending()) {
+      thread->ClearException();
+  }
+  return false;  // Incomplete knowledge needs slow path.
+}
+
 // Return true if the class should be skipped during compilation. We
 // never skip classes in the boot class loader. However, if we have a
 // non-boot class loader and we can resolve the class in the boot
diff --git a/src/compiler.h b/src/compiler.h
index d966b02..b55960b 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -109,6 +109,10 @@
                               int& field_offset, int& ssb_index,
                               bool& is_referrers_class, bool& is_volatile) const;
 
+  // Can we fastpath a interface, super class or virtual method call? Computes method's vtable index
+  bool ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, bool is_interface,
+                         bool is_super, int& vtable_idx) const;
+
  private:
 
   // Checks if class specified by type_idx is one of the image_classes_
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index e4073d4..eb2c06b 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -351,19 +351,16 @@
     }
 }
 
-typedef int (*NextCallInsn)(CompilationUnit*, MIR*, DecodedInstruction*, int,
-                            ArmLIR*);
+typedef int (*NextCallInsn)(CompilationUnit*, MIR*, int, uint32_t dexIdx,
+                            uint32_t methodIdx);
 
 /*
  * Bit of a hack here - in leiu of a real scheduling pass,
  * emit the next instruction in static & direct invoke sequences.
  */
 STATIC int nextSDCallInsn(CompilationUnit* cUnit, MIR* mir,
-                        DecodedInstruction* dInsn, int state,
-                        ArmLIR* rollback)
+                          int state, uint32_t dexIdx, uint32_t unused)
 {
-    DCHECK(rollback == NULL);
-    uint32_t idx = dInsn->vB;
     switch(state) {
         case 0:  // Get the current Method* [sets r0]
             loadCurrMethodDirect(cUnit, r0);
@@ -375,9 +372,9 @@
             break;
         case 2:  // Grab target method* and target code_
             loadWordDisp(cUnit, r0,
-                CodeAndDirectMethods::CodeOffsetInBytes(idx), rLR);
+                CodeAndDirectMethods::CodeOffsetInBytes(dexIdx), rLR);
             loadWordDisp(cUnit, r0,
-                CodeAndDirectMethods::MethodOffsetInBytes(idx), r0);
+                CodeAndDirectMethods::MethodOffsetInBytes(dexIdx), r0);
             break;
         default:
             return -1;
@@ -393,22 +390,13 @@
  * r1 here rather than the standard loadArgRegs.
  */
 STATIC int nextVCallInsn(CompilationUnit* cUnit, MIR* mir,
-                        DecodedInstruction* dInsn, int state,
-                        ArmLIR* rollback)
+                         int state, uint32_t dexIdx, uint32_t methodIdx)
 {
-    DCHECK(rollback == NULL);
     RegLocation rlArg;
     /*
      * This is the fast path in which the target virtual method is
      * fully resolved at compile time.
      */
-    Method* baseMethod = cUnit->class_linker->ResolveMethod(*cUnit->dex_file,
-                                                            dInsn->vB,
-                                                            cUnit->dex_cache,
-                                                            cUnit->class_loader,
-                                                            false);
-    CHECK(baseMethod != NULL);
-    uint32_t target_idx = baseMethod->GetMethodIndex();
     switch(state) {
         case 0:  // Get "this" [set r1]
             rlArg = oatGetSrc(cUnit, mir, 0);
@@ -423,7 +411,7 @@
             loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
             break;
         case 3: // Get target method [use rLR, set r0]
-            loadWordDisp(cUnit, rLR, (target_idx * 4) +
+            loadWordDisp(cUnit, rLR, (methodIdx * 4) +
                          Array::DataOffset().Int32Value(), r0);
             break;
         case 4: // Get the target compiled code address [uses r0, sets rLR]
@@ -435,157 +423,20 @@
     return state + 1;
 }
 
-STATIC int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
-                           DecodedInstruction* dInsn, int state,
-                           ArmLIR* rollback)
-{
-    RegLocation rlArg;
-    ArmLIR* skipBranch;
-    ArmLIR* skipTarget;
-    /*
-     * This handles the case in which the base method is not fully
-     * resolved at compile time.  We must generate code to test
-     * for resolution a run time, bail to the slow path if not to
-     * fill in all the tables.  In the latter case, we'll restart at
-     * at the beginning of the sequence.
-     */
-    switch(state) {
-        case 0:  // Get the current Method* [sets r0]
-            loadCurrMethodDirect(cUnit, r0);
-            break;
-        case 1: // Get method->dex_cache_resolved_methods_
-            loadWordDisp(cUnit, r0,
-                Method::GetDexCacheResolvedMethodsOffset().Int32Value(), rLR);
-            break;
-        case 2: // method->dex_cache_resolved_methods_->Get(method_idx)
-            loadWordDisp(cUnit, rLR, (dInsn->vB * 4) +
-                         Array::DataOffset().Int32Value(), rLR);
-            break;
-        case 3: // Resolved?
-            skipBranch = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
-            // Slowest path, bail to helper, rollback and retry
-            loadWordDisp(cUnit, rSELF,
-                         OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-            loadConstant(cUnit, r1, dInsn->vB);
-            loadConstant(cUnit, r2, false);
-            callRuntimeHelper(cUnit, rLR);
-            genUnconditionalBranch(cUnit, rollback);
-            // Resume normal slow path
-            skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-            skipTarget->defMask = ENCODE_ALL;
-            skipBranch->generic.target = (LIR*)skipTarget;
-            // Get base_method->method_index [usr rLR, set r0]
-            loadBaseDisp(cUnit, mir, rLR,
-                         Method::GetMethodIndexOffset().Int32Value(), r0,
-                         kUnsignedHalf, INVALID_SREG);
-            // Load "this" [set r1]
-            rlArg = oatGetSrc(cUnit, mir, 0);
-            loadValueDirectFixed(cUnit, rlArg, r1);
-            break;
-        case 4:
-            // Is "this" null? [use r1]
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
-            // get this->clazz [use r1, set rLR]
-            loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
-            break;
-        case 5:
-            // get this->klass_->vtable_ [usr rLR, set rLR]
-            loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
-            DCHECK_EQ((Array::DataOffset().Int32Value() & 0x3), 0);
-            // In load shadow fold vtable_ object header size into method_index_
-            opRegImm(cUnit, kOpAdd, r0,
-                     Array::DataOffset().Int32Value() / 4);
-            // Get target Method*
-            loadBaseIndexed(cUnit, rLR, r0, r0, 2, kWord);
-            break;
-        case 6: // Get the target compiled code address [uses r0, sets rLR]
-            loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
-            break;
-        default:
-            return -1;
-    }
-    return state + 1;
-}
-
-STATIC int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
-                          DecodedInstruction* dInsn, int callState,
-                          NextCallInsn nextCallInsn, ArmLIR* rollback,
-                          bool skipThis)
-{
-    int nextReg = r1;
-    int nextArg = 0;
-    if (skipThis) {
-        nextReg++;
-        nextArg++;
-    }
-    for (; (nextReg <= r3) && (nextArg < mir->ssaRep->numUses); nextReg++) {
-        RegLocation rlArg = oatGetRawSrc(cUnit, mir, nextArg++);
-        rlArg = oatUpdateRawLoc(cUnit, rlArg);
-        if (rlArg.wide && (nextReg <= r2)) {
-            loadValueDirectWideFixed(cUnit, rlArg, nextReg, nextReg + 1);
-            nextReg++;
-            nextArg++;
-        } else {
-            rlArg.wide = false;
-            loadValueDirectFixed(cUnit, rlArg, nextReg);
-        }
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
-    }
-    return callState;
-}
-
-// Interleave launch code for INVOKE_INTERFACE.
-STATIC int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
-                                 DecodedInstruction* dInsn, int state,
-                                 ArmLIR* rollback)
-{
-    DCHECK(rollback == NULL);
-    switch(state) {
-        case 0: // Load trampoline target
-            loadWordDisp(cUnit, rSELF,
-                         OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline),
-                         rLR);
-            // Load r0 with method index
-            loadConstant(cUnit, r0, dInsn->vB);
-            break;
-        default:
-            return -1;
-    }
-    return state + 1;
-}
-
 /*
  * Interleave launch code for INVOKE_SUPER.  See comments
  * for nextVCallIns.
  */
 STATIC int nextSuperCallInsn(CompilationUnit* cUnit, MIR* mir,
-                             DecodedInstruction* dInsn, int state,
-                             ArmLIR* rollback)
+                             int state, uint32_t dexIdx, uint32_t methodIdx)
 {
-    DCHECK(rollback == NULL);
-    RegLocation rlArg;
     /*
      * This is the fast path in which the target virtual method is
      * fully resolved at compile time.  Note also that this path assumes
      * that the check to verify that the target method index falls
      * within the size of the super's vtable has been done at compile-time.
      */
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Method* baseMethod = class_linker->ResolveMethod(*cUnit->dex_file,
-                                                     dInsn->vB,
-                                                     cUnit->dex_cache,
-                                                     cUnit->class_loader,
-                                                     false);
-    CHECK(baseMethod != NULL);
-    Class* declaring_class = cUnit->dex_cache->GetResolvedTypes()
-        ->Get(cUnit->dex_file->GetMethodId(cUnit->method_idx).class_idx_);
-    Class* superClass = (declaring_class != NULL)
-        ? declaring_class->GetSuperClass() : NULL;
-    CHECK(superClass != NULL);
-    int32_t target_idx = baseMethod->GetMethodIndex();
-    CHECK(superClass->GetVTable()->GetLength() > target_idx);
-    Method* targetMethod = superClass->GetVTable()->Get(target_idx);
-    CHECK(targetMethod != NULL);
+    RegLocation rlArg;
     switch(state) {
         case 0: // Get current Method* [set r0]
             loadCurrMethodDirect(cUnit, r0);
@@ -606,7 +457,7 @@
             loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
             break;
         case 3: // Get target method [use rLR, set r0]
-            loadWordDisp(cUnit, rLR, (target_idx * 4) +
+            loadWordDisp(cUnit, rLR, (methodIdx * 4) +
                          Array::DataOffset().Int32Value(), r0);
             break;
         case 4: // Get the target compiled code address [uses r0, sets rLR]
@@ -618,85 +469,95 @@
     return state + 1;
 }
 
-/* Slow-path version of nextSuperCallInsn */
-STATIC int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
-                               DecodedInstruction* dInsn, int state,
-                               ArmLIR* rollback)
+STATIC int nextInvokeInsnSP(CompilationUnit* cUnit, MIR* mir,
+                                  bool accessCheck, bool isInterface,
+                                  bool isSuper, int state, uint32_t dexIdx,
+                                  uint32_t methodIdx)
 {
-    RegLocation rlArg;
-    ArmLIR* skipBranch;
-    ArmLIR* skipTarget;
-    int tReg;
     /*
      * This handles the case in which the base method is not fully
-     * resolved at compile time.  We must generate code to test
-     * for resolution a run time, bail to the slow path if not to
-     * fill in all the tables.  In the latter case, we'll restart at
-     * at the beginning of the sequence.
+     * resolved at compile time, we bail to a runtime helper.
      */
-    switch(state) {
-        case 0:  // Get the current Method* [sets r0]
-            loadCurrMethodDirect(cUnit, r0);
-            break;
-        case 1: // Get method->dex_cache_resolved_methods_ [uses r0, set rLR]
-            loadWordDisp(cUnit, r0,
-                Method::GetDexCacheResolvedMethodsOffset().Int32Value(), rLR);
-            break;
-        case 2: // method->dex_cache_resolved_methods_->Get(meth_idx) [u/s rLR]
-            loadWordDisp(cUnit, rLR, (dInsn->vB * 4) +
-                         Array::DataOffset().Int32Value(), rLR);
-            break;
-        case 3: // Resolved?
-            skipBranch = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
-            // Slowest path, bail to helper, rollback and retry
-            loadWordDisp(cUnit, rSELF,
-                         OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-            loadConstant(cUnit, r1, dInsn->vB);
-            loadConstant(cUnit, r2, true);
-            callRuntimeHelper(cUnit, rLR);
-            genUnconditionalBranch(cUnit, rollback);
-            // Resume normal slow path
-            skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
-            skipTarget->defMask = ENCODE_ALL;
-            skipBranch->generic.target = (LIR*)skipTarget;
-            // Get base_method->method_index [usr rLR, set rLR]
-            loadBaseDisp(cUnit, mir, rLR,
-                         Method::GetMethodIndexOffset().Int32Value(), rLR,
-                         kUnsignedHalf, INVALID_SREG);
-            // Load "this" [set r1]
-            rlArg = oatGetSrc(cUnit, mir, 0);
-            loadValueDirectFixed(cUnit, rlArg, r1);
-            // Load curMethod->declaring_class_ [uses r0, sets r0]
-            loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
-                         r0);
-            // Null this?
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
-            // Get method->declaring_class_->super_class [usr r0, set r0]
-            loadWordDisp(cUnit, r0, Class::SuperClassOffset().Int32Value(), r0);
-            break;
-        case 4: // Get ...->super_class_->vtable [u/s r0]
-            loadWordDisp(cUnit, r0, Class::VTableOffset().Int32Value(), r0);
-            if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
-                // Range check, throw NSM on failure
-                tReg = oatAllocTemp(cUnit);
-                loadWordDisp(cUnit, r0, Array::LengthOffset().Int32Value(),
-                             tReg);
-                genRegRegCheck(cUnit, kArmCondCs, rLR, tReg, mir,
-                               kArmThrowNoSuchMethod);
-                oatFreeTemp(cUnit, tReg);
-            }
-            // Adjust vtable_ base past object header
-            opRegImm(cUnit, kOpAdd, r0, Array::DataOffset().Int32Value());
-            // Get target Method*
-            loadBaseIndexed(cUnit, r0, rLR, r0, 2, kWord);
-            break;
-        case 5: // Get the target compiled code address [uses r0, sets rLR]
-            loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
-            break;
-        default:
-            return -1;
+    if (state == 0) {
+        int trampoline;
+        if (!accessCheck) {
+          DCHECK(isInterface);
+          trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline);
+        } else {
+          if (isInterface) {
+            trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampolineWithAccessCheck);
+          } else if (isSuper) {
+            trampoline = OFFSETOF_MEMBER(Thread, pInvokeSuperTrampolineWithAccessCheck);
+          } else {
+            trampoline = OFFSETOF_MEMBER(Thread, pInvokeVirtualTrampolineWithAccessCheck);
+          }
+        }
+        // Load trampoline target
+        loadWordDisp(cUnit, rSELF, trampoline, rLR);
+        // Load r0 with method index
+        loadConstant(cUnit, r0, dexIdx);
+        return 1;
     }
-    return state + 1;
+    return -1;
+}
+
+STATIC int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
+                               int state, uint32_t dexIdx, uint32_t methodIdx)
+{
+  return nextInvokeInsnSP(cUnit, mir, true, false, true, state, dexIdx,
+                          methodIdx);
+}
+
+STATIC int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
+                           int state, uint32_t dexIdx, uint32_t methodIdx)
+{
+  return nextInvokeInsnSP(cUnit, mir, true, false, false, state, dexIdx,
+                          methodIdx);
+}
+
+/*
+ * All invoke-interface calls bounce off of art_invoke_interface_trampoline,
+ * which will locate the target and continue on via a tail call.
+ */
+STATIC int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
+                                 int state, uint32_t dexIdx, uint32_t unused)
+{
+  return nextInvokeInsnSP(cUnit, mir, false, true, false, state, dexIdx, 0);
+}
+
+STATIC int nextInterfaceCallInsnWithAccessCheck(CompilationUnit* cUnit,
+                                                MIR* mir, int state,
+                                                uint32_t dexIdx,
+                                                uint32_t unused)
+{
+  return nextInvokeInsnSP(cUnit, mir, true, true, false, state, dexIdx, 0);
+}
+
+STATIC int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
+                          DecodedInstruction* dInsn, int callState,
+                          NextCallInsn nextCallInsn, uint32_t dexIdx,
+                          uint32_t methodIdx, bool skipThis)
+{
+    int nextReg = r1;
+    int nextArg = 0;
+    if (skipThis) {
+        nextReg++;
+        nextArg++;
+    }
+    for (; (nextReg <= r3) && (nextArg < mir->ssaRep->numUses); nextReg++) {
+        RegLocation rlArg = oatGetRawSrc(cUnit, mir, nextArg++);
+        rlArg = oatUpdateRawLoc(cUnit, rlArg);
+        if (rlArg.wide && (nextReg <= r2)) {
+            loadValueDirectWideFixed(cUnit, rlArg, nextReg, nextReg + 1);
+            nextReg++;
+            nextArg++;
+        } else {
+            rlArg.wide = false;
+            loadValueDirectFixed(cUnit, rlArg, nextReg);
+        }
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
+    }
+    return callState;
 }
 
 /*
@@ -709,8 +570,8 @@
 STATIC int genDalvikArgsNoRange(CompilationUnit* cUnit, MIR* mir,
                                 DecodedInstruction* dInsn, int callState,
                                 ArmLIR** pcrLabel, bool isRange,
-                                NextCallInsn nextCallInsn, ArmLIR* rollback,
-                                bool skipThis)
+                                NextCallInsn nextCallInsn, uint32_t dexIdx,
+                                uint32_t methodIdx, bool skipThis)
 {
     RegLocation rlArg;
 
@@ -718,7 +579,7 @@
     if (dInsn->vA == 0)
         return callState;
 
-    callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+    callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
 
     DCHECK_LE(dInsn->vA, 5U);
     if (dInsn->vA > 3) {
@@ -739,12 +600,12 @@
                 reg = r3;
                 loadWordDisp(cUnit, rSP,
                              oatSRegOffset(cUnit, rlArg.sRegLow) + 4, reg);
-                callState = nextCallInsn(cUnit, mir, dInsn, callState,
-                                         rollback);
+                callState = nextCallInsn(cUnit, mir, callState, dexIdx,
+                                         methodIdx);
             }
             storeBaseDisp(cUnit, rSP, (nextUse + 1) * 4, reg, kWord);
             storeBaseDisp(cUnit, rSP, 16 /* (3+1)*4 */, reg, kWord);
-            callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+            callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
             nextUse++;
         }
         // Loop through the rest
@@ -764,8 +625,8 @@
                 } else {
                     loadValueDirectFixed(cUnit, rlArg, lowReg);
                 }
-                callState = nextCallInsn(cUnit, mir, dInsn, callState,
-                                         rollback);
+                callState = nextCallInsn(cUnit, mir, callState, dexIdx,
+                                         methodIdx);
             }
             int outsOffset = (nextUse + 1) * 4;
             if (rlArg.wide) {
@@ -775,12 +636,12 @@
                 storeWordDisp(cUnit, rSP, outsOffset, lowReg);
                 nextUse++;
             }
-            callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+            callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         }
     }
 
     callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
-                            rollback, skipThis);
+                            dexIdx, methodIdx, skipThis);
 
     if (pcrLabel) {
         *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
@@ -806,7 +667,8 @@
 STATIC int genDalvikArgsRange(CompilationUnit* cUnit, MIR* mir,
                               DecodedInstruction* dInsn, int callState,
                               ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
-                              ArmLIR* rollback, bool skipThis)
+                              uint32_t dexIdx, uint32_t methodIdx,
+                              bool skipThis)
 {
     int firstArg = dInsn->vC;
     int numArgs = dInsn->vA;
@@ -814,7 +676,8 @@
     // If we can treat it as non-range (Jumbo ops will use range form)
     if (numArgs <= 5)
         return genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pcrLabel,
-                                    true, nextCallInsn, rollback, skipThis);
+                                    true, nextCallInsn, dexIdx, methodIdx,
+                                    skipThis);
     /*
      * Make sure range list doesn't span the break between in normal
      * Dalvik vRegs and the ins.
@@ -867,25 +730,25 @@
     } else {
         // Use vldm/vstm pair using r3 as a temp
         int regsLeft = std::min(numArgs - 3, 16);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         opRegRegImm(cUnit, kOpAdd, r3, rSP, startOffset);
         ArmLIR* ld = newLIR3(cUnit, kThumb2Vldms, r3, fr0, regsLeft);
         //TUNING: loosen barrier
         ld->defMask = ENCODE_ALL;
         setMemRefType(ld, true /* isLoad */, kDalvikReg);
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         opRegRegImm(cUnit, kOpAdd, r3, rSP, 4 /* Method* */ + (3 * 4));
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
         ArmLIR* st = newLIR3(cUnit, kThumb2Vstms, r3, fr0, regsLeft);
         setMemRefType(st, false /* isLoad */, kDalvikReg);
         st->defMask = ENCODE_ALL;
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
     }
 
     callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
-                            rollback, skipThis);
+                            dexIdx, methodIdx, skipThis);
 
-    callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+    callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
     if (pcrLabel) {
         *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
     }
@@ -916,10 +779,10 @@
     // Explicit register usage
     oatLockCallTemps(cUnit);
 
+    uint32_t dexMethodIdx = mir->dalvikInsn.vB;
     // Is this the special "Ljava/lang/Object;.<init>:()V" case?
-    if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT) {
-        int idx = mir->dalvikInsn.vB;
-        Method* target = cUnit->dex_cache->GetResolvedMethods()->Get(idx);
+    if (direct && !range) {
+        Method* target = cUnit->dex_cache->GetResolvedMethods()->Get(dexMethodIdx);
         if (target) {
             if (PrettyMethod(target) == "java.lang.Object.<init>()V") {
                 RegLocation rlArg = oatGetSrc(cUnit, mir, 0);
@@ -936,14 +799,15 @@
 
     if (range) {
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, pNullCk,
-                                       nextCallInsn, NULL, false);
+                                       nextCallInsn, dexMethodIdx, 0, false);
     } else {
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pNullCk,
-                                         false, nextCallInsn, NULL, false);
+                                         false, nextCallInsn, dexMethodIdx,
+                                         0, false);
     }
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, NULL);
+        callState = nextCallInsn(cUnit, mir, callState, dexMethodIdx, 0);
     }
     if (DISPLAY_MISSING_TARGETS) {
         genShowTarget(cUnit);
@@ -952,164 +816,44 @@
     oatClobberCalleeSave(cUnit);
 }
 
-/*
- * All invoke-interface calls bounce off of art_invoke_interface_trampoline,
- * which will locate the target and continue on via a tail call.
- */
-STATIC void genInvokeInterface(CompilationUnit* cUnit, MIR* mir)
+STATIC void genInvoke(CompilationUnit* cUnit, MIR* mir, bool isInterface,
+                      bool isSuper, bool isRange)
 {
     DecodedInstruction* dInsn = &mir->dalvikInsn;
     int callState = 0;
-    ArmLIR* nullCk;
-    oatFlushAllRegs(cUnit);    /* Everything to home location */
-
-    // Explicit register usage
-    oatLockCallTemps(cUnit);
-    /* Note: must call nextInterfaceCallInsn() prior to 1st argument load */
-    callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
-    if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
-        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk,
-                                         false, nextInterfaceCallInsn, NULL,
-                                         false);
-    else
-        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk,
-                                       nextInterfaceCallInsn, NULL, false);
-    // Finish up any of the call sequence not interleaved in arg loading
-    while (callState >= 0) {
-        callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
-    }
-    if (DISPLAY_MISSING_TARGETS) {
-        genShowTarget(cUnit);
-    }
-    opReg(cUnit, kOpBlx, rLR);
-    oatClobberCalleeSave(cUnit);
-}
-
-STATIC void genInvokeSuper(CompilationUnit* cUnit, MIR* mir)
-{
-    DecodedInstruction* dInsn = &mir->dalvikInsn;
-    int callState = 0;
-    ArmLIR* rollback;
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Method* baseMethod = class_linker->ResolveMethod(*cUnit->dex_file,
-                                                     dInsn->vB,
-                                                     cUnit->dex_cache,
-                                                     cUnit->class_loader,
-                                                     false);
-    NextCallInsn nextCallInsn;
-    bool fastPath = true;
-    oatFlushAllRegs(cUnit);    /* Everything to home location */
-
-    // Explicit register usage
-    oatLockCallTemps(cUnit);
-
-    // For testing, force call to artResolveMethodFromCode & ignore result
-    if (EXERCISE_RESOLVE_METHOD) {
-        loadCurrMethodDirect(cUnit, r0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-        loadConstant(cUnit, r1, dInsn->vB);
-        loadConstant(cUnit, r2, true);
-        callRuntimeHelper(cUnit, rLR);
-    }
-
-    if (SLOW_INVOKE_PATH || baseMethod == NULL) {
-        Thread* thread = Thread::Current();
-        if (thread->IsExceptionPending()) {  // clear any exception left by resolve method
-            thread->ClearException();
-        }
-        fastPath = false;
-    } else {
-        Class* declaring_class = cUnit->dex_cache->GetResolvedTypes()
-            ->Get(cUnit->dex_file->GetMethodId(cUnit->method_idx).class_idx_);
-        Class* superClass = (declaring_class != NULL)
-            ? declaring_class->GetSuperClass() : NULL;
-        if (superClass == NULL) {
-            fastPath = false;
-        } else {
-            int32_t target_idx = baseMethod->GetMethodIndex();
-            if (superClass->GetVTable()->GetLength() <= target_idx) {
-                fastPath = false;
-            } else {
-                fastPath = (superClass->GetVTable()->Get(target_idx) != NULL);
-            }
-        }
-    }
-    if (fastPath) {
-        nextCallInsn = nextSuperCallInsn;
-        rollback = NULL;
-    } else {
-        nextCallInsn = nextSuperCallInsnSP;
-        rollback = newLIR0(cUnit, kArmPseudoTargetLabel);
-        rollback->defMask = -1;
-    }
-    if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
-        callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, NULL,
-                                         false, nextCallInsn, rollback, true);
-    else
-        callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, NULL,
-                                       nextCallInsn, rollback, true);
-    // Finish up any of the call sequence not interleaved in arg loading
-    while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
-    }
-    if (DISPLAY_MISSING_TARGETS) {
-        genShowTarget(cUnit);
-    }
-    opReg(cUnit, kOpBlx, rLR);
-    oatClobberCalleeSave(cUnit);
-}
-
-STATIC void genInvokeVirtual(CompilationUnit* cUnit, MIR* mir)
-{
-    DecodedInstruction* dInsn = &mir->dalvikInsn;
-    int callState = 0;
-    ArmLIR* rollback;
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Method* method = class_linker->ResolveMethod(*cUnit->dex_file,
-                                                 dInsn->vB,
-                                                 cUnit->dex_cache,
-                                                 cUnit->class_loader,
-                                                 false);
     NextCallInsn nextCallInsn;
     oatFlushAllRegs(cUnit);    /* Everything to home location */
     // Explicit register usage
     oatLockCallTemps(cUnit);
 
-    // For testing, force call to artResolveMethodFromCode & ignore result
-    if (EXERCISE_RESOLVE_METHOD) {
-        loadCurrMethodDirect(cUnit, r0);
-        loadWordDisp(cUnit, rSELF,
-                     OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR);
-        loadConstant(cUnit, r1, dInsn->vB);
-        loadConstant(cUnit, r2, false);
-        callRuntimeHelper(cUnit, rLR);
-    }
-
-    if (SLOW_INVOKE_PATH || method == NULL) {
-        Thread* thread = Thread::Current();
-        if (thread->IsExceptionPending()) {  // clear any exception left by resolve method
-            thread->ClearException();
-        }
-        // Slow path
-        nextCallInsn = nextVCallInsnSP;
-        // If we need a slow-path callout, we'll restart here
-        rollback = newLIR0(cUnit, kArmPseudoTargetLabel);
-        rollback->defMask = -1;
+    uint32_t dexMethodIdx = dInsn->vB;
+    int vtableIdx;
+    bool fastPath =
+        cUnit->compiler->ComputeInvokeInfo(dexMethodIdx, cUnit,
+                                           isInterface, isSuper, vtableIdx)
+        && !SLOW_INVOKE_PATH;
+    if (isInterface) {
+      nextCallInsn = fastPath ? nextInterfaceCallInsn
+                              : nextInterfaceCallInsnWithAccessCheck;
+    } else if (isSuper) {
+      nextCallInsn = fastPath ? nextSuperCallInsn : nextSuperCallInsnSP;
     } else {
-        // Fast path
-        nextCallInsn = nextVCallInsn;
-        rollback = NULL;
+      nextCallInsn = fastPath ? nextVCallInsn : nextVCallInsnSP;
     }
-    if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
+    bool skipThis = fastPath && !isInterface;
+    if (!isRange) {
         callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, NULL,
-                                         false, nextCallInsn, rollback, true);
-    else
+                                         false, nextCallInsn, dexMethodIdx,
+                                         vtableIdx, skipThis);
+    } else {
         callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, NULL,
-                                       nextCallInsn, rollback, true);
+                                       nextCallInsn, dexMethodIdx, vtableIdx,
+                                       skipThis);
+    }
     // Finish up any of the call sequence not interleaved in arg loading
     while (callState >= 0) {
-        callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
+        callState = nextCallInsn(cUnit, mir, callState, dexMethodIdx,
+                                 vtableIdx);
     }
     if (DISPLAY_MISSING_TARGETS) {
         genShowTarget(cUnit);
@@ -1586,18 +1330,30 @@
             break;
 
         case OP_INVOKE_VIRTUAL:
+            genInvoke(cUnit, mir, false /*interface*/, false /*super*/,
+                      false /*range*/);
+            break;
         case OP_INVOKE_VIRTUAL_RANGE:
-            genInvokeVirtual(cUnit, mir);
+            genInvoke(cUnit, mir, false /*interface*/, false /*super*/,
+                      true /*range*/);
             break;
 
         case OP_INVOKE_SUPER:
+            genInvoke(cUnit, mir, false /*interface*/, true /*super*/,
+                      false /*range*/);
+            break;
         case OP_INVOKE_SUPER_RANGE:
-            genInvokeSuper(cUnit, mir);
+            genInvoke(cUnit, mir, false /*interface*/, true /*super*/,
+                      true /*range*/);
             break;
 
         case OP_INVOKE_INTERFACE:
+            genInvoke(cUnit, mir, true /*interface*/, false /*super*/,
+                      false /*range*/);
+            break;
         case OP_INVOKE_INTERFACE_RANGE:
-            genInvokeInterface(cUnit, mir);
+            genInvoke(cUnit, mir, true /*interface*/, false /*super*/,
+                      true /*range*/);
             break;
 
         case OP_NEG_INT:
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 3e5ebd7..dfaaf01 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -3172,7 +3172,7 @@
   if (is_super) {
     DCHECK(method_type == METHOD_VIRTUAL);
     Class* super = method_->GetDeclaringClass()->GetSuperClass();
-    if (super == NULL || res_method->GetMethodIndex() > super->GetVTable()->GetLength()) {
+    if (super == NULL || res_method->GetMethodIndex() >= super->GetVTable()->GetLength()) {
       if (super == NULL) {  // Only Object has no super class
         Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_)
                                      << " to super " << PrettyMethod(res_method);
diff --git a/src/object.cc b/src/object.cc
index 6be1136..bb87698 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -909,7 +909,7 @@
   SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
 }
 
-Method* Class::FindVirtualMethodForInterface(Method* method, bool can_throw) {
+Method* Class::FindVirtualMethodForInterface(Method* method) {
   Class* declaring_class = method->GetDeclaringClass();
   DCHECK(declaring_class != NULL) << PrettyClass(this);
   DCHECK(declaring_class->IsInterface()) << PrettyMethod(method);
@@ -922,11 +922,6 @@
       return interface_entry->GetMethodArray()->Get(method->GetMethodIndex());
     }
   }
-  if (can_throw) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
-        "Class %s does not implement interface %s",
-        PrettyDescriptor(this).c_str(), PrettyDescriptor(declaring_class).c_str());
-  }
   return NULL;
 }
 
@@ -1346,7 +1341,7 @@
 std::string Throwable::Dump() const {
   std::string result(PrettyTypeOf(this));
   result += ": ";
-  String* msg = GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false);
+  String* msg = GetDetailMessage();
   if (msg != NULL) {
     result += msg->ToModifiedUtf8();
   }
diff --git a/src/object.h b/src/object.h
index 19ca325..738d2b0 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1015,7 +1015,7 @@
 
 template<class T>
 T* ObjectArray<T>::Get(int32_t i) const {
-  if (!IsValidIndex(i)) {
+  if (UNLIKELY(!IsValidIndex(i))) {
     return NULL;
   }
   MemberOffset data_offset(DataOffset().Int32Value() + i * sizeof(Object*));
@@ -1523,7 +1523,7 @@
   // Given a method implemented by this class, but potentially from a
   // super class or interface, return the specific implementation
   // method for this class.
-  Method* FindVirtualMethodForInterface(Method* method, bool can_throw);
+  Method* FindVirtualMethodForInterface(Method* method);
 
   Method* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const;
 
@@ -1532,7 +1532,7 @@
       return method;
     }
     if (method->GetDeclaringClass()->IsInterface()) {
-      return FindVirtualMethodForInterface(method, true);
+      return FindVirtualMethodForInterface(method);
     }
     return FindVirtualMethodForVirtual(method);
   }
@@ -1941,10 +1941,10 @@
 
 template<class T>
 void ObjectArray<T>::Set(int32_t i, T* object) {
-  if (IsValidIndex(i)) {
+  if (LIKELY(IsValidIndex(i))) {
     if (object != NULL) {
       Class* element_class = GetClass()->GetComponentType();
-      if (!object->InstanceOf(element_class)) {
+      if (UNLIKELY(!object->InstanceOf(element_class))) {
         ThrowArrayStoreException(object);
         return;
       }
@@ -2261,6 +2261,9 @@
     SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_),
                    new_detail_message, false);
   }
+  String* GetDetailMessage() const {
+    return GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false);
+  }
   std::string Dump() const;
 
   // This is a runtime version of initCause, you shouldn't use it if initCause may have been
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 7c47a8f..1ac92fd 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -34,6 +34,71 @@
   self->SetTopOfStack(sp, 0);
 }
 
+static void ThrowNewIllegalAccessErrorClass(Thread* self, Class* referrer, Class* accessed) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "illegal class access: '%s' -> '%s'",
+                           PrettyDescriptor(referrer).c_str(),
+                           PrettyDescriptor(accessed).c_str());
+}
+
+static void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self, Class* referrer,
+                                                             Class* accessed, const Method* caller,
+                                                             const Method* called,
+                                                             bool is_interface, bool is_super) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "illegal class access ('%s' -> '%s')"
+                           "in attempt to invoke %s method '%s' from '%s'",
+                           PrettyDescriptor(referrer).c_str(),
+                           PrettyDescriptor(accessed).c_str(),
+                           (is_interface ? "interface" : (is_super ? "super class" : "virtual")),
+                           PrettyMethod(called).c_str(),
+                           PrettyMethod(caller).c_str());
+}
+
+static void ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
+                                                                          const Method* referrer,
+                                                                          const Method* interface_method,
+                                                                          Object* this_object) {
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+      "class '%s' does not implement interface '%s' in call to '%s' from '%s'",
+      PrettyDescriptor(this_object->GetClass()).c_str(),
+      PrettyDescriptor(interface_method->GetDeclaringClass()).c_str(),
+      PrettyMethod(interface_method).c_str(), PrettyMethod(referrer).c_str());
+}
+
+static void ThrowNewIllegalAccessErrorField(Thread* self, Class* referrer, Field* accessed) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "Field '%s' is inaccessible to class '%s'",
+                           PrettyField(accessed, false).c_str(),
+                           PrettyDescriptor(referrer).c_str());
+}
+
+static void ThrowNewIllegalAccessErrorMethod(Thread* self, Class* referrer, Method* accessed) {
+  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+                           "Method '%s' is inaccessible to class '%s'",
+                           PrettyMethod(accessed).c_str(),
+                           PrettyDescriptor(referrer).c_str());
+}
+
+static void ThrowNullPointerExceptionForFieldAccess(Thread* self, Field* field, bool is_read) {
+  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+                           "Attempt to %s field '%s' on a null object reference",
+                           is_read ? "read from" : "write to",
+                           PrettyField(field, true).c_str());
+}
+
+static void ThrowNullPointerExceptionForMethodAccess(Thread* self, Method* caller,
+                                                     uint32_t method_idx, bool is_interface,
+                                                     bool is_super) {
+  const DexFile& dex_file =
+      Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
+  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+                           "Attempt to invoke %s method '%s' from '%s' on a null object reference",
+                           (is_interface ? "interface" : (is_super ? "super class" : "virtual")),
+                           PrettyMethod(method_idx, dex_file, true).c_str(),
+                           PrettyMethod(caller).c_str());
+}
+
 /*
  * Report location to debugger.  Note: dalvikPC is the current offset within
  * the method.  However, because the offset alone cannot distinguish between
@@ -482,6 +547,7 @@
   return resolved_field;
 }
 
+
 // Slow path field resolution and declaring class initialization
 Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
                          bool is_static, bool is_primitive, size_t expected_size) {
@@ -491,14 +557,10 @@
     Class* fields_class = resolved_field->GetDeclaringClass();
     Class* referring_class = referrer->GetDeclaringClass();
     if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;","%s tried to access class %s",
-                               PrettyMethod(referrer).c_str(),
-                               PrettyDescriptor(fields_class).c_str());
+      ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
     } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
                                                           resolved_field->GetAccessFlags()))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;","%s tried to access field %s",
-                               PrettyMethod(referrer).c_str(),
-                               PrettyField(resolved_field, false).c_str());
+      ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
       return NULL;  // failure
     }
     FieldHelper fh(resolved_field);
@@ -527,13 +589,6 @@
   return NULL;
 }
 
-static void ThrowNullPointerExceptionForFieldAccess(Thread* self, Field* field, bool is_read) {
-  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-                           "Attempt to %s field '%s' of a null object",
-                           is_read ? "read from" : "write to",
-                           PrettyField(field, true).c_str());
-}
-
 extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, const Method* referrer,
                                            Thread* self, Method** sp) {
   Field* field = FindFieldFast(field_idx, referrer, true, sizeof(int32_t));
@@ -766,10 +821,7 @@
     }
     Class* referrer = method->GetDeclaringClass();
     if (UNLIKELY(!referrer->CanAccess(klass))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                               "illegal class access: '%s' -> '%s'",
-                               PrettyDescriptor(referrer).c_str(),
-                               PrettyDescriptor(klass).c_str());
+      ThrowNewIllegalAccessErrorClass(self, referrer, klass);
       return NULL;  // Failure
     }
   }
@@ -815,10 +867,7 @@
   if (access_check) {
     Class* referrer = method->GetDeclaringClass();
     if (UNLIKELY(!referrer->CanAccess(klass))) {
-      self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                               "illegal class access: '%s' -> '%s'",
-                               PrettyDescriptor(referrer).c_str(),
-                               PrettyDescriptor(klass).c_str());
+      ThrowNewIllegalAccessErrorClass(self, referrer, klass);
       return NULL;  // Failure
     }
   }
@@ -868,10 +917,7 @@
     if (access_check) {
       Class* referrer = method->GetDeclaringClass();
       if (UNLIKELY(!referrer->CanAccess(klass))) {
-        self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                                 "illegal class access: '%s' -> '%s'",
-                                 PrettyDescriptor(referrer).c_str(),
-                                 PrettyDescriptor(klass).c_str());
+        ThrowNewIllegalAccessErrorClass(self, referrer, klass);
         return NULL;  // Failure
       }
     }
@@ -945,11 +991,9 @@
     return NULL;  // Failure - Indicate to caller to deliver exception
   }
   // Perform access check if necessary.
-  if (verify_access && !referrer->GetDeclaringClass()->CanAccess(klass)) {
-    self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
-                             "Class %s is inaccessible to method %s",
-                             PrettyDescriptor(klass).c_str(),
-                             PrettyMethod(referrer, true).c_str());
+  Class* referring_class = referrer->GetDeclaringClass();
+  if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
+    ThrowNewIllegalAccessErrorClass(self, referring_class, klass);
     return NULL;  // Failure - Indicate to caller to deliver exception
   }
   // If we're just implementing const-class, we shouldn't call <clinit>.
@@ -960,7 +1004,7 @@
   //
   // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
   // running.
-  if (klass == referrer->GetDeclaringClass() && MethodHelper(referrer).IsClassInitializer()) {
+  if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
     return klass;
   }
   if (!class_linker->EnsureInitialized(klass, true)) {
@@ -1086,46 +1130,191 @@
   return 0;  // Success
 }
 
-// See comments in runtime_support_asm.S
-extern "C" uint64_t artFindInterfaceMethodInCacheFromCode(uint32_t method_idx,
-                                                          Object* this_object,
-                                                          Method* caller_method,
-                                                          Thread* thread, Method** sp) {
-  Method* interface_method = caller_method->GetDexCacheResolvedMethods()->Get(method_idx);
-  Method* found_method = NULL;  // The found method
-  if (LIKELY(interface_method != NULL && this_object != NULL)) {
-    found_method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method, false);
+// Fast path method resolution that can't throw exceptions
+static Method* FindMethodFast(uint32_t method_idx, Object* this_object, const Method* referrer,
+                              bool access_check, bool is_interface, bool is_super) {
+  if (UNLIKELY(this_object == NULL)) {
+    return NULL;
   }
-  if (UNLIKELY(found_method == NULL)) {
-    FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs);
-    if (this_object == NULL) {
-      thread->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-          "null receiver during interface dispatch");
-      return 0;
+  Method* resolved_method =
+      referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
+  if (UNLIKELY(resolved_method == NULL)) {
+    return NULL;
+  }
+  if (access_check) {
+    Class* methods_class = resolved_method->GetDeclaringClass();
+    Class* referring_class = referrer->GetDeclaringClass();
+    if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+                 !referring_class->CanAccessMember(methods_class,
+                                                   resolved_method->GetAccessFlags()))) {
+      // potential illegal access
+      return NULL;
     }
-    if (interface_method == NULL) {
-      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      interface_method = class_linker->ResolveMethod(method_idx, caller_method, false);
-      if (interface_method == NULL) {
-        // Could not resolve interface method. Throw error and unwind
-        CHECK(thread->IsExceptionPending());
-        return 0;
+  }
+  if (is_interface) {
+    return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+  } else if (is_super) {
+    return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+  } else {
+    return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+  }
+}
+
+// Slow path method resolution
+static Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
+                                  Thread* self, bool access_check, bool is_interface,
+                                  bool is_super) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, false);
+  if (LIKELY(resolved_method != NULL)) {
+    if (!access_check) {
+      if (is_interface) {
+        Method* interface_method =
+            this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+        if (UNLIKELY(interface_method == NULL)) {
+          ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
+                                                                        resolved_method,
+                                                                        this_object);
+          return NULL;
+        } else {
+          return interface_method;
+        }
+      } else {
+        ObjectArray<Method>* vtable;
+        uint16_t vtable_index = resolved_method->GetMethodIndex();
+        if (is_super) {
+          vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
+        } else {
+          vtable = this_object->GetClass()->GetVTable();
+        }
+        // TODO: eliminate bounds check?
+        return vtable->Get(vtable_index);
+      }
+    } else {
+      Class* methods_class = resolved_method->GetDeclaringClass();
+      Class* referring_class = referrer->GetDeclaringClass();
+      if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+                   !referring_class->CanAccessMember(methods_class,
+                                                     resolved_method->GetAccessFlags()))) {
+        // The referring class can't access the resolved method, this may occur as a result of a
+        // protected method being made public by implementing an interface that re-declares the
+        // method public. Resort to the dex file to determine the correct class for the access check
+        const DexFile& dex_file = class_linker->FindDexFile(referring_class->GetDexCache());
+        methods_class = class_linker->ResolveType(dex_file,
+                                                  dex_file.GetMethodId(method_idx).class_idx_,
+                                                  referring_class);
+        if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
+          ThrowNewIllegalAccessErrorClassForMethodDispatch(self, referring_class, methods_class,
+                                                           referrer, resolved_method, is_interface,
+                                                           is_super);
+          return NULL;  // failure
+        } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
+                                                              resolved_method->GetAccessFlags()))) {
+          ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
+          return NULL;  // failure
+        }
+      }
+      if (is_interface) {
+        Method* interface_method =
+            this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+        if (UNLIKELY(interface_method == NULL)) {
+          ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
+                                                                        resolved_method,
+                                                                        this_object);
+          return NULL;
+        } else {
+          return interface_method;
+        }
+      } else {
+        ObjectArray<Method>* vtable;
+        uint16_t vtable_index = resolved_method->GetMethodIndex();
+        if (is_super) {
+          Class* super_class = referring_class->GetSuperClass();
+          if (LIKELY(super_class != NULL)) {
+            vtable = referring_class->GetSuperClass()->GetVTable();
+          } else {
+            vtable = NULL;
+          }
+        } else {
+          vtable = this_object->GetClass()->GetVTable();
+        }
+        if (LIKELY(vtable != NULL &&
+                   vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
+          return vtable->GetWithoutChecks(vtable_index);
+        } else {
+          // Behavior to agree with that of the verifier
+          self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
+                                   "attempt to invoke %s method '%s' from '%s'"
+                                   " using incorrect form of method dispatch",
+                                   (is_super ? "super class" : "virtual"),
+                                   PrettyMethod(resolved_method).c_str(),
+                                   PrettyMethod(referrer).c_str());
+          return NULL;
+        }
       }
     }
-    found_method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method, true);
-    if (found_method == NULL) {
-      CHECK(thread->IsExceptionPending());
-      return 0;
+  }
+  DCHECK(self->IsExceptionPending());  // Throw exception and unwind
+  return NULL;
+}
+
+static uint64_t artInvokeCommon(uint32_t method_idx, Object* this_object, Method* caller_method,
+                                Thread* self, Method** sp, bool access_check, bool is_interface,
+                                bool is_super){
+  Method* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
+                                  is_interface, is_super);
+  if (UNLIKELY(method == NULL)) {
+    FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+    if (UNLIKELY(this_object == NULL)) {
+      ThrowNullPointerExceptionForMethodAccess(self, caller_method, method_idx, is_interface,
+                                               is_super);
+      return 0;  // failure
+    }
+    method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check,
+                                is_interface, is_super);
+    if (UNLIKELY(method == NULL)) {
+      CHECK(self->IsExceptionPending());
+      return 0;  // failure
     }
   }
-  const void* code = found_method->GetCode();
+  // TODO: DCHECK
+  CHECK(!self->IsExceptionPending());
+  const void* code = method->GetCode();
 
-  uint32_t method_uint = reinterpret_cast<uint32_t>(found_method);
+  uint32_t method_uint = reinterpret_cast<uint32_t>(method);
   uint64_t code_uint = reinterpret_cast<uint32_t>(code);
   uint64_t result = ((code_uint << 32) | method_uint);
   return result;
 }
 
+// See comments in runtime_support_asm.S
+extern "C" uint64_t artInvokeInterfaceTrampoline(uint32_t method_idx, Object* this_object,
+                                                 Method* caller_method, Thread* self,
+                                                 Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, false, true, false);
+}
+
+extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
+                                                                Object* this_object,
+                                                                Method* caller_method, Thread* self,
+                                                                Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, true, false);
+}
+
+extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
+                                                            Object* this_object,
+                                                            Method* caller_method, Thread* self,
+                                                            Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, false, true);
+}
+
+extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
+                                                              Object* this_object,
+                                                              Method* caller_method, Thread* self,
+                                                              Method** sp) {
+  return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, false, false);
+}
+
 static void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {
   ScopedLocalRef<jclass> jlr_UTE_class(env,
       env->FindClass("java/lang/reflect/UndeclaredThrowableException"));
diff --git a/src/runtime_support.h b/src/runtime_support.h
index b9c03e4..c8db48c 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -70,6 +70,9 @@
   extern "C" void art_do_long_jump(uint32_t*, uint32_t*);
   extern "C" void art_handle_fill_data_from_code(void*, void*);
   extern "C" void art_invoke_interface_trampoline(uint32_t, void*);
+  extern "C" void art_invoke_interface_trampoline_with_access_check(uint32_t, void*);
+  extern "C" void art_invoke_super_trampoline_with_access_check(uint32_t, void*);
+  extern "C" void art_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
   extern "C" void art_lock_object_from_code(void*);
   extern "C" void art_object_init_from_code(void*);
   extern "C" void art_test_suspend();
diff --git a/src/runtime_support_arm.S b/src/runtime_support_arm.S
index c45583a..8113b5b 100644
--- a/src/runtime_support_arm.S
+++ b/src/runtime_support_arm.S
@@ -171,7 +171,7 @@
     b   artThrowVerificationErrorFromCode @ artThrowVerificationErrorFromCode(kind, ref, Thread*, SP)
 
     .global art_invoke_interface_trampoline
-    .extern artFindInterfaceMethodInCacheFromCode
+    .extern artInvokeInterfaceTrampoline
     /*
      * All generated callsites for interface invokes will load arguments as usual - except instead
      * of loading arg0/r0 with the target Method*, arg0/r0 will contain the method_idx.  This
@@ -179,10 +179,10 @@
      * artFindInterfaceMethodInCacheFromCode(idx, this, method);
      * NOTE: "this" is first visable argument of the target, and so can be found in arg1/r1.
      *
-     * artFindInterfaceMethodInCacheFromCode will attempt to locate the target and return a 64-bit
+     * artInvokeInterfaceTrampoline will attempt to locate the target and return a 64-bit
      * result in r0/r1 consisting of the target Method* in r0 and method->code_ in r1.
      *
-     * If unsuccessful, artFindInterfaceMethodInCacheFromCode will return NULL/NULL. There will be
+     * If unsuccessful, artInvokeInterfaceTrampoline will return NULL/NULL. There will be
      * a pending exception in the thread and we branch to another stub to deliver it.
      *
      * On success this wrapper will restore arguments and *jump* to the target, leaving the lr
@@ -193,7 +193,49 @@
     ldr    r2, [sp, #48]                          @ pass caller Method*
     mov    r3, r9                                 @ pass Thread::Current
     str    sp, [sp, #0]                           @ pass SP
-    bl     artFindInterfaceMethodInCacheFromCode  @ (method_idx, this, caller, Thread*, SP)
+    bl     artInvokeInterfaceTrampoline           @ (method_idx, this, caller, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_invoke_interface_trampoline_with_access_check
+    .extern artInvokeInterfaceTrampolineWithAccessCheck
+art_invoke_interface_trampoline_with_access_check:
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #48]                          @ pass caller Method*
+    mov    r3, r9                                 @ pass Thread::Current
+    str    sp, [sp, #0]                           @ pass SP
+    bl     artInvokeInterfaceTrampolineWithAccessCheck @ (method_idx, this, caller, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_invoke_super_trampoline_with_access_check
+    .extern artInvokeSuperTrampolineWithAccessCheck
+art_invoke_super_trampoline_with_access_check:
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #48]                          @ pass caller Method*
+    mov    r3, r9                                 @ pass Thread::Current
+    str    sp, [sp, #0]                           @ pass SP
+    bl     artInvokeSuperTrampolineWithAccessCheck @ (method_idx, this, caller, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    DELIVER_PENDING_EXCEPTION
+
+    .global art_invoke_virtual_trampoline_with_access_check
+    .extern artInvokeVirtualTrampolineWithAccessCheck
+art_invoke_virtual_trampoline_with_access_check:
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    ldr    r2, [sp, #48]                          @ pass caller Method*
+    mov    r3, r9                                 @ pass Thread::Current
+    str    sp, [sp, #0]                           @ pass SP
+    bl     artInvokeVirtualTrampolineWithAccessCheck @ (method_idx, this, caller, Thread*, SP)
     mov    r12, r1                                @ save r0->code_
     RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
     cmp    r0, #0                                 @ did we find the target?
diff --git a/src/thread.cc b/src/thread.cc
index 582832d..8592bd3 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -116,6 +116,9 @@
   pInitializeTypeFromCode = art_initialize_type_from_code;
   pInitializeTypeAndVerifyAccessFromCode = art_initialize_type_and_verify_access_from_code;
   pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
+  pInvokeInterfaceTrampolineWithAccessCheck = art_invoke_interface_trampoline_with_access_check;
+  pInvokeSuperTrampolineWithAccessCheck = art_invoke_super_trampoline_with_access_check;
+  pInvokeVirtualTrampolineWithAccessCheck = art_invoke_virtual_trampoline_with_access_check;
   pLockObjectFromCode = art_lock_object_from_code;
   pObjectInit = art_object_init_from_code;
   pResolveStringFromCode = art_resolve_string_from_code;
@@ -1451,7 +1454,10 @@
   // resolution.
   ClearException();
   if (kDebugExceptionDelivery) {
-    DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << std::endl);
+    String* msg = exception->GetDetailMessage();
+    std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : "");
+    DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
+                        << ": " << str_msg << std::endl);
   }
 
   Context* long_jump_context = GetLongJumpContext();
diff --git a/src/thread.h b/src/thread.h
index b1b8a1e..b21af96 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -89,85 +89,6 @@
 
   static const size_t kDefaultStackSize = 96 * KB;
 
-  // Runtime support function pointers
-  void (*pDebugMe)(Method*, uint32_t);
-  void* (*pMemcpy)(void*, const void*, size_t);
-  uint64_t (*pShlLong)(uint64_t, uint32_t);
-  uint64_t (*pShrLong)(uint64_t, uint32_t);
-  uint64_t (*pUshrLong)(uint64_t, uint32_t);
-  float (*pI2f)(int);
-  int (*pF2iz)(float);
-  float (*pD2f)(double);
-  double (*pF2d)(float);
-  double (*pI2d)(int);
-  int (*pD2iz)(double);
-  float (*pL2f)(long);
-  double (*pL2d)(long);
-  long long (*pF2l)(float);
-  long long (*pD2l)(double);
-  float (*pFadd)(float, float);
-  float (*pFsub)(float, float);
-  float (*pFdiv)(float, float);
-  float (*pFmul)(float, float);
-  float (*pFmodf)(float, float);
-  double (*pDadd)(double, double);
-  double (*pDsub)(double, double);
-  double (*pDdiv)(double, double);
-  double (*pDmul)(double, double);
-  double (*pFmod)(double, double);
-  int (*pIdivmod)(int, int);
-  int (*pIdiv)(int, int);
-  long long (*pLmul)(long long, long long);
-  long long (*pLdivmod)(long long, long long);
-  void (*pCheckSuspendFromCode)(Thread*);  // Stub that is called when the suspend count is non-zero
-  void (*pTestSuspendFromCode)();  // Stub that is periodically called to test the suspend count
-  void* (*pAllocObjectFromCode)(uint32_t, void*);
-  void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*);
-  void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t);
-  void* (*pAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
-  void (*pCanPutArrayElementFromCode)(void*, void*);
-  void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t);
-  void* (*pCheckAndAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
-  void (*pCheckCastFromCode)(void*, void*);
-  Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
-  void (*pDeliverException)(void*);
-  Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
-  void* (*pFindNativeMethod)(Thread* thread);
-  int32_t (*pGet32Instance)(uint32_t, void*);
-  int64_t (*pGet64Instance)(uint32_t, void*);
-  void* (*pGetObjInstance)(uint32_t, void*);
-  int32_t (*pGet32Static)(uint32_t);
-  int64_t (*pGet64Static)(uint32_t);
-  void* (*pGetObjStatic)(uint32_t);
-  void (*pHandleFillArrayDataFromCode)(void*, void*);
-  void* (*pInitializeStaticStorage)(uint32_t, void*);
-  uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
-  void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
-  void* (*pInitializeTypeFromCode)(uint32_t, void*);
-  void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
-  void (*pLockObjectFromCode)(void*);
-  void (*pObjectInit)(void*);
-  void* (*pResolveMethodFromCode)(void*, uint32_t, bool);
-  void* (*pResolveStringFromCode)(void*, uint32_t);
-  int (*pSet32Instance)(uint32_t, void*, int32_t);  // field_idx, obj, src
-  int (*pSet64Instance)(uint32_t, void*, int64_t);
-  int (*pSetObjInstance)(uint32_t, void*, void*);
-  int (*pSet32Static)(uint32_t, int32_t);
-  int (*pSet64Static)(uint32_t, int64_t);
-  int (*pSetObjStatic)(uint32_t, void*);
-  void (*pThrowStackOverflowFromCode)(void*);
-  void (*pThrowNullPointerFromCode)();
-  void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
-  void (*pThrowDivZeroFromCode)();
-  void (*pThrowVerificationErrorFromCode)(int32_t, int32_t);
-  void (*pThrowNegArraySizeFromCode)(int32_t);
-  void (*pThrowNoSuchMethodFromCode)(int32_t);
-  void (*pThrowAbstractMethodErrorFromCode)(Method* method, Thread* thread, Method** sp);
-  void (*pUnlockObjectFromCode)(void*);
-  void* (*pUnresolvedDirectMethodTrampolineFromCode)(int32_t, Method**, Thread*,
-                                                     Runtime::TrampolineType);
-  void (*pUpdateDebuggerFromCode)(void*, void*, int32_t, void*);
-
   class StackVisitor {
    public:
     virtual ~StackVisitor() {}
@@ -642,6 +563,90 @@
   // A cached copy of the java.lang.Thread's name.
   std::string* name_;
 
+ public:
+  // Runtime support function pointers
+  void (*pDebugMe)(Method*, uint32_t);
+  void* (*pMemcpy)(void*, const void*, size_t);
+  uint64_t (*pShlLong)(uint64_t, uint32_t);
+  uint64_t (*pShrLong)(uint64_t, uint32_t);
+  uint64_t (*pUshrLong)(uint64_t, uint32_t);
+  float (*pI2f)(int);
+  int (*pF2iz)(float);
+  float (*pD2f)(double);
+  double (*pF2d)(float);
+  double (*pI2d)(int);
+  int (*pD2iz)(double);
+  float (*pL2f)(long);
+  double (*pL2d)(long);
+  long long (*pF2l)(float);
+  long long (*pD2l)(double);
+  float (*pFadd)(float, float);
+  float (*pFsub)(float, float);
+  float (*pFdiv)(float, float);
+  float (*pFmul)(float, float);
+  float (*pFmodf)(float, float);
+  double (*pDadd)(double, double);
+  double (*pDsub)(double, double);
+  double (*pDdiv)(double, double);
+  double (*pDmul)(double, double);
+  double (*pFmod)(double, double);
+  int (*pIdivmod)(int, int);
+  int (*pIdiv)(int, int);
+  long long (*pLmul)(long long, long long);
+  long long (*pLdivmod)(long long, long long);
+  void (*pCheckSuspendFromCode)(Thread*);  // Stub that is called when the suspend count is non-zero
+  void (*pTestSuspendFromCode)();  // Stub that is periodically called to test the suspend count
+  void* (*pAllocObjectFromCode)(uint32_t, void*);
+  void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*);
+  void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t);
+  void* (*pAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
+  void (*pCanPutArrayElementFromCode)(void*, void*);
+  void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t);
+  void* (*pCheckAndAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t);
+  void (*pCheckCastFromCode)(void*, void*);
+  Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
+  void (*pDeliverException)(void*);
+  Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
+  void* (*pFindNativeMethod)(Thread* thread);
+  int32_t (*pGet32Instance)(uint32_t, void*);
+  int64_t (*pGet64Instance)(uint32_t, void*);
+  void* (*pGetObjInstance)(uint32_t, void*);
+  int32_t (*pGet32Static)(uint32_t);
+  int64_t (*pGet64Static)(uint32_t);
+  void* (*pGetObjStatic)(uint32_t);
+  void (*pHandleFillArrayDataFromCode)(void*, void*);
+  void* (*pInitializeStaticStorage)(uint32_t, void*);
+  uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
+  void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
+  void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*);
+  void (*pInvokeSuperTrampolineWithAccessCheck)(uint32_t, void*);
+  void (*pInvokeVirtualTrampolineWithAccessCheck)(uint32_t, void*);
+  void* (*pInitializeTypeFromCode)(uint32_t, void*);
+  void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
+  void (*pLockObjectFromCode)(void*);
+  void (*pObjectInit)(void*);
+  void* (*pResolveMethodFromCode)(void*, uint32_t, bool);
+  void* (*pResolveStringFromCode)(void*, uint32_t);
+  int (*pSet32Instance)(uint32_t, void*, int32_t);  // field_idx, obj, src
+  int (*pSet64Instance)(uint32_t, void*, int64_t);
+  int (*pSetObjInstance)(uint32_t, void*, void*);
+  int (*pSet32Static)(uint32_t, int32_t);
+  int (*pSet64Static)(uint32_t, int64_t);
+  int (*pSetObjStatic)(uint32_t, void*);
+  void (*pThrowStackOverflowFromCode)(void*);
+  void (*pThrowNullPointerFromCode)();
+  void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
+  void (*pThrowDivZeroFromCode)();
+  void (*pThrowVerificationErrorFromCode)(int32_t, int32_t);
+  void (*pThrowNegArraySizeFromCode)(int32_t);
+  void (*pThrowNoSuchMethodFromCode)(int32_t);
+  void (*pThrowAbstractMethodErrorFromCode)(Method* method, Thread* thread, Method** sp);
+  void (*pUnlockObjectFromCode)(void*);
+  void* (*pUnresolvedDirectMethodTrampolineFromCode)(int32_t, Method**, Thread*,
+                                                     Runtime::TrampolineType);
+  void (*pUpdateDebuggerFromCode)(void*, void*, int32_t, void*);
+
+ private:
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };