Refactor reflective method invocation.

Move invocation code out of JNI internal into reflection, including ArgArray
code. Make reflective invocation use the ArgArray to build arguments rather
than allocating a jvalue[] and unboxing arguments into that.
Move reflection part of jni_internal_test into reflection_test.
Make greater use of fast JNI.

Change-Id: Ib381372df5f9a83679e30e7275de24fa0e6b1057
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 954ca99..99285cc 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -63,6 +63,7 @@
 COMPILER_GTEST_COMMON_SRC_FILES := \
 	runtime/jni_internal_test.cc \
 	runtime/proxy_test.cc \
+	runtime/reflection_test.cc \
 	compiler/dex/local_value_numbering_test.cc \
 	compiler/driver/compiler_driver_test.cc \
 	compiler/elf_writer_test.cc \
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index df5afa2..31acb69 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -591,7 +591,7 @@
 
     // Build stack trace
     jobject internal = Thread::Current()->CreateInternalStackTrace(soa);
-    jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(env, internal);
+    jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
     mirror::ObjectArray<mirror::StackTraceElement>* trace_array =
         soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(ste_array);
     EXPECT_TRUE(trace_array != NULL);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 852cef4..9808869 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -28,7 +28,6 @@
 #include "gc/accounting/card_table-inl.h"
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
-#include "invoke_arg_array_builder.h"
 #include "jdwp/object_registry.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -39,6 +38,7 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/throwable.h"
 #include "object_utils.h"
+#include "reflection.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
@@ -3052,10 +3052,8 @@
 
   CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
 
-  MethodHelper mh(m.get());
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-  arg_array.BuildArgArray(soa, pReq->receiver, reinterpret_cast<jvalue*>(pReq->arg_values));
-  InvokeWithArgArray(soa, m.get(), &arg_array, &pReq->result_value, mh.GetShorty());
+  pReq->result_value = InvokeWithJValues(soa, pReq->receiver, soa.EncodeMethod(pReq->method),
+                                         reinterpret_cast<jvalue*>(pReq->arg_values));
 
   mirror::Throwable* exception = soa.Self()->GetException(NULL);
   soa.Self()->ClearException();
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index 2067a45..a0ba6b9 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -16,10 +16,10 @@
 
 #include "class_linker.h"
 #include "interpreter/interpreter.h"
-#include "invoke_arg_array_builder.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/object-inl.h"
 #include "object_utils.h"
+#include "reflection.h"
 #include "runtime.h"
 #include "stack.h"
 
@@ -46,9 +46,7 @@
   }
   uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
   if (kUsePortableCompiler) {
-    ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-    arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset);
-    method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty());
+    InvokeWithShadowFrame(self, shadow_frame, arg_offset, mh, result);
   } else {
     method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
                    (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 36dc1cb..184e5e9 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -21,13 +21,13 @@
 #include "entrypoints/entrypoint_utils.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
-#include "invoke_arg_array_builder.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "object_utils.h"
 #include "runtime.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 4a75152..9c76a14 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -201,7 +201,7 @@
 
   jobject internal = thread->CreateInternalStackTrace(soa);
   ASSERT_TRUE(internal != NULL);
-  jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(env, internal);
+  jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
   ASSERT_TRUE(ste_array != NULL);
   mirror::ObjectArray<mirror::StackTraceElement>* trace_array =
       soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(ste_array);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 76b94fd..45904ff 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -48,7 +48,6 @@
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "heap-inl.h"
 #include "image.h"
-#include "invoke_arg_array_builder.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object.h"
@@ -57,6 +56,7 @@
 #include "mirror/reference-inl.h"
 #include "object_utils.h"
 #include "os.h"
+#include "reflection.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
@@ -2453,11 +2453,10 @@
 
 void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) {
   ScopedObjectAccess soa(self);
-  JValue result;
-  ArgArray arg_array("VL", 2);
-  arg_array.Append(object);
-  soa.DecodeMethod(WellKnownClasses::java_lang_ref_FinalizerReference_add)->Invoke(self,
-      arg_array.GetArray(), arg_array.GetNumBytes(), &result, "VL");
+  ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(object));
+  jvalue args[1];
+  args[0].l = arg.get();
+  InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_FinalizerReference_add, args);
 }
 
 void Heap::EnqueueClearedReferences() {
@@ -2467,11 +2466,11 @@
     // When a runtime isn't started there are no reference queues to care about so ignore.
     if (LIKELY(Runtime::Current()->IsStarted())) {
       ScopedObjectAccess soa(self);
-      JValue result;
-      ArgArray arg_array("VL", 2);
-      arg_array.Append(cleared_references_.GetList());
-      soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(),
-          arg_array.GetArray(), arg_array.GetNumBytes(), &result, "VL");
+      ScopedLocalRef<jobject> arg(self->GetJniEnv(),
+                                  soa.AddLocalReference<jobject>(cleared_references_.GetList()));
+      jvalue args[1];
+      args[0].l = arg.get();
+      InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
     }
     cleared_references_.Clear();
   }
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 589e0b0..21eeafa 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -29,7 +29,6 @@
 #include "dex_instruction.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "gc/accounting/card_table-inl.h"
-#include "invoke_arg_array_builder.h"
 #include "nth_caller_visitor.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method.h"
diff --git a/runtime/invoke_arg_array_builder.h b/runtime/invoke_arg_array_builder.h
deleted file mode 100644
index 6ecce40..0000000
--- a/runtime/invoke_arg_array_builder.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_INVOKE_ARG_ARRAY_BUILDER_H_
-#define ART_RUNTIME_INVOKE_ARG_ARRAY_BUILDER_H_
-
-#include "mirror/art_method.h"
-#include "mirror/object.h"
-#include "scoped_thread_state_change.h"
-
-namespace art {
-
-static inline size_t NumArgArrayBytes(const char* shorty, uint32_t shorty_len) {
-  size_t num_bytes = 0;
-  for (size_t i = 1; i < shorty_len; ++i) {
-    char ch = shorty[i];
-    if (ch == 'D' || ch == 'J') {
-      num_bytes += 8;
-    } else if (ch == 'L') {
-      // Argument is a reference or an array.  The shorty descriptor
-      // does not distinguish between these types.
-      num_bytes += sizeof(mirror::Object*);
-    } else {
-      num_bytes += 4;
-    }
-  }
-  return num_bytes;
-}
-
-class ArgArray {
- public:
-  explicit ArgArray(const char* shorty, uint32_t shorty_len)
-      : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) {
-    size_t num_slots = shorty_len + 1;  // +1 in case of receiver.
-    if (LIKELY((num_slots * 2) < kSmallArgArraySize)) {
-      // We can trivially use the small arg array.
-      arg_array_ = small_arg_array_;
-    } else {
-      // Analyze shorty to see if we need the large arg array.
-      for (size_t i = 1; i < shorty_len; ++i) {
-        char c = shorty[i];
-        if (c == 'J' || c == 'D') {
-          num_slots++;
-        }
-      }
-      if (num_slots <= kSmallArgArraySize) {
-        arg_array_ = small_arg_array_;
-      } else {
-        large_arg_array_.reset(new uint32_t[num_slots]);
-        arg_array_ = large_arg_array_.get();
-      }
-    }
-  }
-
-  uint32_t* GetArray() {
-    return arg_array_;
-  }
-
-  uint32_t GetNumBytes() {
-    return num_bytes_;
-  }
-
-  void Append(uint32_t value) {
-    arg_array_[num_bytes_ / 4] = value;
-    num_bytes_ += 4;
-  }
-
-  void Append(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Append(StackReference<mirror::Object>::FromMirrorPtr(obj).AsVRegValue());
-  }
-
-  void AppendWide(uint64_t value) {
-    // For ARM and MIPS portable, align wide values to 8 bytes (ArgArray starts at offset of 4).
-#if defined(ART_USE_PORTABLE_COMPILER) && (defined(__arm__) || defined(__mips__))
-    if (num_bytes_ % 8 == 0) {
-      num_bytes_ += 4;
-    }
-#endif
-    arg_array_[num_bytes_ / 4] = value;
-    arg_array_[(num_bytes_ / 4) + 1] = value >> 32;
-    num_bytes_ += 8;
-  }
-
-  void BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver, va_list ap)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // Set receiver if non-null (method is not static)
-    if (receiver != nullptr) {
-      Append(receiver);
-    }
-    for (size_t i = 1; i < shorty_len_; ++i) {
-      switch (shorty_[i]) {
-        case 'Z':
-        case 'B':
-        case 'C':
-        case 'S':
-        case 'I':
-          Append(va_arg(ap, jint));
-          break;
-        case 'F': {
-          JValue value;
-          value.SetF(va_arg(ap, jdouble));
-          Append(value.GetI());
-          break;
-        }
-        case 'L':
-          Append(soa.Decode<mirror::Object*>(va_arg(ap, jobject)));
-          break;
-        case 'D': {
-          JValue value;
-          value.SetD(va_arg(ap, jdouble));
-          AppendWide(value.GetJ());
-          break;
-        }
-        case 'J': {
-          AppendWide(va_arg(ap, jlong));
-          break;
-        }
-      }
-    }
-  }
-
-  void BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver, jvalue* args)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // Set receiver if non-null (method is not static)
-    if (receiver != nullptr) {
-      Append(receiver);
-    }
-    for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
-      switch (shorty_[i]) {
-        case 'Z':
-          Append(args[args_offset].z);
-          break;
-        case 'B':
-          Append(args[args_offset].b);
-          break;
-        case 'C':
-          Append(args[args_offset].c);
-          break;
-        case 'S':
-          Append(args[args_offset].s);
-          break;
-        case 'I':
-        case 'F':
-          Append(args[args_offset].i);
-          break;
-        case 'L':
-          Append(soa.Decode<mirror::Object*>(args[args_offset].l));
-          break;
-        case 'D':
-        case 'J':
-          AppendWide(args[args_offset].j);
-          break;
-      }
-    }
-  }
-
-
-  void BuildArgArrayFromFrame(ShadowFrame* shadow_frame, uint32_t arg_offset)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // Set receiver if non-null (method is not static)
-    size_t cur_arg = arg_offset;
-    if (!shadow_frame->GetMethod()->IsStatic()) {
-      Append(shadow_frame->GetVReg(cur_arg));
-      cur_arg++;
-    }
-    for (size_t i = 1; i < shorty_len_; ++i) {
-      switch (shorty_[i]) {
-        case 'Z':
-        case 'B':
-        case 'C':
-        case 'S':
-        case 'I':
-        case 'F':
-        case 'L':
-          Append(shadow_frame->GetVReg(cur_arg));
-          cur_arg++;
-          break;
-        case 'D':
-        case 'J':
-          AppendWide(shadow_frame->GetVRegLong(cur_arg));
-          cur_arg++;
-          cur_arg++;
-          break;
-      }
-    }
-  }
-
- private:
-  enum { kSmallArgArraySize = 16 };
-  const char* const shorty_;
-  const uint32_t shorty_len_;
-  uint32_t num_bytes_;
-  uint32_t* arg_array_;
-  uint32_t small_arg_array_[kSmallArgArraySize];
-  UniquePtr<uint32_t[]> large_arg_array_;
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_INVOKE_ARG_ARRAY_BUILDER_H_
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 6efff1a..43db7ec 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -30,7 +30,6 @@
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
-#include "invoke_arg_array_builder.h"
 #include "jni.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -41,6 +40,7 @@
 #include "mirror/throwable.h"
 #include "object_utils.h"
 #include "parsed_options.h"
+#include "reflection.h"
 #include "runtime.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
@@ -77,104 +77,6 @@
   return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
 }
 
-static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  const DexFile::TypeList* params = MethodHelper(m).GetParameterTypeList();
-  if (params == nullptr) {
-    return;  // No arguments so nothing to check.
-  }
-  uint32_t offset = 0;
-  uint32_t num_params = params->Size();
-  size_t error_count = 0;
-  if (!m->IsStatic()) {
-    offset = 1;
-  }
-  for (uint32_t i = 0; i < num_params; i++) {
-    uint16_t type_idx = params->GetTypeItem(i).type_idx_;
-    mirror::Class* param_type = MethodHelper(m).GetClassFromTypeIdx(type_idx);
-    if (param_type == nullptr) {
-      Thread* self = Thread::Current();
-      CHECK(self->IsExceptionPending());
-      LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
-          << MethodHelper(m).GetTypeDescriptorFromTypeIdx(type_idx) << "\n"
-          << self->GetException(nullptr)->Dump();
-      self->ClearException();
-      ++error_count;
-    } else if (!param_type->IsPrimitive()) {
-      // TODO: check primitives are in range.
-      mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]);
-      if (argument != nullptr && !argument->InstanceOf(param_type)) {
-        LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
-                   << PrettyTypeOf(argument) << " as argument " << (i + 1)
-                   << " to " << PrettyMethod(m);
-        ++error_count;
-      }
-    } else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) {
-      offset++;
-    }
-  }
-  if (error_count > 0) {
-    // TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort
-    // with an argument.
-    JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
-              PrettyMethod(m).c_str());
-  }
-}
-
-void InvokeWithArgArray(const ScopedObjectAccess& soa, mirror::ArtMethod* method,
-                        ArgArray* arg_array, JValue* result, const char* shorty)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  uint32_t* args = arg_array->GetArray();
-  if (UNLIKELY(soa.Env()->check_jni)) {
-    CheckMethodArguments(method, args);
-  }
-  method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
-}
-
-static JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj,
-                                jmethodID mid, va_list args)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtMethod* method = soa.DecodeMethod(mid);
-  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
-  MethodHelper mh(method);
-  JValue result;
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-  arg_array.BuildArgArray(soa, receiver, args);
-  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
-  return result;
-}
-
-static mirror::ArtMethod* FindVirtualMethod(mirror::Object* receiver, mirror::ArtMethod* method)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method);
-}
-
-static JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccess& soa,
-                                                  jobject obj, jmethodID mid, jvalue* args)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
-  mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
-  MethodHelper mh(method);
-  JValue result;
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-  arg_array.BuildArgArray(soa, receiver, args);
-  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
-  return result;
-}
-
-static JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccess& soa,
-                                                  jobject obj, jmethodID mid, va_list args)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
-  mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
-  MethodHelper mh(method);
-  JValue result;
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-  arg_array.BuildArgArray(soa, receiver, args);
-  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
-  return result;
-}
-
 // Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
 // separated with slashes but aren't wrapped with "L;" like regular descriptors
 // (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an
@@ -611,18 +513,6 @@
   SafeMap<std::string, SharedLibrary*> libraries_;
 };
 
-JValue InvokeWithJValues(const ScopedObjectAccess& soa, jobject obj, jmethodID mid,
-                         jvalue* args) {
-  mirror::ArtMethod* method = soa.DecodeMethod(mid);
-  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
-  MethodHelper mh(method);
-  JValue result;
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-  arg_array.BuildArgArray(soa, receiver, args);
-  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
-  return result;
-}
-
 #define CHECK_NON_NULL_ARGUMENT(fn, value) \
   if (UNLIKELY(value == nullptr)) { \
     JniAbortF(#fn, #value " == null"); \
@@ -1014,7 +904,8 @@
     CHECK_NON_NULL_ARGUMENT(CallObjectMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallObjectMethodA, mid);
     ScopedObjectAccess soa(env);
-    JValue result(InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args));
+    JValue result(InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                                      args));
     return soa.AddLocalReference<jobject>(result.GetL());
   }
 
@@ -1040,7 +931,8 @@
     CHECK_NON_NULL_ARGUMENT(CallBooleanMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallBooleanMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetZ();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetZ();
   }
 
   static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1065,7 +957,8 @@
     CHECK_NON_NULL_ARGUMENT(CallByteMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallByteMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetB();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetB();
   }
 
   static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1090,7 +983,8 @@
     CHECK_NON_NULL_ARGUMENT(CallCharMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallCharMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetC();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetC();
   }
 
   static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1115,7 +1009,8 @@
     CHECK_NON_NULL_ARGUMENT(CallDoubleMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallDoubleMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetD();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetD();
   }
 
   static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1140,7 +1035,8 @@
     CHECK_NON_NULL_ARGUMENT(CallFloatMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallFloatMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetF();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetF();
   }
 
   static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1165,7 +1061,8 @@
     CHECK_NON_NULL_ARGUMENT(CallIntMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallIntMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetI();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetI();
   }
 
   static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1190,7 +1087,8 @@
     CHECK_NON_NULL_ARGUMENT(CallLongMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallLongMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetJ();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetJ();
   }
 
   static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1215,7 +1113,8 @@
     CHECK_NON_NULL_ARGUMENT(CallShortMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallShortMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetS();
+    return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
+                                               args).GetS();
   }
 
   static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -1239,7 +1138,7 @@
     CHECK_NON_NULL_ARGUMENT(CallVoidMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallVoidMethodA, mid);
     ScopedObjectAccess soa(env);
-    InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args);
+    InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
   }
 
   static jobject CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1268,7 +1167,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethodA, mid);
     ScopedObjectAccess soa(env);
-    JValue result(InvokeWithJValues(soa, obj, mid, args));
+    JValue result(InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args));
     return soa.AddLocalReference<jobject>(result.GetL());
   }
 
@@ -1297,7 +1196,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetZ();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetZ();
   }
 
   static jbyte CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1324,7 +1223,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetB();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetB();
   }
 
   static jchar CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1351,7 +1250,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetC();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetC();
   }
 
   static jshort CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1378,7 +1277,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetS();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetS();
   }
 
   static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1405,7 +1304,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetI();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetI();
   }
 
   static jlong CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1432,7 +1331,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetJ();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetJ();
   }
 
   static jfloat CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1459,7 +1358,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetF();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetF();
   }
 
   static jdouble CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1486,7 +1385,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethodA, mid);
     ScopedObjectAccess soa(env);
-    return InvokeWithJValues(soa, obj, mid, args).GetD();
+    return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetD();
   }
 
   static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1512,7 +1411,7 @@
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethodA, obj);
     CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethodA, mid);
     ScopedObjectAccess soa(env);
-    InvokeWithJValues(soa, obj, mid, args);
+    InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
   }
 
   static jfieldID GetFieldID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 7b49d33..42796db 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -42,7 +42,6 @@
   class ArtMethod;
   class ClassLoader;
 }  // namespace mirror
-class ArgArray;
 union JValue;
 class Libraries;
 class ParsedOptions;
@@ -55,12 +54,6 @@
 void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
                            jint method_count);
 
-JValue InvokeWithJValues(const ScopedObjectAccess&, jobject obj, jmethodID mid, jvalue* args)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-void InvokeWithArgArray(const ScopedObjectAccess& soa, mirror::ArtMethod* method,
-                        ArgArray *arg_array, JValue* result, const char* shorty)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
 int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause);
 
 class JavaVMExt : public JavaVM {
@@ -155,6 +148,10 @@
   void PushFrame(int capacity);
   void PopFrame();
 
+  template<typename T>
+  T AddLocalReference(mirror::Object* obj, bool jni_work_arounds)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static Offset SegmentStateOffset();
 
   static Offset LocalRefCookieOffset() {
@@ -218,8 +215,29 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
 };
 
+template<typename T>
+inline T JNIEnvExt::AddLocalReference(mirror::Object* obj, bool jni_work_arounds) {
+  IndirectRef ref = locals.Add(local_ref_cookie, obj);
+
+  // TODO: fix this to understand PushLocalFrame, so we can turn it on.
+  if (false) {
+    if (check_jni) {
+      size_t entry_count = locals.Capacity();
+      if (entry_count > 16) {
+        locals.Dump(LOG(WARNING) << "Warning: more than 16 JNI local references: "
+            << entry_count << " (most recent was a " << PrettyTypeOf(obj) << ")\n");
+        // TODO: LOG(FATAL) in a later release?
+      }
+    }
+  }
+
+  if (jni_work_arounds) {
+    return reinterpret_cast<T>(obj);
+  }
+  return reinterpret_cast<T>(ref);
+}
+
 }  // namespace art
 
 std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
-
 #endif  // ART_RUNTIME_JNI_INTERNAL_H_
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 28408d2..7b29a9c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -21,7 +21,6 @@
 #include <cmath>
 
 #include "common_compiler_test.h"
-#include "invoke_arg_array_builder.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
@@ -74,7 +73,7 @@
     }
   }
 
-  virtual void TearDown() {
+  virtual void TearDown() OVERRIDE {
     CleanUpJniEnv();
     CommonCompilerTest::TearDown();
   }
@@ -86,676 +85,6 @@
     return soa.AddLocalReference<jclass>(c);
   }
 
-  void JniInternalTestMakeExecutable(mirror::ArtMethod** method,
-                                     mirror::Object** receiver,
-                                     bool is_static, const char* method_name,
-                                     const char* method_signature)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    const char* class_name = is_static ? "StaticLeafMethods" : "NonStaticLeafMethods";
-    jobject jclass_loader(LoadDex(class_name));
-    Thread* self = Thread::Current();
-    SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
-    SirtRef<mirror::ClassLoader>
-        class_loader(self,
-                     ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader));
-    if (is_static) {
-      MakeExecutable(ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader),
-                     class_name);
-    } else {
-      MakeExecutable(nullptr, "java.lang.Class");
-      MakeExecutable(nullptr, "java.lang.Object");
-      MakeExecutable(ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader),
-                     class_name);
-    }
-
-    mirror::Class* c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(),
-                                                class_loader);
-    CHECK(c != NULL);
-
-    *method = is_static ? c->FindDirectMethod(method_name, method_signature)
-                        : c->FindVirtualMethod(method_name, method_signature);
-    CHECK(method != nullptr);
-
-    *receiver = (is_static ? nullptr : c->AllocObject(self));
-
-    // Start runtime.
-    bool started = runtime_->Start();
-    CHECK(started);
-    self->TransitionFromSuspendedToRunnable();
-  }
-
-  void InvokeNopMethod(bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "nop", "()V");
-
-    ArgArray arg_array("V", 1);
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-    }
-
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "V");
-  }
-
-  void InvokeIdentityByteMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B");
-
-    ArgArray arg_array("BB", 2);
-    uint32_t* args = arg_array.GetArray();
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    arg_array.Append(0U);
-    result.SetB(-1);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB");
-    EXPECT_EQ(0, result.GetB());
-
-    args[0] = -1;
-    result.SetB(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB");
-    EXPECT_EQ(-1, result.GetB());
-
-    args[0] = SCHAR_MAX;
-    result.SetB(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB");
-    EXPECT_EQ(SCHAR_MAX, result.GetB());
-
-    args[0] = (SCHAR_MIN << 24) >> 24;
-    result.SetB(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB");
-    EXPECT_EQ(SCHAR_MIN, result.GetB());
-  }
-
-  void InvokeIdentityIntMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I");
-
-    ArgArray arg_array("II", 2);
-    uint32_t* args = arg_array.GetArray();
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    arg_array.Append(0U);
-    result.SetI(-1);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II");
-    EXPECT_EQ(0, result.GetI());
-
-    args[0] = -1;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II");
-    EXPECT_EQ(-1, result.GetI());
-
-    args[0] = INT_MAX;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II");
-    EXPECT_EQ(INT_MAX, result.GetI());
-
-    args[0] = INT_MIN;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II");
-    EXPECT_EQ(INT_MIN, result.GetI());
-  }
-
-  void InvokeIdentityDoubleMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D");
-
-    ArgArray arg_array("DD", 2);
-    uint32_t* args = arg_array.GetArray();
-    JValue value;
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    value.SetD(0.0);
-    arg_array.AppendWide(value.GetJ());
-    result.SetD(-1.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD");
-    EXPECT_EQ(0.0, result.GetD());
-
-    value.SetD(-1.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD");
-    EXPECT_EQ(-1.0, result.GetD());
-
-    value.SetD(DBL_MAX);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD");
-    EXPECT_EQ(DBL_MAX, result.GetD());
-
-    value.SetD(DBL_MIN);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD");
-    EXPECT_EQ(DBL_MIN, result.GetD());
-  }
-
-  void InvokeSumIntIntMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I");
-
-    ArgArray arg_array("III", 3);
-    uint32_t* args = arg_array.GetArray();
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    result.SetI(-1);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III");
-    EXPECT_EQ(0, result.GetI());
-
-    args[0] = 1;
-    args[1] = 2;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III");
-    EXPECT_EQ(3, result.GetI());
-
-    args[0] = -2;
-    args[1] = 5;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III");
-    EXPECT_EQ(3, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MIN;
-    result.SetI(1234);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III");
-    EXPECT_EQ(-1, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MAX;
-    result.SetI(INT_MIN);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III");
-    EXPECT_EQ(-2, result.GetI());
-  }
-
-  void InvokeSumIntIntIntMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I");
-
-    ArgArray arg_array("IIII", 4);
-    uint32_t* args = arg_array.GetArray();
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    result.SetI(-1);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIII");
-    EXPECT_EQ(0, result.GetI());
-
-    args[0] = 1;
-    args[1] = 2;
-    args[2] = 3;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIII");
-    EXPECT_EQ(6, result.GetI());
-
-    args[0] = -1;
-    args[1] = 2;
-    args[2] = -3;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIII");
-    EXPECT_EQ(-2, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MIN;
-    args[2] = INT_MAX;
-    result.SetI(1234);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIII");
-    EXPECT_EQ(2147483646, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MAX;
-    args[2] = INT_MAX;
-    result.SetI(INT_MIN);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIII");
-    EXPECT_EQ(2147483645, result.GetI());
-  }
-
-  void InvokeSumIntIntIntIntMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I");
-
-    ArgArray arg_array("IIIII", 5);
-    uint32_t* args = arg_array.GetArray();
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    result.SetI(-1);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIII");
-    EXPECT_EQ(0, result.GetI());
-
-    args[0] = 1;
-    args[1] = 2;
-    args[2] = 3;
-    args[3] = 4;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIII");
-    EXPECT_EQ(10, result.GetI());
-
-    args[0] = -1;
-    args[1] = 2;
-    args[2] = -3;
-    args[3] = 4;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIII");
-    EXPECT_EQ(2, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MIN;
-    args[2] = INT_MAX;
-    args[3] = INT_MIN;
-    result.SetI(1234);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIII");
-    EXPECT_EQ(-2, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MAX;
-    args[2] = INT_MAX;
-    args[3] = INT_MAX;
-    result.SetI(INT_MIN);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIII");
-    EXPECT_EQ(-4, result.GetI());
-  }
-
-  void InvokeSumIntIntIntIntIntMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I");
-
-    ArgArray arg_array("IIIIII", 6);
-    uint32_t* args = arg_array.GetArray();
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    arg_array.Append(0U);
-    result.SetI(-1.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIIII");
-    EXPECT_EQ(0, result.GetI());
-
-    args[0] = 1;
-    args[1] = 2;
-    args[2] = 3;
-    args[3] = 4;
-    args[4] = 5;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIIII");
-    EXPECT_EQ(15, result.GetI());
-
-    args[0] = -1;
-    args[1] = 2;
-    args[2] = -3;
-    args[3] = 4;
-    args[4] = -5;
-    result.SetI(0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIIII");
-    EXPECT_EQ(-3, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MIN;
-    args[2] = INT_MAX;
-    args[3] = INT_MIN;
-    args[4] = INT_MAX;
-    result.SetI(1234);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIIII");
-    EXPECT_EQ(2147483645, result.GetI());
-
-    args[0] = INT_MAX;
-    args[1] = INT_MAX;
-    args[2] = INT_MAX;
-    args[3] = INT_MAX;
-    args[4] = INT_MAX;
-    result.SetI(INT_MIN);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "IIIIII");
-    EXPECT_EQ(2147483643, result.GetI());
-  }
-
-  void InvokeSumDoubleDoubleMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D");
-
-    ArgArray arg_array("DDD", 3);
-    uint32_t* args = arg_array.GetArray();
-    JValue value;
-    JValue value2;
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    value.SetD(0.0);
-    value2.SetD(0.0);
-    arg_array.AppendWide(value.GetJ());
-    arg_array.AppendWide(value2.GetJ());
-    result.SetD(-1.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDD");
-    EXPECT_EQ(0.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(2.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDD");
-    EXPECT_EQ(3.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(-2.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDD");
-    EXPECT_EQ(-1.0, result.GetD());
-
-    value.SetD(DBL_MAX);
-    value2.SetD(DBL_MIN);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDD");
-    EXPECT_EQ(1.7976931348623157e308, result.GetD());
-
-    value.SetD(DBL_MAX);
-    value2.SetD(DBL_MAX);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDD");
-    EXPECT_EQ(INFINITY, result.GetD());
-  }
-
-  void InvokeSumDoubleDoubleDoubleMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D");
-
-    ArgArray arg_array("DDDD", 4);
-    uint32_t* args = arg_array.GetArray();
-    JValue value;
-    JValue value2;
-    JValue value3;
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    value.SetD(0.0);
-    value2.SetD(0.0);
-    value3.SetD(0.0);
-    arg_array.AppendWide(value.GetJ());
-    arg_array.AppendWide(value2.GetJ());
-    arg_array.AppendWide(value3.GetJ());
-    result.SetD(-1.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDD");
-    EXPECT_EQ(0.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(2.0);
-    value3.SetD(3.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    args[4] = value3.GetJ();
-    args[5] = value3.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDD");
-    EXPECT_EQ(6.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(-2.0);
-    value3.SetD(3.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    args[4] = value3.GetJ();
-    args[5] = value3.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDD");
-    EXPECT_EQ(2.0, result.GetD());
-  }
-
-  void InvokeSumDoubleDoubleDoubleDoubleMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D");
-
-    ArgArray arg_array("DDDDD", 5);
-    uint32_t* args = arg_array.GetArray();
-    JValue value;
-    JValue value2;
-    JValue value3;
-    JValue value4;
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    value.SetD(0.0);
-    value2.SetD(0.0);
-    value3.SetD(0.0);
-    value4.SetD(0.0);
-    arg_array.AppendWide(value.GetJ());
-    arg_array.AppendWide(value2.GetJ());
-    arg_array.AppendWide(value3.GetJ());
-    arg_array.AppendWide(value4.GetJ());
-    result.SetD(-1.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDDD");
-    EXPECT_EQ(0.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(2.0);
-    value3.SetD(3.0);
-    value4.SetD(4.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    args[4] = value3.GetJ();
-    args[5] = value3.GetJ() >> 32;
-    args[6] = value4.GetJ();
-    args[7] = value4.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDDD");
-    EXPECT_EQ(10.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(-2.0);
-    value3.SetD(3.0);
-    value4.SetD(-4.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    args[4] = value3.GetJ();
-    args[5] = value3.GetJ() >> 32;
-    args[6] = value4.GetJ();
-    args[7] = value4.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDDD");
-    EXPECT_EQ(-2.0, result.GetD());
-  }
-
-  void InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method;
-    mirror::Object* receiver;
-    JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D");
-
-    ArgArray arg_array("DDDDDD", 6);
-    uint32_t* args = arg_array.GetArray();
-    JValue value;
-    JValue value2;
-    JValue value3;
-    JValue value4;
-    JValue value5;
-    JValue result;
-
-    if (!is_static) {
-      arg_array.Append(receiver);
-      args++;
-    }
-
-    value.SetD(0.0);
-    value2.SetD(0.0);
-    value3.SetD(0.0);
-    value4.SetD(0.0);
-    value5.SetD(0.0);
-    arg_array.AppendWide(value.GetJ());
-    arg_array.AppendWide(value2.GetJ());
-    arg_array.AppendWide(value3.GetJ());
-    arg_array.AppendWide(value4.GetJ());
-    arg_array.AppendWide(value5.GetJ());
-    result.SetD(-1.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDDDD");
-    EXPECT_EQ(0.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(2.0);
-    value3.SetD(3.0);
-    value4.SetD(4.0);
-    value5.SetD(5.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    args[4] = value3.GetJ();
-    args[5] = value3.GetJ() >> 32;
-    args[6] = value4.GetJ();
-    args[7] = value4.GetJ() >> 32;
-    args[8] = value5.GetJ();
-    args[9] = value5.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDDDD");
-    EXPECT_EQ(15.0, result.GetD());
-
-    value.SetD(1.0);
-    value2.SetD(-2.0);
-    value3.SetD(3.0);
-    value4.SetD(-4.0);
-    value5.SetD(5.0);
-    args[0] = value.GetJ();
-    args[1] = value.GetJ() >> 32;
-    args[2] = value2.GetJ();
-    args[3] = value2.GetJ() >> 32;
-    args[4] = value3.GetJ();
-    args[5] = value3.GetJ() >> 32;
-    args[6] = value4.GetJ();
-    args[7] = value4.GetJ() >> 32;
-    args[8] = value5.GetJ();
-    args[9] = value5.GetJ() >> 32;
-    result.SetD(0.0);
-    method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result,
-                   "DDDDDD");
-    EXPECT_EQ(3.0, result.GetD());
-  }
-
   JavaVMExt* vm_;
   JNIEnv* env_;
   jclass aioobe_;
@@ -1772,176 +1101,6 @@
   env_->DeleteWeakGlobalRef(o2);
 }
 
-TEST_F(JniInternalTest, StaticMainMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  jobject jclass_loader = LoadDex("Main");
-  SirtRef<mirror::ClassLoader>
-      class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
-  CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
-
-  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V");
-  ASSERT_TRUE(method != NULL);
-
-  ArgArray arg_array("VL", 2);
-  arg_array.Append(0U);
-  JValue result;
-
-  // Start runtime.
-  bool started = runtime_->Start();
-  CHECK(started);
-  Thread::Current()->TransitionFromSuspendedToRunnable();
-
-  method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "VL");
-}
-
-TEST_F(JniInternalTest, StaticNopMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeNopMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticNopMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeNopMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticIdentityByteMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeIdentityByteMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticIdentityByteMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeIdentityByteMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticIdentityIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeIdentityIntMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticIdentityIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeIdentityIntMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticIdentityDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeIdentityDoubleMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticIdentityDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeIdentityDoubleMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntIntMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntIntMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntIntIntMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntIntIntMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumIntIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntIntIntIntMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumIntIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumIntIntIntIntIntMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleDoubleMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleDoubleMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleDoubleDoubleMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleDoubleDoubleMethod(false);
-}
-
-TEST_F(JniInternalTest, StaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(true);
-}
-
-TEST_F(JniInternalTest, NonStaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
-  ScopedObjectAccess soa(Thread::Current());
-  InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(false);
-}
-
 TEST_F(JniInternalTest, Throw) {
   EXPECT_EQ(JNI_ERR, env_->Throw(NULL));
 
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 6482917..0b58af4 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -29,7 +29,7 @@
 #include "jni_internal.h"
 #include "mirror/class.h"
 #include "ScopedUtfChars.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "toStringArray.h"
 #include "trace.h"
 
@@ -153,12 +153,12 @@
 }
 
 static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags);
 }
 
 static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return Runtime::Current()->GetClassLinker()->NumLoadedClasses();
 }
 
@@ -318,14 +318,14 @@
   NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
   NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"),
   NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"),
-  NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
+  NATIVE_METHOD(VMDebug, getLoadedClassCount, "!()I"),
   NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, infopoint, "(I)V"),
-  NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
-  NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
+  NATIVE_METHOD(VMDebug, isDebuggerConnected, "!()Z"),
+  NATIVE_METHOD(VMDebug, isDebuggingEnabled, "!()Z"),
   NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
-  NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
-  NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
+  NATIVE_METHOD(VMDebug, lastDebuggerActivity, "!()J"),
+  NATIVE_METHOD(VMDebug, printLoadedClasses, "!(I)V"),
   NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
   NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"),
   NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
@@ -338,7 +338,7 @@
   NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"),
   NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"),
   NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
-  NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
+  NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"),
 };
 
 void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 0e2d921..5c5eaa1 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -532,7 +532,7 @@
   NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),
   NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
   NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
-  NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
+  NATIVE_METHOD(VMRuntime, isDebuggerActive, "!()Z"),
   NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
   NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
   NATIVE_METHOD(VMRuntime, newUnpaddedArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 7e02e29..9975bf7 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -26,42 +26,43 @@
 
 namespace art {
 
-static jobject GetThreadStack(JNIEnv* env, jobject peer) {
-  {
-    ScopedObjectAccess soa(env);
-    if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
-      return soa.Self()->CreateInternalStackTrace(soa);
-    }
-  }
-  // Suspend thread to build stack trace.
-  bool timed_out;
-  Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
-  if (thread != NULL) {
-    jobject trace;
-    {
-      ScopedObjectAccess soa(env);
-      trace = thread->CreateInternalStackTrace(soa);
-    }
-    // Restart suspended thread.
-    Runtime::Current()->GetThreadList()->Resume(thread, false);
-    return trace;
+static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject peer)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  jobject trace = nullptr;
+  if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
+    trace = soa.Self()->CreateInternalStackTrace(soa);
   } else {
-    if (timed_out) {
-      LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
-          "generous timeout.";
+    // Suspend thread to build stack trace.
+    soa.Self()->TransitionFromRunnableToSuspended(kNative);
+    bool timed_out;
+    Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
+    if (thread != nullptr) {
+      // Must be runnable to create returned array.
+      CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kNative);
+      trace = thread->CreateInternalStackTrace(soa);
+      soa.Self()->TransitionFromRunnableToSuspended(kNative);
+      // Restart suspended thread.
+      Runtime::Current()->GetThreadList()->Resume(thread, false);
+    } else {
+      if (timed_out) {
+        LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
+            "generous timeout.";
+      }
     }
-    return NULL;
+    CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kNative);
   }
+  return trace;
 }
 
 static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread,
                                            jobjectArray javaSteArray) {
-  jobject trace = GetThreadStack(env, javaThread);
-  if (trace == NULL) {
+  ScopedFastNativeObjectAccess soa(env);
+  jobject trace = GetThreadStack(soa, javaThread);
+  if (trace == nullptr) {
     return 0;
   }
   int32_t depth;
-  Thread::InternalStackTraceToStackTraceElementArray(env, trace, javaSteArray, &depth);
+  Thread::InternalStackTraceToStackTraceElementArray(soa, trace, javaSteArray, &depth);
   return depth;
 }
 
@@ -111,19 +112,20 @@
 }
 
 static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject javaThread) {
-  jobject trace = GetThreadStack(env, javaThread);
-  if (trace == NULL) {
-    return NULL;
+  ScopedFastNativeObjectAccess soa(env);
+  jobject trace = GetThreadStack(soa, javaThread);
+  if (trace == nullptr) {
+    return nullptr;
   }
-  return Thread::InternalStackTraceToStackTraceElementArray(env, trace);
+  return Thread::InternalStackTraceToStackTraceElementArray(soa, trace);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"),
+  NATIVE_METHOD(VMStack, fillStackTraceElements, "!(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"),
   NATIVE_METHOD(VMStack, getCallingClassLoader, "!()Ljava/lang/ClassLoader;"),
   NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
   NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"),
-  NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"),
+  NATIVE_METHOD(VMStack, getThreadStackTrace, "!(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"),
 };
 
 void register_dalvik_system_VMStack(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 8bf36e7..6daf9a9 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -45,7 +45,7 @@
 // "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
 static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
                                  jobject javaLoader) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == nullptr) {
     return nullptr;
@@ -96,7 +96,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
+  NATIVE_METHOD(Class, classForName, "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
   NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"),
   NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"),
 };
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index f6149ff..636be5d 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -92,12 +92,12 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Runtime, freeMemory, "()J"),
+  NATIVE_METHOD(Runtime, freeMemory, "!()J"),
   NATIVE_METHOD(Runtime, gc, "()V"),
-  NATIVE_METHOD(Runtime, maxMemory, "()J"),
+  NATIVE_METHOD(Runtime, maxMemory, "!()J"),
   NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
   NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"),
-  NATIVE_METHOD(Runtime, totalMemory, "()J"),
+  NATIVE_METHOD(Runtime, totalMemory, "!()J"),
 };
 
 void register_java_lang_Runtime(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 2665a08..de1b593 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -38,7 +38,7 @@
 }
 
 static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = Thread::FromManagedThread(soa, java_thread);
   return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
@@ -170,8 +170,8 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"),
-  NATIVE_METHOD(Thread, interrupted, "()Z"),
-  NATIVE_METHOD(Thread, isInterrupted, "()Z"),
+  NATIVE_METHOD(Thread, interrupted, "!()Z"),
+  NATIVE_METHOD(Thread, isInterrupted, "!()Z"),
   NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
   NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"),
   NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"),
diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc
index 332a130..d1a1105 100644
--- a/runtime/native/java_lang_Throwable.cc
+++ b/runtime/native/java_lang_Throwable.cc
@@ -15,26 +15,27 @@
  */
 
 #include "jni_internal.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "thread.h"
 
 namespace art {
 
 static jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return soa.Self()->CreateInternalStackTrace(soa);
 }
 
 static jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject javaStackState) {
-  if (javaStackState == NULL) {
-      return NULL;
+  if (javaStackState == nullptr) {
+      return nullptr;
   }
-  return Thread::InternalStackTraceToStackTraceElementArray(env, javaStackState);
+  ScopedFastNativeObjectAccess soa(env);
+  return Thread::InternalStackTraceToStackTraceElementArray(soa, javaStackState);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Throwable, nativeFillInStackTrace, "()Ljava/lang/Object;"),
-  NATIVE_METHOD(Throwable, nativeGetStackTrace, "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;"),
+  NATIVE_METHOD(Throwable, nativeFillInStackTrace, "!()Ljava/lang/Object;"),
+  NATIVE_METHOD(Throwable, nativeGetStackTrace, "!(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;"),
 };
 
 void register_java_lang_Throwable(JNIEnv* env) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 314cdb1..cb8e623 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -18,14 +18,14 @@
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "ScopedUtfChars.h"
 #include "zip_archive.h"
 
 namespace art {
 
 static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == NULL) {
@@ -89,9 +89,9 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(VMClassLoader, findLoadedClass, "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"),
+  NATIVE_METHOD(VMClassLoader, findLoadedClass, "!(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"),
   NATIVE_METHOD(VMClassLoader, getBootClassPathResource, "(Ljava/lang/String;I)Ljava/lang/String;"),
-  NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "()I"),
+  NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "!()I"),
 };
 
 void register_java_lang_VMClassLoader(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index c06bf4c..a22d7ca 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -22,7 +22,7 @@
 #include "mirror/object-inl.h"
 #include "object_utils.h"
 #include "reflection.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -35,8 +35,7 @@
  * with an interface, array, or primitive class.
  */
 static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
-  // TODO: ScopedFastNativeObjectAccess
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   jobject art_method = soa.Env()->GetObjectField(
       javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod);
 
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 694f5e4..7e21d6c 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -90,7 +90,7 @@
 
   class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr);
   mirror::Class* declaringClass = f->GetDeclaringClass();
-  if (!VerifyObjectInClass(class_or_rcvr, declaringClass)) {
+  if (!VerifyObjectIsClass(class_or_rcvr, declaringClass)) {
     return false;
   }
   return true;
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index d29de3d..0b8bb7b 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -24,19 +24,19 @@
 #include "mirror/proxy.h"
 #include "object_utils.h"
 #include "reflection.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "well_known_classes.h"
 
 namespace art {
 
 static jobject Method_invoke(JNIEnv* env,
                              jobject javaMethod, jobject javaReceiver, jobject javaArgs) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
 }
 
 static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   jobject art_method = soa.Env()->GetObjectField(
       javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod);
 
@@ -59,8 +59,8 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Method, invoke, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
-  NATIVE_METHOD(Method, getExceptionTypesNative, "()[Ljava/lang/Class;"),
+  NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
+  NATIVE_METHOD(Method, getExceptionTypesNative, "!()[Ljava/lang/Class;"),
 };
 
 void register_java_lang_reflect_Method(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 1266c41..07d670d 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -19,14 +19,14 @@
 #include "mirror/class_loader.h"
 #include "mirror/object_array.h"
 #include "mirror/string.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "verify_object-inl.h"
 
 namespace art {
 
 static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces,
                                   jobject loader, jobjectArray methods, jobjectArray throws) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   mirror::Class* result = class_linker->CreateProxyClass(soa, name, interfaces, loader, methods,
                                                          throws);
@@ -34,7 +34,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Proxy, generateProxy, "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/ArtMethod;[[Ljava/lang/Class;)Ljava/lang/Class;"),
+  NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/ArtMethod;[[Ljava/lang/Class;)Ljava/lang/Class;"),
 };
 
 void register_java_lang_reflect_Proxy(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 4f81a0b..1b9ebe4 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -18,7 +18,7 @@
 #include "base/mutex.h"
 #include "debugger.h"
 #include "jni_internal.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "stack.h"
@@ -31,7 +31,7 @@
 }
 
 static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return Dbg::GetRecentAllocations();
 }
 
@@ -46,24 +46,24 @@
 static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
   // Suspend thread to build stack trace.
   ThreadList* thread_list = Runtime::Current()->GetThreadList();
+  jobjectArray trace = nullptr;
   bool timed_out;
   Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out);
   if (thread != NULL) {
-    jobject trace;
     {
       ScopedObjectAccess soa(env);
-      trace = thread->CreateInternalStackTrace(soa);
+      jobject internal_trace = thread->CreateInternalStackTrace(soa);
+      trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
     }
     // Restart suspended thread.
     thread_list->Resume(thread, false);
-    return Thread::InternalStackTraceToStackTraceElementArray(env, trace);
   } else {
     if (timed_out) {
       LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend "
           "within a generous timeout.";
     }
-    return NULL;
   }
+  return trace;
 }
 
 static void ThreadCountCallback(Thread*, void* context) {
@@ -136,7 +136,7 @@
 }
 
 static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when));
 }
 
@@ -150,11 +150,11 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(DdmVmInternal, enableRecentAllocations, "(Z)V"),
-  NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "()[B"),
-  NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "()Z"),
+  NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "!()[B"),
+  NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "!()Z"),
   NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"),
   NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"),
-  NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "(I)Z"),
+  NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "!(I)Z"),
   NATIVE_METHOD(DdmVmInternal, heapSegmentNotify, "(IIZ)Z"),
   NATIVE_METHOD(DdmVmInternal, threadNotify, "(Z)V"),
 };
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
index 645d78c..744ac05 100644
--- a/runtime/native/scoped_fast_native_object_access.h
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -17,22 +17,19 @@
 #ifndef ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
 #define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
 
-#include "base/casts.h"
-#include "jni_internal.h"
-#include "thread-inl.h"
 #include "mirror/art_method.h"
-#include "verify_object.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
 // Variant of ScopedObjectAccess that does no runnable transitions. Should only be used by "fast"
 // JNI methods.
-class ScopedFastNativeObjectAccess {
+class ScopedFastNativeObjectAccess : public ScopedObjectAccess {
  public:
   explicit ScopedFastNativeObjectAccess(JNIEnv* env)
     LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
     SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
-     : env_(down_cast<JNIEnvExt*>(env)), self_(ThreadForEnv(env)) {
+     : ScopedObjectAccess(env) {
     Locks::mutator_lock_->AssertSharedHeld(Self());
     DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative());
     // Don't work with raw objects in non-runnable states.
@@ -42,57 +39,8 @@
   ~ScopedFastNativeObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
   }
 
-  Thread* Self() const {
-    return self_;
-  }
-
-  JNIEnvExt* Env() const {
-    return env_;
-  }
-
-  template<typename T>
-  T Decode(jobject obj) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    // Don't work with raw objects in non-runnable states.
-    DCHECK_EQ(Self()->GetState(), kRunnable);
-    return down_cast<T>(Self()->DecodeJObject(obj));
-  }
-
-  mirror::ArtField* DecodeField(jfieldID fid) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    // Don't work with raw objects in non-runnable states.
-    DCHECK_EQ(Self()->GetState(), kRunnable);
-    return reinterpret_cast<mirror::ArtField*>(fid);
-  }
-
-  /*
-   * Variant of ScopedObjectAccessUnched::AddLocalReference that without JNI work arounds
-   * or check JNI that should be being used by fast native methods.
-   */
-  template<typename T>
-  T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    // Don't work with raw objects in non-runnable states.
-    DCHECK_EQ(Self()->GetState(), kRunnable);
-    if (obj == NULL) {
-      return NULL;
-    }
-
-    DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
-
-    IndirectReferenceTable& locals = Env()->locals;
-
-    uint32_t cookie = Env()->local_ref_cookie;
-    IndirectRef ref = locals.Add(cookie, obj);
-
-    return reinterpret_cast<T>(ref);
-  }
-
  private:
-  JNIEnvExt* const env_;
-  Thread* const self_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedFastNativeObjectAccess);
 };
 
 }  // namespace art
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index 96ad55f..63801d3 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -341,7 +341,7 @@
     shorty_ = nullptr;
   }
 
-  const mirror::ArtMethod* GetMethod() const {
+  mirror::ArtMethod* GetMethod() const {
     return method_;
   }
 
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 0bfa70f..4310557 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -19,7 +19,6 @@
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
-#include "invoke_arg_array_builder.h"
 #include "jni_internal.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -29,12 +28,440 @@
 #include "mirror/object_array-inl.h"
 #include "object_utils.h"
 #include "scoped_thread_state_change.h"
+#include "stack.h"
 #include "well_known_classes.h"
 
 namespace art {
 
-jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject javaReceiver,
-                     jobject javaArgs) {
+class ArgArray {
+ public:
+  explicit ArgArray(const char* shorty, uint32_t shorty_len)
+      : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) {
+    size_t num_slots = shorty_len + 1;  // +1 in case of receiver.
+    if (LIKELY((num_slots * 2) < kSmallArgArraySize)) {
+      // We can trivially use the small arg array.
+      arg_array_ = small_arg_array_;
+    } else {
+      // Analyze shorty to see if we need the large arg array.
+      for (size_t i = 1; i < shorty_len; ++i) {
+        char c = shorty[i];
+        if (c == 'J' || c == 'D') {
+          num_slots++;
+        }
+      }
+      if (num_slots <= kSmallArgArraySize) {
+        arg_array_ = small_arg_array_;
+      } else {
+        large_arg_array_.reset(new uint32_t[num_slots]);
+        arg_array_ = large_arg_array_.get();
+      }
+    }
+  }
+
+  uint32_t* GetArray() {
+    return arg_array_;
+  }
+
+  uint32_t GetNumBytes() {
+    return num_bytes_;
+  }
+
+  void Append(uint32_t value) {
+    arg_array_[num_bytes_ / 4] = value;
+    num_bytes_ += 4;
+  }
+
+  void Append(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Append(StackReference<mirror::Object>::FromMirrorPtr(obj).AsVRegValue());
+  }
+
+  void AppendWide(uint64_t value) {
+    // For ARM and MIPS portable, align wide values to 8 bytes (ArgArray starts at offset of 4).
+#if defined(ART_USE_PORTABLE_COMPILER) && (defined(__arm__) || defined(__mips__))
+    if (num_bytes_ % 8 == 0) {
+      num_bytes_ += 4;
+    }
+#endif
+    arg_array_[num_bytes_ / 4] = value;
+    arg_array_[(num_bytes_ / 4) + 1] = value >> 32;
+    num_bytes_ += 8;
+  }
+
+  void AppendFloat(float value) {
+    jvalue jv;
+    jv.f = value;
+    Append(jv.i);
+  }
+
+  void AppendDouble(double value) {
+    jvalue jv;
+    jv.d = value;
+    AppendWide(jv.j);
+  }
+
+  void BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver, va_list ap)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Set receiver if non-null (method is not static)
+    if (receiver != nullptr) {
+      Append(receiver);
+    }
+    for (size_t i = 1; i < shorty_len_; ++i) {
+      switch (shorty_[i]) {
+        case 'Z':
+        case 'B':
+        case 'C':
+        case 'S':
+        case 'I':
+          Append(va_arg(ap, jint));
+          break;
+        case 'F':
+          AppendFloat(va_arg(ap, jdouble));
+          break;
+        case 'L':
+          Append(soa.Decode<mirror::Object*>(va_arg(ap, jobject)));
+          break;
+        case 'D':
+          AppendDouble(va_arg(ap, jdouble));
+          break;
+        case 'J':
+          AppendWide(va_arg(ap, jlong));
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+    }
+  }
+
+  void BuildArgArray(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver,
+                     jvalue* args)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Set receiver if non-null (method is not static)
+    if (receiver != nullptr) {
+      Append(receiver);
+    }
+    for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
+      switch (shorty_[i]) {
+        case 'Z':
+          Append(args[args_offset].z);
+          break;
+        case 'B':
+          Append(args[args_offset].b);
+          break;
+        case 'C':
+          Append(args[args_offset].c);
+          break;
+        case 'S':
+          Append(args[args_offset].s);
+          break;
+        case 'I':
+        case 'F':
+          Append(args[args_offset].i);
+          break;
+        case 'L':
+          Append(soa.Decode<mirror::Object*>(args[args_offset].l));
+          break;
+        case 'D':
+        case 'J':
+          AppendWide(args[args_offset].j);
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+    }
+  }
+
+  void BuildArgArrayFromFrame(ShadowFrame* shadow_frame, uint32_t arg_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Set receiver if non-null (method is not static)
+    size_t cur_arg = arg_offset;
+    if (!shadow_frame->GetMethod()->IsStatic()) {
+      Append(shadow_frame->GetVReg(cur_arg));
+      cur_arg++;
+    }
+    for (size_t i = 1; i < shorty_len_; ++i) {
+      switch (shorty_[i]) {
+        case 'Z':
+        case 'B':
+        case 'C':
+        case 'S':
+        case 'I':
+        case 'F':
+        case 'L':
+          Append(shadow_frame->GetVReg(cur_arg));
+          cur_arg++;
+          break;
+        case 'D':
+        case 'J':
+          AppendWide(shadow_frame->GetVRegLong(cur_arg));
+          cur_arg++;
+          cur_arg++;
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+    }
+  }
+
+  static void ThrowIllegalPrimitiveArgumentException(const char* expected,
+                                                     const StringPiece& found_descriptor)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ThrowIllegalArgumentException(nullptr,
+        StringPrintf("Invalid primitive conversion from %s to %s", expected,
+                     PrettyDescriptor(found_descriptor.as_string()).c_str()).c_str());
+  }
+
+  bool BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver,
+                     mirror::ObjectArray<mirror::Object>* args, MethodHelper& mh)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    const DexFile::TypeList* classes = mh.GetParameterTypeList();
+    // Set receiver if non-null (method is not static)
+    if (receiver != nullptr) {
+      Append(receiver);
+    }
+    for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
+      mirror::Object* arg = args->Get(args_offset);
+      if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
+        mirror::Class* dst_class =
+            mh.GetClassFromTypeIdx(classes->GetTypeItem(args_offset).type_idx_);
+        if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
+          ThrowIllegalArgumentException(nullptr,
+              StringPrintf("method %s argument %d has type %s, got %s",
+                  PrettyMethod(mh.GetMethod(), false).c_str(),
+                  args_offset + 1,  // Humans don't count from 0.
+                  PrettyDescriptor(dst_class).c_str(),
+                  PrettyTypeOf(arg).c_str()).c_str());
+          return false;
+        }
+      }
+
+#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
+          const StringPiece src_descriptor(arg != nullptr \
+              ? ClassHelper(arg->GetClass<>()).GetDescriptor() \
+              : "null"); \
+          if (LIKELY(src_descriptor == match_descriptor)) { \
+            mirror::ArtField* primitive_field = arg->GetClass()->GetIFields()->Get(0); \
+            append(primitive_field-> get_fn(arg));
+
+#define DO_ARG(match_descriptor, get_fn, append) \
+          } else if (LIKELY(src_descriptor == match_descriptor)) { \
+            mirror::ArtField* primitive_field = arg->GetClass()->GetIFields()->Get(0); \
+            append(primitive_field-> get_fn(arg));
+
+#define DO_FAIL(expected) \
+          } else { \
+            if (arg->GetClass<>()->IsPrimitive()) { \
+              ThrowIllegalPrimitiveArgumentException(expected, src_descriptor); \
+            } else { \
+              ThrowIllegalArgumentException(nullptr, \
+                  StringPrintf("method %s argument %d has type %s, got %s", \
+                      PrettyMethod(mh.GetMethod(), false).c_str(), \
+                      args_offset + 1, \
+                      expected, \
+                      PrettyTypeOf(arg).c_str()).c_str()); \
+            } \
+            return false; \
+          } }
+
+      switch (shorty_[i]) {
+        case 'L':
+          Append(arg);
+          break;
+        case 'Z':
+          DO_FIRST_ARG("Ljava/lang/Boolean;", GetBoolean, Append)
+          DO_FAIL("boolean")
+          break;
+        case 'B':
+          DO_FIRST_ARG("Ljava/lang/Byte;", GetByte, Append)
+          DO_FAIL("byte")
+          break;
+        case 'C':
+          DO_FIRST_ARG("Ljava/lang/Character;", GetChar, Append)
+          DO_FAIL("char")
+          break;
+        case 'S':
+          DO_FIRST_ARG("Ljava/lang/Short;", GetShort, Append)
+          DO_ARG("Ljava/lang/Byte;", GetByte, Append)
+          DO_FAIL("short")
+          break;
+        case 'I':
+          DO_FIRST_ARG("Ljava/lang/Integer;", GetInt, Append)
+          DO_ARG("Ljava/lang/Character;", GetChar, Append)
+          DO_ARG("Ljava/lang/Short;", GetShort, Append)
+          DO_ARG("Ljava/lang/Byte;", GetByte, Append)
+          DO_FAIL("int")
+          break;
+        case 'J':
+          DO_FIRST_ARG("Ljava/lang/Long;", GetLong, AppendWide)
+          DO_ARG("Ljava/lang/Integer;", GetInt, AppendWide)
+          DO_ARG("Ljava/lang/Character;", GetChar, AppendWide)
+          DO_ARG("Ljava/lang/Short;", GetShort, AppendWide)
+          DO_ARG("Ljava/lang/Byte;", GetByte, AppendWide)
+          DO_FAIL("long")
+          break;
+        case 'F':
+          DO_FIRST_ARG("Ljava/lang/Float;", GetFloat, AppendFloat)
+          DO_ARG("Ljava/lang/Long;", GetLong, AppendFloat)
+          DO_ARG("Ljava/lang/Integer;", GetInt, AppendFloat)
+          DO_ARG("Ljava/lang/Character;", GetChar, AppendFloat)
+          DO_ARG("Ljava/lang/Short;", GetShort, AppendFloat)
+          DO_ARG("Ljava/lang/Byte;", GetByte, AppendFloat)
+          DO_FAIL("float")
+          break;
+        case 'D':
+          DO_FIRST_ARG("Ljava/lang/Double;", GetDouble, AppendDouble)
+          DO_ARG("Ljava/lang/Float;", GetFloat, AppendDouble)
+          DO_ARG("Ljava/lang/Long;", GetLong, AppendDouble)
+          DO_ARG("Ljava/lang/Integer;", GetInt, AppendDouble)
+          DO_ARG("Ljava/lang/Character;", GetChar, AppendDouble)
+          DO_ARG("Ljava/lang/Short;", GetShort, AppendDouble)
+          DO_ARG("Ljava/lang/Byte;", GetByte, AppendDouble)
+          DO_FAIL("double")
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+#undef DO_FIRST_ARG
+#undef DO_ARG
+#undef DO_FAIL
+    }
+    return true;
+  }
+
+ private:
+  enum { kSmallArgArraySize = 16 };
+  const char* const shorty_;
+  const uint32_t shorty_len_;
+  uint32_t num_bytes_;
+  uint32_t* arg_array_;
+  uint32_t small_arg_array_[kSmallArgArraySize];
+  UniquePtr<uint32_t[]> large_arg_array_;
+};
+
+static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const DexFile::TypeList* params = MethodHelper(m).GetParameterTypeList();
+  if (params == nullptr) {
+    return;  // No arguments so nothing to check.
+  }
+  uint32_t offset = 0;
+  uint32_t num_params = params->Size();
+  size_t error_count = 0;
+  if (!m->IsStatic()) {
+    offset = 1;
+  }
+  for (uint32_t i = 0; i < num_params; i++) {
+    uint16_t type_idx = params->GetTypeItem(i).type_idx_;
+    mirror::Class* param_type = MethodHelper(m).GetClassFromTypeIdx(type_idx);
+    if (param_type == nullptr) {
+      Thread* self = Thread::Current();
+      CHECK(self->IsExceptionPending());
+      LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
+          << MethodHelper(m).GetTypeDescriptorFromTypeIdx(type_idx) << "\n"
+          << self->GetException(nullptr)->Dump();
+      self->ClearException();
+      ++error_count;
+    } else if (!param_type->IsPrimitive()) {
+      // TODO: check primitives are in range.
+      mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]);
+      if (argument != nullptr && !argument->InstanceOf(param_type)) {
+        LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
+                   << PrettyTypeOf(argument) << " as argument " << (i + 1)
+                   << " to " << PrettyMethod(m);
+        ++error_count;
+      }
+    } else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) {
+      offset++;
+    }
+  }
+  if (error_count > 0) {
+    // TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort
+    // with an argument.
+    JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
+              PrettyMethod(m).c_str());
+  }
+}
+
+static mirror::ArtMethod* FindVirtualMethod(mirror::Object* receiver,
+                                            mirror::ArtMethod* method)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method);
+}
+
+
+static void InvokeWithArgArray(const ScopedObjectAccessUnchecked& soa, mirror::ArtMethod* method,
+                               ArgArray* arg_array, JValue* result, const char* shorty)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  uint32_t* args = arg_array->GetArray();
+  if (UNLIKELY(soa.Env()->check_jni)) {
+    CheckMethodArguments(method, args);
+  }
+  method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
+}
+
+JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj, jmethodID mid, va_list args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtMethod* method = soa.DecodeMethod(mid);
+  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+JValue InvokeWithJValues(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver,
+                         jmethodID mid, jvalue* args) {
+  mirror::ArtMethod* method = soa.DecodeMethod(mid);
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccess& soa,
+                                           mirror::Object* receiver, jmethodID mid, jvalue* args) {
+  mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccess& soa,
+                                           jobject obj, jmethodID mid, va_list args) {
+  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
+  mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset,
+                           MethodHelper& mh, JValue* result) {
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset);
+  shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result,
+                                    mh.GetShorty());
+}
+
+jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod,
+                     jobject javaReceiver, jobject javaArgs) {
   jmethodID mid = soa.Env()->FromReflectedMethod(javaMethod);
   mirror::ArtMethod* m = soa.DecodeMethod(mid);
 
@@ -47,17 +474,16 @@
     declaring_class = sirt_c.get();
   }
 
-  mirror::Object* receiver = NULL;
+  mirror::Object* receiver = nullptr;
   if (!m->IsStatic()) {
     // Check that the receiver is non-null and an instance of the field's declaring class.
     receiver = soa.Decode<mirror::Object*>(javaReceiver);
-    if (!VerifyObjectInClass(receiver, declaring_class)) {
+    if (!VerifyObjectIsClass(receiver, declaring_class)) {
       return NULL;
     }
 
     // Find the actual implementation of the virtual method.
     m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m);
-    mid = soa.EncodeMethod(m);
   }
 
   // Get our arrays of arguments and their types, and check they're the same size.
@@ -65,8 +491,8 @@
       soa.Decode<mirror::ObjectArray<mirror::Object>*>(javaArgs);
   MethodHelper mh(m);
   const DexFile::TypeList* classes = mh.GetParameterTypeList();
-  uint32_t classes_size = classes == NULL ? 0 : classes->Size();
-  uint32_t arg_count = (objects != NULL) ? objects->GetLength() : 0;
+  uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size();
+  uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0;
   if (arg_count != classes_size) {
     ThrowIllegalArgumentException(NULL,
                                   StringPrintf("Wrong number of arguments; expected %d, got %d",
@@ -74,22 +500,15 @@
     return NULL;
   }
 
-  // Translate javaArgs to a jvalue[].
-  UniquePtr<jvalue[]> args(new jvalue[arg_count]);
-  JValue* decoded_args = reinterpret_cast<JValue*>(args.get());
-  for (uint32_t i = 0; i < arg_count; ++i) {
-    mirror::Object* arg = objects->Get(i);
-    mirror::Class* dst_class = mh.GetClassFromTypeIdx(classes->GetTypeItem(i).type_idx_);
-    if (!UnboxPrimitiveForArgument(arg, dst_class, decoded_args[i], m, i)) {
-      return NULL;
-    }
-    if (!dst_class->IsPrimitive()) {
-      args[i].l = soa.AddLocalReference<jobject>(arg);
-    }
+  // Invoke the method.
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  if (!arg_array.BuildArgArray(soa, receiver, objects, mh)) {
+    CHECK(soa.Self()->IsExceptionPending());
+    return nullptr;
   }
 
-  // Invoke the method.
-  JValue value(InvokeWithJValues(soa, javaReceiver, mid, args.get()));
+  InvokeWithArgArray(soa, m, &arg_array, &result, mh.GetShorty());
 
   // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early.
   if (soa.Self()->IsExceptionPending()) {
@@ -103,10 +522,11 @@
   }
 
   // Box if necessary and return.
-  return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(), value));
+  return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(),
+                                                     result));
 }
 
-bool VerifyObjectInClass(mirror::Object* o, mirror::Class* c) {
+bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
   if (o == NULL) {
     ThrowNullPointerException(NULL, "null receiver");
     return false;
@@ -218,6 +638,10 @@
   if (src_class == Primitive::kPrimNot) {
     return value.GetL();
   }
+  if (src_class == Primitive::kPrimVoid) {
+    // There's no such thing as a void field, and void methods invoked via reflection return null.
+    return nullptr;
+  }
 
   jmethodID m = NULL;
   const char* shorty;
@@ -254,20 +678,15 @@
     m = WellKnownClasses::java_lang_Short_valueOf;
     shorty = "LS";
     break;
-  case Primitive::kPrimVoid:
-    // There's no such thing as a void field, and void methods invoked via reflection return null.
-    return nullptr;
   default:
     LOG(FATAL) << static_cast<int>(src_class);
     shorty = nullptr;
   }
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  if (kIsDebugBuild) {
-    CHECK_EQ(soa.Self()->GetState(), kRunnable);
-  }
+  DCHECK_EQ(soa.Self()->GetState(), kRunnable);
 
-  ArgArray arg_array(nullptr, 0);
+  ArgArray arg_array(shorty, 2);
   JValue result;
   if (src_class == Primitive::kPrimDouble || src_class == Primitive::kPrimLong) {
     arg_array.AppendWide(value.GetJ());
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 13c90af..d2f9f25 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -28,7 +28,10 @@
   class Object;
 }  // namespace mirror
 union JValue;
+class MethodHelper;
 class ScopedObjectAccess;
+class ScopedObjectAccessUnchecked;
+class ShadowFrame;
 class ThrowLocation;
 
 mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value)
@@ -48,10 +51,30 @@
                            const JValue& src, JValue& dst)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-jobject InvokeMethod(const ScopedObjectAccess& soa, jobject method, jobject receiver, jobject args)
+JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj, jmethodID mid, va_list args)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-bool VerifyObjectInClass(mirror::Object* o, mirror::Class* c)
+JValue InvokeWithJValues(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver,
+                         jmethodID mid, jvalue* args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccess& soa,
+                                           mirror::Object* receiver, jmethodID mid, jvalue* args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccess& soa,
+                                           jobject obj, jmethodID mid, va_list args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset,
+                           MethodHelper& mh, JValue* result)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+jobject InvokeMethod(const ScopedObjectAccess& soa, jobject method, jobject receiver,
+                     jobject args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 }  // namespace art
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
new file mode 100644
index 0000000..c14da5d
--- /dev/null
+++ b/runtime/reflection_test.cc
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reflection.h"
+
+#include <cmath>
+
+#include "common_compiler_test.h"
+#include "mirror/art_method-inl.h"
+
+namespace art {
+
+// TODO: Convert to CommonRuntimeTest. Currently MakeExecutable is used.
+class ReflectionTest : public CommonCompilerTest {
+ protected:
+  virtual void SetUp() {
+    CommonCompilerTest::SetUp();
+
+    vm_ = Runtime::Current()->GetJavaVM();
+
+    // Turn on -verbose:jni for the JNI tests.
+    // gLogVerbosity.jni = true;
+
+    vm_->AttachCurrentThread(&env_, NULL);
+
+    ScopedLocalRef<jclass> aioobe(env_,
+                                  env_->FindClass("java/lang/ArrayIndexOutOfBoundsException"));
+    CHECK(aioobe.get() != NULL);
+    aioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(aioobe.get()));
+
+    ScopedLocalRef<jclass> ase(env_, env_->FindClass("java/lang/ArrayStoreException"));
+    CHECK(ase.get() != NULL);
+    ase_ = reinterpret_cast<jclass>(env_->NewGlobalRef(ase.get()));
+
+    ScopedLocalRef<jclass> sioobe(env_,
+                                  env_->FindClass("java/lang/StringIndexOutOfBoundsException"));
+    CHECK(sioobe.get() != NULL);
+    sioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(sioobe.get()));
+  }
+
+  void CleanUpJniEnv() {
+    if (aioobe_ != NULL) {
+      env_->DeleteGlobalRef(aioobe_);
+      aioobe_ = NULL;
+    }
+    if (ase_ != NULL) {
+      env_->DeleteGlobalRef(ase_);
+      ase_ = NULL;
+    }
+    if (sioobe_ != NULL) {
+      env_->DeleteGlobalRef(sioobe_);
+      sioobe_ = NULL;
+    }
+  }
+
+  virtual void TearDown() {
+    CleanUpJniEnv();
+    CommonCompilerTest::TearDown();
+  }
+
+  jclass GetPrimitiveClass(char descriptor) {
+    ScopedObjectAccess soa(env_);
+    mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor);
+    CHECK(c != nullptr);
+    return soa.AddLocalReference<jclass>(c);
+  }
+
+  void ReflectionTestMakeExecutable(mirror::ArtMethod** method,
+                                    mirror::Object** receiver,
+                                    bool is_static, const char* method_name,
+                                    const char* method_signature)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    const char* class_name = is_static ? "StaticLeafMethods" : "NonStaticLeafMethods";
+    jobject jclass_loader(LoadDex(class_name));
+    Thread* self = Thread::Current();
+    SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
+    SirtRef<mirror::ClassLoader>
+        class_loader(self,
+                     ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader));
+    if (is_static) {
+      MakeExecutable(ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader),
+                     class_name);
+    } else {
+      MakeExecutable(nullptr, "java.lang.Class");
+      MakeExecutable(nullptr, "java.lang.Object");
+      MakeExecutable(ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader),
+                     class_name);
+    }
+
+    mirror::Class* c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(),
+                                                class_loader);
+    CHECK(c != NULL);
+
+    *method = is_static ? c->FindDirectMethod(method_name, method_signature)
+                        : c->FindVirtualMethod(method_name, method_signature);
+    CHECK(method != nullptr);
+
+    *receiver = (is_static ? nullptr : c->AllocObject(self));
+
+    // Start runtime.
+    bool started = runtime_->Start();
+    CHECK(started);
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+  void InvokeNopMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "nop", "()V");
+    InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), nullptr);
+  }
+
+  void InvokeIdentityByteMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B");
+    jvalue args[1];
+
+    args[0].b = 0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0, result.GetB());
+
+    args[0].b = -1;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-1, result.GetB());
+
+    args[0].b = SCHAR_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(SCHAR_MAX, result.GetB());
+
+    args[0].b = (SCHAR_MIN << 24) >> 24;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(SCHAR_MIN, result.GetB());
+  }
+
+  void InvokeIdentityIntMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I");
+    jvalue args[1];
+
+    args[0].i = 0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0, result.GetI());
+
+    args[0].i = -1;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-1, result.GetI());
+
+    args[0].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(INT_MAX, result.GetI());
+
+    args[0].i = INT_MIN;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(INT_MIN, result.GetI());
+  }
+
+  void InvokeIdentityDoubleMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D");
+    jvalue args[1];
+
+    args[0].d = 0.0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0.0, result.GetD());
+
+    args[0].d = -1.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-1.0, result.GetD());
+
+    args[0].d = DBL_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(DBL_MAX, result.GetD());
+
+    args[0].d = DBL_MIN;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(DBL_MIN, result.GetD());
+  }
+
+  void InvokeSumIntIntMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I");
+    jvalue args[2];
+
+    args[0].i = 1;
+    args[1].i = 2;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(3, result.GetI());
+
+    args[0].i = -2;
+    args[1].i = 5;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(3, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MIN;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-1, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-2, result.GetI());
+  }
+
+  void InvokeSumIntIntIntMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I");
+    jvalue args[3];
+
+    args[0].i = 0;
+    args[1].i = 0;
+    args[2].i = 0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0, result.GetI());
+
+    args[0].i = 1;
+    args[1].i = 2;
+    args[2].i = 3;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(6, result.GetI());
+
+    args[0].i = -1;
+    args[1].i = 2;
+    args[2].i = -3;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-2, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MIN;
+    args[2].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(2147483646, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MAX;
+    args[2].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(2147483645, result.GetI());
+  }
+
+  void InvokeSumIntIntIntIntMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I");
+    jvalue args[4];
+
+    args[0].i = 0;
+    args[1].i = 0;
+    args[2].i = 0;
+    args[3].i = 0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0, result.GetI());
+
+    args[0].i = 1;
+    args[1].i = 2;
+    args[2].i = 3;
+    args[3].i = 4;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(10, result.GetI());
+
+    args[0].i = -1;
+    args[1].i = 2;
+    args[2].i = -3;
+    args[3].i = 4;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(2, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MIN;
+    args[2].i = INT_MAX;
+    args[3].i = INT_MIN;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-2, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MAX;
+    args[2].i = INT_MAX;
+    args[3].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-4, result.GetI());
+  }
+
+  void InvokeSumIntIntIntIntIntMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I");
+    jvalue args[5];
+
+    args[0].i = 0;
+    args[1].i = 0;
+    args[2].i = 0;
+    args[3].i = 0;
+    args[4].i = 0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0, result.GetI());
+
+    args[0].i = 1;
+    args[1].i = 2;
+    args[2].i = 3;
+    args[3].i = 4;
+    args[4].i = 5;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(15, result.GetI());
+
+    args[0].i = -1;
+    args[1].i = 2;
+    args[2].i = -3;
+    args[3].i = 4;
+    args[4].i = -5;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-3, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MIN;
+    args[2].i = INT_MAX;
+    args[3].i = INT_MIN;
+    args[4].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(2147483645, result.GetI());
+
+    args[0].i = INT_MAX;
+    args[1].i = INT_MAX;
+    args[2].i = INT_MAX;
+    args[3].i = INT_MAX;
+    args[4].i = INT_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(2147483643, result.GetI());
+  }
+
+  void InvokeSumDoubleDoubleMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D");
+    jvalue args[2];
+
+    args[0].d = 0.0;
+    args[1].d = 0.0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = 2.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(3.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = -2.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-1.0, result.GetD());
+
+    args[0].d = DBL_MAX;
+    args[1].d = DBL_MIN;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(1.7976931348623157e308, result.GetD());
+
+    args[0].d = DBL_MAX;
+    args[1].d = DBL_MAX;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(INFINITY, result.GetD());
+  }
+
+  void InvokeSumDoubleDoubleDoubleMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D");
+    jvalue args[3];
+
+    args[0].d = 0.0;
+    args[1].d = 0.0;
+    args[2].d = 0.0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = 2.0;
+    args[2].d = 3.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(6.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = -2.0;
+    args[2].d = 3.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(2.0, result.GetD());
+  }
+
+  void InvokeSumDoubleDoubleDoubleDoubleMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D");
+    jvalue args[4];
+
+    args[0].d = 0.0;
+    args[1].d = 0.0;
+    args[2].d = 0.0;
+    args[3].d = 0.0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = 2.0;
+    args[2].d = 3.0;
+    args[3].d = 4.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(10.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = -2.0;
+    args[2].d = 3.0;
+    args[3].d = -4.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(-2.0, result.GetD());
+  }
+
+  void InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(bool is_static) {
+    ScopedObjectAccess soa(env_);
+    mirror::ArtMethod* method;
+    mirror::Object* receiver;
+    ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D");
+    jvalue args[5];
+
+    args[0].d = 0.0;
+    args[1].d = 0.0;
+    args[2].d = 0.0;
+    args[3].d = 0.0;
+    args[4].d = 0.0;
+    JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(0.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = 2.0;
+    args[2].d = 3.0;
+    args[3].d = 4.0;
+    args[4].d = 5.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(15.0, result.GetD());
+
+    args[0].d = 1.0;
+    args[1].d = -2.0;
+    args[2].d = 3.0;
+    args[3].d = -4.0;
+    args[4].d = 5.0;
+    result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+    EXPECT_EQ(3.0, result.GetD());
+  }
+
+  JavaVMExt* vm_;
+  JNIEnv* env_;
+  jclass aioobe_;
+  jclass ase_;
+  jclass sioobe_;
+};
+
+TEST_F(ReflectionTest, StaticMainMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadDex("Main");
+  SirtRef<mirror::ClassLoader>
+      class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
+  CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
+
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V");
+  ASSERT_TRUE(method != NULL);
+
+  // Start runtime.
+  bool started = runtime_->Start();
+  CHECK(started);
+  soa.Self()->TransitionFromSuspendedToRunnable();
+
+  jvalue args[1];
+  args[0].l = nullptr;
+  InvokeWithJValues(soa, nullptr, soa.EncodeMethod(method), args);
+}
+
+TEST_F(ReflectionTest, StaticNopMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeNopMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticNopMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeNopMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticIdentityByteMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeIdentityByteMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticIdentityByteMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeIdentityByteMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticIdentityIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeIdentityIntMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticIdentityIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeIdentityIntMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticIdentityDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeIdentityDoubleMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticIdentityDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeIdentityDoubleMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumIntIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntIntMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumIntIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntIntMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumIntIntIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntIntIntMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumIntIntIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntIntIntMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumIntIntIntIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntIntIntIntMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumIntIntIntIntIntMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumIntIntIntIntIntMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleDoubleMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleDoubleMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleDoubleDoubleMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleDoubleDoubleMethod(false);
+}
+
+TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(true);
+}
+
+TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
+  TEST_DISABLED_FOR_PORTABLE();
+  InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(false);
+}
+
+}  // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1555bf2..51edc85 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -42,7 +42,6 @@
 #include "image.h"
 #include "instrumentation.h"
 #include "intern_table.h"
-#include "invoke_arg_array_builder.h"
 #include "jni_internal.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -54,6 +53,7 @@
 #include "monitor.h"
 #include "parsed_options.h"
 #include "oat_file.h"
+#include "reflection.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "signal_catcher.h"
@@ -316,9 +316,7 @@
       class_loader_class->FindDirectMethod("getSystemClassLoader", "()Ljava/lang/ClassLoader;");
   CHECK(getSystemClassLoader != NULL);
 
-  JValue result;
-  ArgArray arg_array(nullptr, 0);
-  InvokeWithArgArray(soa, getSystemClassLoader, &arg_array, &result, "L");
+  JValue result = InvokeWithJValues(soa, nullptr, soa.EncodeMethod(getSystemClassLoader), nullptr);
   SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
                                             down_cast<mirror::ClassLoader*>(result.GetL()));
   CHECK(class_loader.get() != nullptr);
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index d9e7986..ebc5452 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -171,28 +171,7 @@
 
     DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
 
-    IndirectReferenceTable& locals = Env()->locals;
-
-    uint32_t cookie = Env()->local_ref_cookie;
-    IndirectRef ref = locals.Add(cookie, obj);
-
-#if 0  // TODO: fix this to understand PushLocalFrame, so we can turn it on.
-    if (Env()->check_jni) {
-      size_t entry_count = locals.Capacity();
-      if (entry_count > 16) {
-        LOG(WARNING) << "Warning: more than 16 JNI local references: "
-                     << entry_count << " (most recent was a " << PrettyTypeOf(obj) << ")\n"
-                     << Dumpable<IndirectReferenceTable>(locals);
-        // TODO: LOG(FATAL) in a later release?
-      }
-    }
-#endif
-    if (Vm()->work_around_app_jni_bugs) {
-      // Hand out direct pointers to support broken old apps.
-      return reinterpret_cast<T>(obj);
-    }
-
-    return reinterpret_cast<T>(ref);
+    return Env()->AddLocalReference<T>(obj, Vm()->work_around_app_jni_bugs);
   }
 
   template<typename T>
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f4b9d9a..f5ed082 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -45,7 +45,6 @@
 #include "gc/accounting/card_table-inl.h"
 #include "gc/heap.h"
 #include "gc/space/space.h"
-#include "invoke_arg_array_builder.h"
 #include "jni_internal.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -174,12 +173,7 @@
     // Invoke the 'run' method of our java.lang.Thread.
     mirror::Object* receiver = self->opeer_;
     jmethodID mid = WellKnownClasses::java_lang_Thread_run;
-    mirror::ArtMethod* m =
-        receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(soa.DecodeMethod(mid));
-    JValue result;
-    ArgArray arg_array(nullptr, 0);
-    arg_array.Append(receiver);
-    m->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), &result, "V");
+    InvokeVirtualOrInterfaceWithJValues(soa, receiver, mid, nullptr);
   }
   // Detach and delete self.
   Runtime::Current()->GetThreadList()->Unregister(self);
@@ -1413,10 +1407,8 @@
   return soa.AddLocalReference<jobjectArray>(trace);
 }
 
-jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal,
-    jobjectArray output_array, int* stack_depth) {
-  // Transition into runnable state to work on Object*/Array*
-  ScopedObjectAccess soa(env);
+jobjectArray Thread::InternalStackTraceToStackTraceElementArray(const ScopedObjectAccess& soa,
+    jobject internal, jobjectArray output_array, int* stack_depth) {
   // Decode the internal stack trace into the depth, method trace and PC trace
   int32_t depth = soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal)->GetLength() - 1;
 
@@ -1526,11 +1518,12 @@
                                       const char* exception_class_descriptor,
                                       const char* msg) {
   DCHECK_EQ(this, Thread::Current());
+  ScopedObjectAccessUnchecked soa(this);
   // Ensure we don't forget arguments over object allocation.
   SirtRef<mirror::Object> saved_throw_this(this, throw_location.GetThis());
   SirtRef<mirror::ArtMethod> saved_throw_method(this, throw_location.GetMethod());
   // Ignore the cause throw location. TODO: should we report this as a re-throw?
-  SirtRef<mirror::Throwable> cause(this, GetException(nullptr));
+  ScopedLocalRef<jobject> cause(GetJniEnv(), soa.AddLocalReference<jobject>(GetException(nullptr)));
   ClearException();
   Runtime* runtime = Runtime::Current();
 
@@ -1567,10 +1560,11 @@
   // Choose an appropriate constructor and set up the arguments.
   const char* signature;
   const char* shorty;
-  SirtRef<mirror::String> msg_string(this, nullptr);
+  ScopedLocalRef<jstring> msg_string(GetJniEnv(), nullptr);
   if (msg != nullptr) {
     // Ensure we remember this and the method over the String allocation.
-    msg_string.reset(mirror::String::AllocFromModifiedUtf8(this, msg));
+    msg_string.reset(
+        soa.AddLocalReference<jstring>(mirror::String::AllocFromModifiedUtf8(this, msg)));
     if (UNLIKELY(msg_string.get() == nullptr)) {
       CHECK(IsExceptionPending());  // OOME.
       return;
@@ -1602,25 +1596,27 @@
     // case in the compiler. We won't be able to invoke the constructor of the exception, so set
     // the exception fields directly.
     if (msg != nullptr) {
-      exception->SetDetailMessage(msg_string.get());
+      exception->SetDetailMessage(down_cast<mirror::String*>(DecodeJObject(msg_string.get())));
     }
     if (cause.get() != nullptr) {
-      exception->SetCause(cause.get());
+      exception->SetCause(down_cast<mirror::Throwable*>(DecodeJObject(cause.get())));
     }
     ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(),
                                          throw_location.GetDexPc());
     SetException(gc_safe_throw_location, exception.get());
   } else {
-    ArgArray args(shorty, strlen(shorty));
-    args.Append(exception.get());
+    jvalue jv_args[2];
+    size_t i = 0;
+
     if (msg != nullptr) {
-      args.Append(msg_string.get());
+      jv_args[i].l = msg_string.get();
+      ++i;
     }
     if (cause.get() != nullptr) {
-      args.Append(cause.get());
+      jv_args[i].l = cause.get();
+      ++i;
     }
-    JValue result;
-    exception_init_method->Invoke(this, args.GetArray(), args.GetNumBytes(), &result, shorty);
+    InvokeWithJValues(soa, exception.get(), soa.EncodeMethod(exception_init_method), jv_args);
     if (LIKELY(!IsExceptionPending())) {
       ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(),
                                            throw_location.GetDexPc());
diff --git a/runtime/thread.h b/runtime/thread.h
index 264a927..fdf976d 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -398,8 +398,9 @@
   // StackTraceElement[]. If output_array is NULL, a new array is created, otherwise as many
   // frames as will fit are written into the given array. If stack_depth is non-NULL, it's updated
   // with the number of valid frames in the returned array.
-  static jobjectArray InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal,
-      jobjectArray output_array = NULL, int* stack_depth = NULL);
+  static jobjectArray InternalStackTraceToStackTraceElementArray(const ScopedObjectAccess& soa,
+      jobject internal, jobjectArray output_array = nullptr, int* stack_depth = nullptr)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void VisitRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 2e6ce4f..76b6f27 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -17,7 +17,6 @@
 #include "transaction.h"
 
 #include "common_runtime_test.h"
-#include "invoke_arg_array_builder.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"