Further proxy refactorings.

Factor the crawling of the quick stack arguments into a common visitor.
Factor the proxy invocation dispatch into common runtime support code,
fix numerous bugs relating to GC in the LLVM runtime support with this.
Clean up BoxPrimitive to not use an in argument as an out.

Change-Id: I7b12c8d88d5083614e480b8fb1d2f2ef7c0a51b7
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index 7d73249..2dcfba5 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -730,8 +730,9 @@
   }
 }
 
-// Handler for invocation on proxy methods. We create a boxed argument array. And we invoke
-// the invocation handler which is a field within the proxy object receiver.
+// Handler for invocation on proxy methods. Create a boxed argument array and invoke the invocation
+// handler which is a field within the proxy object receiver. The var args encode the arguments
+// with the last argument being a pointer to a JValue to store the result in.
 void art_proxy_invoke_handler_from_code(AbstractMethod* proxy_method, ...)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   va_list ap;
@@ -740,141 +741,59 @@
   Object* receiver = va_arg(ap, Object*);
   Thread* self = va_arg(ap, Thread*);
   MethodHelper proxy_mh(proxy_method);
-  const size_t num_params = proxy_mh.NumArgs();
 
-  // Start new JNI local reference state
+  // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
+  const char* old_cause =
+      self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
+  self->VerifyStack();
+
+  // Start new JNI local reference state.
   JNIEnvExt* env = self->GetJniEnv();
   ScopedObjectAccessUnchecked soa(env);
   ScopedJniEnvLocalRefState env_state(env);
 
-  // Create local ref. copies of the receiver
+  // Create local ref. copies of the receiver.
   jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver);
 
-  // Convert proxy method into expected interface method
+  // Convert proxy method into expected interface method.
   AbstractMethod* interface_method = proxy_method->FindOverriddenMethod();
   DCHECK(interface_method != NULL);
   DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
+  jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method);
 
-  // Set up arguments array and place in local IRT during boxing (which may allocate/GC)
-  jvalue args_jobj[3];
-  args_jobj[0].l = rcvr_jobj;
-  args_jobj[1].l = soa.AddLocalReference<jobject>(interface_method);
-  // Args array, if no arguments then NULL (don't include receiver in argument count)
-  args_jobj[2].l = NULL;
-  ObjectArray<Object>* args = NULL;
-  if ((num_params - 1) > 0) {
-    args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(self, num_params - 1);
-    if (args == NULL) {
-      CHECK(self->IsExceptionPending());
-      return;
-    }
-    args_jobj[2].l = soa.AddLocalReference<jobjectArray>(args);
-  }
-
-  // Get parameter types.
-  const char* shorty = proxy_mh.GetShorty();
-  ObjectArray<Class>* param_types = proxy_mh.GetParameterTypes(self);
-  if (param_types == NULL) {
-    CHECK(self->IsExceptionPending());
-    return;
-  }
-
-  // Box arguments.
-  for (size_t i = 0; i < (num_params - 1);++i) {
-    JValue val;
-    switch (shorty[i+1]) {
-      case 'Z':
-        val.SetZ(va_arg(ap, jint));
+  // Record arguments and turn Object* arguments into jobject to survive GC.
+  std::vector<jvalue> args;
+  const size_t num_params = proxy_mh.NumArgs();
+  for (size_t i = 1; i < num_params; ++i) {
+    jvalue val;
+    switch (proxy_mh.GetParamPrimitiveType(i)) {
+      case Primitive::kPrimNot:
+        val.l = soa.AddLocalReference<jobject>(va_arg(ap, Object*));
         break;
-      case 'B':
-        val.SetB(va_arg(ap, jint));
+      case Primitive::kPrimBoolean:  // Fall-through.
+      case Primitive::kPrimByte:     // Fall-through.
+      case Primitive::kPrimChar:     // Fall-through.
+      case Primitive::kPrimShort:    // Fall-through.
+      case Primitive::kPrimInt:      // Fall-through.
+        val.i = va_arg(ap, jint);
         break;
-      case 'C':
-        val.SetC(va_arg(ap, jint));
-        break;
-      case 'S':
-        val.SetS(va_arg(ap, jint));
-        break;
-      case 'I':
-        val.SetI(va_arg(ap, jint));
-        break;
-      case 'F':
-        val.SetI(va_arg(ap, jint)); // TODO: is this right?
-        break;
-      case 'L':
-        val.SetL(va_arg(ap, Object*));
+      case Primitive::kPrimFloat:
+        val.f = va_arg(ap, jfloat);
         break;
       case 'D':
+        val.d(va_arg(ap, jdouble));
+        break;
       case 'J':
-        val.SetJ(va_arg(ap, jlong)); // TODO: is this right for double?
+        val.j(va_arg(ap, jlong));
         break;
     }
-    Class* param_type = param_types->Get(i);
-    if (param_type->IsPrimitive()) {
-      BoxPrimitive(param_type->GetPrimitiveType(), val);
-      if (self->IsExceptionPending()) {
-        return;
-      }
-    }
-    args->Set(i, val.GetL());
+    args.push_back(val);
   }
-
-  DCHECK(env->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy));
-
-  jobject inv_hand = env->GetObjectField(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy_h);
-  // Call InvocationHandler.invoke
-  jobject result = env->CallObjectMethodA(inv_hand, WellKnownClasses::java_lang_reflect_InvocationHandler_invoke, args_jobj);
-
-  // Place result in stack args
-  if (!self->IsExceptionPending()) {
-    if (shorty[0] == 'V') {
-      return;
-    }
-    Object* result_ref = self->DecodeJObject(result);
-    JValue* result_unboxed = va_arg(ap, JValue*);
-    if (result_ref == NULL) {
-      result_unboxed->SetL(NULL);
-    } else {
-      bool unboxed_okay = UnboxPrimitiveForResult(result_ref, proxy_mh.GetReturnType(), *result_unboxed);
-      if (!unboxed_okay) {
-        self->ClearException();
-        self->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
-                                 "Couldn't convert result of type %s to %s",
-                                 PrettyTypeOf(result_ref).c_str(),
-                                 PrettyDescriptor(proxy_mh.GetReturnType()).c_str());
-        return;
-      }
-    }
-  } else {
-    // In the case of checked exceptions that aren't declared, the exception must be wrapped by
-    // a UndeclaredThrowableException.
-    Throwable* exception = self->GetException();
-    if (exception->IsCheckedException()) {
-      SynthesizedProxyClass* proxy_class =
-          down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
-      int throws_index = -1;
-      size_t num_virt_methods = proxy_class->NumVirtualMethods();
-      for (size_t i = 0; i < num_virt_methods; i++) {
-        if (proxy_class->GetVirtualMethod(i) == proxy_method) {
-          throws_index = i;
-          break;
-        }
-      }
-      CHECK_NE(throws_index, -1);
-      ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index);
-      Class* exception_class = exception->GetClass();
-      bool declares_exception = false;
-      for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
-        Class* declared_exception = declared_exceptions->Get(i);
-        declares_exception = declared_exception->IsAssignableFrom(exception_class);
-      }
-      if (!declares_exception) {
-        self->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", NULL);
-      }
-    }
-  }
-
+  self->EndAssertNoThreadSuspension(old_cause);
+  JValue* result_unboxed = va_arg(ap, JValue*);
   va_end(ap);
+  *result_unboxed = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
+                                                 rcvr_jobj, interface_method_jobj, args);
 }
 
 void* art_find_runtime_support_func(void* context, const char* name) {
diff --git a/src/native/java_lang_reflect_Field.cc b/src/native/java_lang_reflect_Field.cc
index d99ccb3..fde8f94 100644
--- a/src/native/java_lang_reflect_Field.cc
+++ b/src/native/java_lang_reflect_Field.cc
@@ -101,9 +101,8 @@
   if (!GetFieldValue(soa, o, f, value, true)) {
     return NULL;
   }
-  BoxPrimitive(FieldHelper(f).GetTypeAsPrimitiveType(), value);
-
-  return soa.AddLocalReference<jobject>(value.GetL());
+  return
+      soa.AddLocalReference<jobject>(BoxPrimitive(FieldHelper(f).GetTypeAsPrimitiveType(), value));
 }
 
 static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char dst_descriptor) {
diff --git a/src/oat/runtime/argument_visitor.h b/src/oat/runtime/argument_visitor.h
new file mode 100644
index 0000000..06256ca
--- /dev/null
+++ b/src/oat/runtime/argument_visitor.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013 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_SRC_OAT_RUNTIME_ARGUMENT_VISITOR_H_
+#define ART_SRC_OAT_RUNTIME_ARGUMENT_VISITOR_H_
+
+#include "object_utils.h"
+
+namespace art {
+
+// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
+class ArgumentVisitor {
+ public:
+// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
+// Size of Runtime::kRefAndArgs callee save frame.
+// Size of Method* and register parameters in out stack arguments.
+#if defined(__arm__)
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
+#define STACK_ARG_SKIP 16
+#elif defined(__mips__)
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
+#define STACK_ARG_SKIP 16
+#elif defined(__i386__)
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32
+#define STACK_ARG_SKIP 16
+#else
+#error "Unsupported architecture"
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
+#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
+#define STACK_ARG_SKIP 0
+#endif
+
+  ArgumentVisitor(MethodHelper& caller_mh, AbstractMethod** sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+    caller_mh_(caller_mh),
+    args_in_regs_(ComputeArgsInRegs(caller_mh)),
+    num_params_(caller_mh.NumArgs()),
+    reg_args_(reinterpret_cast<byte*>(sp) + CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
+    stack_args_(reinterpret_cast<byte*>(sp) + CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
+                + STACK_ARG_SKIP),
+    cur_args_(reg_args_),
+    cur_arg_index_(0),
+    param_index_(0),
+    is_split_long_or_double_(false) {
+  }
+
+  virtual ~ArgumentVisitor() {}
+
+  virtual void Visit() = 0;
+
+  bool IsParamAReference() const {
+    return caller_mh_.IsParamAReference(param_index_);
+  }
+
+  bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return caller_mh_.IsParamALongOrDouble(param_index_);
+  }
+
+  Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return caller_mh_.GetParamPrimitiveType(param_index_);
+  }
+
+  byte* GetParamAddress() const {
+    return cur_args_ + (cur_arg_index_ * kPointerSize);
+  }
+
+  bool IsSplitLongOrDouble() const {
+    return is_split_long_or_double_;
+  }
+
+  uint64_t ReadSplitLongParam() const {
+    DCHECK(IsSplitLongOrDouble());
+    uint64_t low_half = *reinterpret_cast<uint32_t*>(GetParamAddress());
+    uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_);
+    return (low_half & 0xffffffffULL) | (high_half << 32);
+
+  }
+
+  void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    for (cur_arg_index_ = 0;  cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
+      is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble();
+      Visit();
+      cur_arg_index_ += (caller_mh_.IsParamALongOrDouble(param_index_) ? 2 : 1);
+      param_index_++;
+    }
+    cur_args_ = stack_args_;
+    cur_arg_index_ = is_split_long_or_double_ ? 1 : 0;
+    is_split_long_or_double_ = false;
+    while (param_index_ < num_params_) {
+      Visit();
+      cur_arg_index_ += (caller_mh_.IsParamALongOrDouble(param_index_) ? 2 : 1);
+      param_index_++;
+    }
+  }
+
+ private:
+  static size_t ComputeArgsInRegs(MethodHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    size_t args_in_regs = 0;
+    size_t num_params = mh.NumArgs();
+    for (size_t i = 0; i < num_params; i++) {
+      args_in_regs = args_in_regs + (mh.IsParamALongOrDouble(i) ? 2 : 1);
+      if (args_in_regs > 3) {
+        args_in_regs = 3;
+        break;
+      }
+    }
+    return args_in_regs;
+  }
+  MethodHelper& caller_mh_;
+  const size_t args_in_regs_;
+  const size_t num_params_;
+  byte* const reg_args_;
+  byte* const stack_args_;
+  byte* cur_args_;
+  size_t cur_arg_index_;
+  size_t param_index_;
+  // Does a 64bit parameter straddle the register and stack arguments?
+  bool is_split_long_or_double_;
+};
+
+}
+
+#endif  // ART_SRC_OAT_RUNTIME_ARGUMENT_VISITOR_H_
diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S
index 7901c5f..dda1222 100644
--- a/src/oat/runtime/arm/runtime_support_arm.S
+++ b/src/oat/runtime/arm/runtime_support_arm.S
@@ -754,18 +754,18 @@
     .extern artProxyInvokeHandler
     /*
      * Called by managed code that is attempting to call a method on a proxy class. On entry
-     * r0 holds the proxy method; r1, r2 and r3 may contain arguments.
+     * r0 holds the proxy method and r1 holds the receiver; r2 and r3 may contain arguments. The
+     * frame size of the invoked proxy method agrees with a ref and args callee save frame.
      */
     ALIGN_FUNCTION_ENTRY
 art_proxy_invoke_handler:
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     str     r0, [sp, #0]           @ place proxy method at bottom of frame
     mov     r2, r9                 @ pass Thread::Current
-    add     r3, sp, #12            @ pointer to r2/r3/LR/caller's Method**/out-args as second arg
-    blx     artProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, args...)
+    mov     r3, sp                 @ pass SP
+    blx     artProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
     ldr     r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     ldr     lr,  [sp, #44]         @ restore lr
-    ldrd    r0,  [sp, #12]         @ load r0/r1 from r2/r3 that were overwritten with the out args
     add     sp,  #48               @ pop frame
     cmp     r12, #0                @ success if no exception is pending
     bxeq    lr                     @ return on success
diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S
index 4484cde..efd3ede 100644
--- a/src/oat/runtime/mips/runtime_support_mips.S
+++ b/src/oat/runtime/mips/runtime_support_mips.S
@@ -853,11 +853,9 @@
     sw      $a0, 0($sp)            # place proxy method at bottom of frame
     move    $a2, rSELF             # pass Thread::Current
     jal     artProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, args...)
-    addiu   $a3, $sp, 8            # pointer to a2/a3/ra/caller's Method**/out-args as second arg
+    move    $a3, $sp               # pass $sp
     lw      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
     lw      $ra, 44($sp)           # restore $ra
-    lw      $v0, 8($sp)
-    lw      $v1, 12($sp)
     bnez    $t0, 1f
     addiu   $sp, $sp, 48           # pop frame
     jr      $ra
diff --git a/src/oat/runtime/support_proxy.cc b/src/oat/runtime/support_proxy.cc
index 1fdab2a..5bf3cde 100644
--- a/src/oat/runtime/support_proxy.cc
+++ b/src/oat/runtime/support_proxy.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "argument_visitor.h"
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
@@ -24,42 +25,69 @@
 
 #include "ScopedLocalRef.h"
 
-#if defined(__arm__)
-#define SP_OFFSET_IN_BYTES 12
-#define FRAME_SIZE_IN_BYTES 48u
-#define ARG2_OFFSET_IN_WORDS 11 // offset to 3rd arg; skip callee saves, LR, Method* and out arg spills for OUT0 to OUT2
-#elif defined(__mips__)
-#define SP_OFFSET_IN_BYTES 8
-#define FRAME_SIZE_IN_BYTES 48u
-#define ARG2_OFFSET_IN_WORDS 12 // offset to 3rd arg; skip callee saves, LR, Method* and out arg spills for OUT0 to OUT2
-#elif defined(__i386__)
-#define SP_OFFSET_IN_BYTES 8
-#define FRAME_SIZE_IN_BYTES 32u
-#define ARG2_OFFSET_IN_WORDS 8 // offset to 3rd arg; skip callee saves, LR, Method* and out arg spills for OUT0 to OUT2
-#else
-#error "Unsupported architecture"
-#define SP_OFFSET_IN_BYTES 0
-#define FRAME_SIZE_IN_BYTES 0
-#define ARG2_OFFSET_IN_WORDS 0
-#endif
-
 namespace art {
 
+// Visits arguments on the stack placing them into the args vector, Object* arguments are converted
+// to jobjects.
+class BuildArgumentVisitor : public ArgumentVisitor {
+ public:
+  BuildArgumentVisitor(MethodHelper& caller_mh, AbstractMethod** sp,
+                       ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) :
+    ArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {}
+
+  virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    jvalue val;
+    Primitive::Type type = GetParamPrimitiveType();
+    switch (type) {
+      case Primitive::kPrimNot: {
+        Object* obj = *reinterpret_cast<Object**>(GetParamAddress());
+        val.l = soa_.AddLocalReference<jobject>(obj);
+        break;
+      }
+      case Primitive::kPrimLong:  // Fall-through.
+      case Primitive::kPrimDouble:
+        if (IsSplitLongOrDouble()) {
+          val.j = ReadSplitLongParam();
+        } else {
+          val.j = *reinterpret_cast<jlong*>(GetParamAddress());
+        }
+        break;
+      case Primitive::kPrimBoolean:
+      case Primitive::kPrimByte:
+      case Primitive::kPrimChar:
+      case Primitive::kPrimShort:
+      case Primitive::kPrimInt:
+      case Primitive::kPrimFloat:
+        val.i =  *reinterpret_cast<jint*>(GetParamAddress());
+        break;
+      case Primitive::kPrimVoid:
+        LOG(FATAL) << "UNREACHABLE";
+        val.j = 0;
+        break;
+    }
+    args_.push_back(val);
+  }
+
+ private:
+  ScopedObjectAccessUnchecked& soa_;
+  std::vector<jvalue>& args_;
+};
+
 // Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method
-// which is responsible for recording callee save registers. We explicitly handlerize incoming
-// reference arguments (so they survive GC) and create a boxed argument array. Finally we invoke
-// the invocation handler which is a field within the proxy object receiver.
-extern "C" void artProxyInvokeHandler(AbstractMethod* proxy_method, Object* receiver,
-                                      Thread* self, byte* stack_args)
+// which is responsible for recording callee save registers. We explicitly place into jobjects the
+// incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a
+// field within the proxy object, which will box the primitive arguments and deal with error cases.
+extern "C" uint64_t artProxyInvokeHandler(AbstractMethod* proxy_method, Object* receiver,
+                                          Thread* self, AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
   const char* old_cause =
       self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
   // Register the top of the managed stack, making stack crawlable.
-  AbstractMethod** proxy_sp = reinterpret_cast<AbstractMethod**>(stack_args - SP_OFFSET_IN_BYTES);
-  DCHECK_EQ(*proxy_sp, proxy_method);
-  self->SetTopOfStack(proxy_sp, 0);
-  DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), FRAME_SIZE_IN_BYTES);
+  DCHECK_EQ(*sp, proxy_method);
+  self->SetTopOfStack(sp, 0);
+  DCHECK_EQ(proxy_method->GetFrameSizeInBytes(),
+            Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
   self->VerifyStack();
   // Start new JNI local reference state.
   JNIEnvExt* env = self->GetJniEnv();
@@ -67,173 +95,26 @@
   ScopedJniEnvLocalRefState env_state(env);
   // Create local ref. copies of proxy method and the receiver.
   jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver);
-  jobject proxy_method_jobj = soa.AddLocalReference<jobject>(proxy_method);
 
-  // Placing into local references incoming arguments from the caller's register arguments,
-  // replacing original Object* with jobject.
+  // Placing arguments into args vector and remove the receiver.
   MethodHelper proxy_mh(proxy_method);
-  const size_t num_params = proxy_mh.NumArgs();
-  size_t args_in_regs = 0;
-  for (size_t i = 1; i < num_params; i++) {  // skip receiver
-    args_in_regs = args_in_regs + (proxy_mh.IsParamALongOrDouble(i) ? 2 : 1);
-    if (args_in_regs > 2) {
-      args_in_regs = 2;
-      break;
-    }
-  }
-  size_t cur_arg = 0;  // current stack location to read
-  size_t param_index = 1;  // skip receiver
-  while (cur_arg < args_in_regs && param_index < num_params) {
-    if (proxy_mh.IsParamAReference(param_index)) {
-      Object* obj = *reinterpret_cast<Object**>(stack_args + (cur_arg * kPointerSize));
-      jobject jobj = soa.AddLocalReference<jobject>(obj);
-      *reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize)) = jobj;
-    }
-    cur_arg = cur_arg + (proxy_mh.IsParamALongOrDouble(param_index) ? 2 : 1);
-    param_index++;
-  }
-  // Placing into local references incoming arguments from the caller's stack arguments.
-  cur_arg += ARG2_OFFSET_IN_WORDS;
-  while (param_index < num_params) {
-    if (proxy_mh.IsParamAReference(param_index)) {
-      Object* obj = *reinterpret_cast<Object**>(stack_args + (cur_arg * kPointerSize));
-      jobject jobj = soa.AddLocalReference<jobject>(obj);
-      *reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize)) = jobj;
-    }
-    cur_arg = cur_arg + (proxy_mh.IsParamALongOrDouble(param_index) ? 2 : 1);
-    param_index++;
-  }
-  self->EndAssertNoThreadSuspension(old_cause);
-  // Sanity check writing the jobjects over the Object*s in place didn't damage the stack.
-  self->VerifyStack();
+  std::vector<jvalue> args;
+  BuildArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args);
+  local_ref_visitor.VisitArguments();
+  args.erase(args.begin());
 
-  // Set up arguments array and place in local IRT during boxing (which may allocate/GC).
-  jvalue args_jobj[3];
-  args_jobj[0].l = rcvr_jobj;
-  args_jobj[1].l = proxy_method_jobj;
-  // Args array, if no arguments then NULL (don't include receiver in argument count).
-  args_jobj[2].l = NULL;
-  ObjectArray<Object>* args = NULL;
-  if ((num_params - 1) > 0) {
-    args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(self, num_params - 1);
-    if (args == NULL) {
-      CHECK(self->IsExceptionPending());
-      return;
-    }
-    args_jobj[2].l = soa.AddLocalReference<jobjectArray>(args);
-  }
   // Convert proxy method into expected interface method.
   AbstractMethod* interface_method = proxy_method->FindOverriddenMethod();
   DCHECK(interface_method != NULL);
   DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
-  args_jobj[1].l = soa.AddLocalReference<jobject>(interface_method);
-  // Box primitive type arguments.
-  cur_arg = 0;  // Stack location to read to start.
-  // Reset index, will index into param type array which doesn't include the receiver.
-  param_index = 0;
-  ObjectArray<Class>* param_types = proxy_mh.GetParameterTypes(self);
-  if (param_types == NULL) {
-    CHECK(self->IsExceptionPending());
-    return;
-  }
-  // Check number of parameter types agrees with number from the Method - less 1 for the receiver.
-  DCHECK_EQ(static_cast<size_t>(param_types->GetLength()), num_params - 1);
-  while (cur_arg < args_in_regs && param_index < (num_params - 1)) {
-    Class* param_type = param_types->Get(param_index);
-    Object* obj;
-    if (!param_type->IsPrimitive()) {
-      obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize)));
-    } else {
-      JValue val = *reinterpret_cast<JValue*>(stack_args + (cur_arg * kPointerSize));
-      if (cur_arg == 1 && (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble())) {
-        // long/double split over regs and stack, mask in high half from stack arguments.
-        uint64_t high_half =
-            *reinterpret_cast<uint32_t*>(stack_args + ((ARG2_OFFSET_IN_WORDS + 2) * kPointerSize));
-        val.SetJ((val.GetJ() & 0xffffffffULL) | (high_half << 32));
-      }
-      BoxPrimitive(param_type->GetPrimitiveType(), val);
-      if (self->IsExceptionPending()) {
-        return;
-      }
-      obj = val.GetL();
-    }
-    args->Set(param_index, obj);
-    cur_arg = cur_arg + (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble() ? 2 : 1);
-    param_index++;
-  }
-  // Placing into local references incoming arguments from the caller's stack arguments.
-  cur_arg += ARG2_OFFSET_IN_WORDS;
-  while (param_index < (num_params - 1)) {
-    Class* param_type = param_types->Get(param_index);
-    Object* obj;
-    if (!param_type->IsPrimitive()) {
-      obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize)));
-    } else {
-      JValue val = *reinterpret_cast<JValue*>(stack_args + (cur_arg * kPointerSize));
-      BoxPrimitive(param_type->GetPrimitiveType(), val);
-      if (self->IsExceptionPending()) {
-        return;
-      }
-      obj = val.GetL();
-    }
-    args->Set(param_index, obj);
-    cur_arg = cur_arg + (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble() ? 2 : 1);
-    param_index++;
-  }
-  // Get the InvocationHandler method and the field that holds it within the Proxy object.
-  DCHECK(env->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy));
-  jobject inv_hand = env->GetObjectField(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy_h);
-  // Call InvocationHandler.invoke.
-  jobject result =
-      env->CallObjectMethodA(inv_hand, WellKnownClasses::java_lang_reflect_InvocationHandler_invoke,
-                             args_jobj);
-  // Place result in stack args.
-  if (!self->IsExceptionPending()) {
-    Object* result_ref = self->DecodeJObject(result);
-    if (result_ref != NULL) {
-      JValue result_unboxed;
-      bool unboxed_okay = UnboxPrimitiveForResult(result_ref, proxy_mh.GetReturnType(),
-                                                  result_unboxed);
-      if (!unboxed_okay) {
-        self->ClearException();
-        self->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
-                                 "Couldn't convert result of type %s to %s",
-                                 PrettyTypeOf(result_ref).c_str(),
-                                 PrettyDescriptor(proxy_mh.GetReturnType()).c_str());
-        return;
-      }
-      *reinterpret_cast<JValue*>(stack_args) = result_unboxed;
-    } else {
-      *reinterpret_cast<jobject*>(stack_args) = NULL;
-    }
-  } else {
-    // In the case of checked exceptions that aren't declared, the exception must be wrapped by
-    // a UndeclaredThrowableException.
-    Throwable* exception = self->GetException();
-    if (exception->IsCheckedException()) {
-      SynthesizedProxyClass* proxy_class =
-          down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
-      int throws_index = -1;
-      size_t num_virt_methods = proxy_class->NumVirtualMethods();
-      for (size_t i = 0; i < num_virt_methods; i++) {
-        if (proxy_class->GetVirtualMethod(i) == proxy_method) {
-          throws_index = i;
-          break;
-        }
-      }
-      CHECK_NE(throws_index, -1);
-      ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index);
-      Class* exception_class = exception->GetClass();
-      bool declares_exception = false;
-      for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
-        Class* declared_exception = declared_exceptions->Get(i);
-        declares_exception = declared_exception->IsAssignableFrom(exception_class);
-      }
-      if (!declares_exception) {
-        self->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", NULL);
-      }
-    }
-  }
+  jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method);
+
+  // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code
+  // that performs allocations.
+  self->EndAssertNoThreadSuspension(old_cause);
+  JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
+                                               rcvr_jobj, interface_method_jobj, args);
+  return result.GetJ();
 }
 
 }  // namespace art
diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S
index 379fcce..f2f9fa4 100644
--- a/src/oat/runtime/x86/runtime_support_x86.S
+++ b/src/oat/runtime/x86/runtime_support_x86.S
@@ -738,15 +738,14 @@
 
 DEFINE_FUNCTION art_proxy_invoke_handler
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame
-    lea 8(%esp), %ebx             // pointer to r2/r3/LR/caller's Method**/out-args as second arg
-    pushl %ebx                    // pass args
+    pushl %esp                    // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     pushl %ecx                    // pass receiver
     pushl %eax                    // pass proxy method
-    call SYMBOL(artProxyInvokeHandler)     // (proxy method, receiver, Thread*, args...)
-    mov 24(%esp), %eax            // get ret0 which was written into r2 on the stack
-    mov 28(%esp), %edx            // get ret1 which was written into r3 on the stack
-    movsd 24(%esp), %xmm0         // get ret0/ret1 from stack for floating point
+    call SYMBOL(artProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    movd %eax, %xmm0              // place return value also into floating point return value
+    movd %edx, %xmm1
+    punpckldq %xmm1, %xmm0
     addl LITERAL(44), %esp        // pop arguments
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 
diff --git a/src/object_utils.h b/src/object_utils.h
index dd5ff05..068dd66 100644
--- a/src/object_utils.h
+++ b/src/object_utils.h
@@ -566,8 +566,7 @@
     return method_->GetDeclaringClass()->GetClassLoader();
   }
 
-  bool IsStatic()
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  bool IsStatic() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return method_->IsStatic();
   }
 
@@ -581,28 +580,26 @@
     return (IsStatic() ? 0 : 1) + GetShortyLength() - 1;
   }
 
-  // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods
-  bool IsParamALongOrDouble(size_t param)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Get the primitive type associated with the given parameter.
+  Primitive::Type GetParamPrimitiveType(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     CHECK_LT(param, NumArgs());
     if (IsStatic()) {
       param++;  // 0th argument must skip return value at start of the shorty
     } else if (param == 0) {
-      return false;  // this argument
+      return Primitive::kPrimNot;
     }
-    char ch = GetShorty()[param];
-    return (ch == 'J' || ch == 'D');
+    return Primitive::GetType(GetShorty()[param]);
   }
 
-  // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods
+  // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods.
+  bool IsParamALongOrDouble(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Primitive::Type type = GetParamPrimitiveType(param);
+    return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
+  }
+
+  // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods.
   bool IsParamAReference(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    CHECK_LT(param, NumArgs());
-    if (IsStatic()) {
-      param++;  // 0th argument must skip return value at start of the shorty
-    } else if (param == 0) {
-      return true;  // this argument
-    }
-    return GetShorty()[param] == 'L';  // An array also has a shorty character of 'L' (not '[')
+    return GetParamPrimitiveType(param) == Primitive::kPrimNot;
   }
 
   bool HasSameNameAndSignature(MethodHelper* other)
diff --git a/src/reflection.cc b/src/reflection.cc
index c793d2c..1ffad3f 100644
--- a/src/reflection.cc
+++ b/src/reflection.cc
@@ -90,8 +90,7 @@
   }
 
   // Box if necessary and return.
-  BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(), value);
-  return soa.AddLocalReference<jobject>(value.GetL());
+  return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(), value));
 }
 
 bool VerifyObjectInClass(Object* o, Class* c) {
@@ -195,9 +194,9 @@
   return false;
 }
 
-void BoxPrimitive(Primitive::Type src_class, JValue& value) {
+Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) {
   if (src_class == Primitive::kPrimNot) {
-    return;
+    return value.GetL();
   }
 
   jmethodID m = NULL;
@@ -228,8 +227,7 @@
     break;
   case Primitive::kPrimVoid:
     // There's no such thing as a void field, and void methods invoked via reflection return null.
-    value.SetL(NULL);
-    return;
+    return NULL;
   default:
     LOG(FATAL) << static_cast<int>(src_class);
   }
@@ -239,7 +237,9 @@
     CHECK_EQ(soa.Self()->GetState(), kRunnable);
   }
   JValue args[1] = { value };
-  soa.DecodeMethod(m)->Invoke(soa.Self(), NULL, args, &value);
+  JValue result;
+  soa.DecodeMethod(m)->Invoke(soa.Self(), NULL, args, &result);
+  return result.GetL();
 }
 
 static std::string UnboxingFailureKind(AbstractMethod* m, int index, Field* f)
diff --git a/src/reflection.h b/src/reflection.h
index b61acda..601543f 100644
--- a/src/reflection.h
+++ b/src/reflection.h
@@ -29,10 +29,10 @@
 class Object;
 class ScopedObjectAccess;
 
-void BoxPrimitive(Primitive::Type src_class, JValue& value)
+Object* BoxPrimitive(Primitive::Type src_class, const JValue& value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-bool UnboxPrimitiveForArgument(Object* o, Class* dst_class, JValue& unboxed_value, AbstractMethod* m,
-                               size_t index)
+bool UnboxPrimitiveForArgument(Object* o, Class* dst_class, JValue& unboxed_value,
+                               AbstractMethod* m, size_t index)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 bool UnboxPrimitiveForField(Object* o, Class* dst_class, JValue& unboxed_value, Field* f)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 856e877..b276917 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -16,6 +16,8 @@
 
 #include "runtime_support.h"
 
+#include "reflection.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
 #include "well_known_classes.h"
 
@@ -372,4 +374,103 @@
   self->ResetDefaultStackEnd();  // Return to default stack size.
 }
 
+JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char* shorty,
+                                    jobject rcvr_jobj, jobject interface_method_jobj,
+                                    std::vector<jvalue>& args) {
+  DCHECK(soa.Env()->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy));
+
+  // Build argument array possibly triggering GC.
+  soa.Self()->AssertThreadSuspensionIsAllowable();
+  jobjectArray args_jobj = NULL;
+  const JValue zero;
+  if (args.size() > 0) {
+    args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, NULL);
+    if (args_jobj == NULL) {
+      CHECK(soa.Self()->IsExceptionPending());
+      return zero;
+    }
+    for (size_t i = 0; i < args.size(); ++i) {
+      if (shorty[i + 1] == 'L') {
+        jobject val = args.at(i).l;
+        soa.Env()->SetObjectArrayElement(args_jobj, i, val);
+      } else {
+        JValue jv;
+        jv.SetJ(args.at(i).j);
+        Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
+        if (val == NULL) {
+          CHECK(soa.Self()->IsExceptionPending());
+          return zero;
+        }
+        soa.Decode<ObjectArray<Object>* >(args_jobj)->Set(i, val);
+      }
+    }
+  }
+
+  // Call InvocationHandler.invoke(Object proxy, Method method, Object[] args).
+  jobject inv_hand = soa.Env()->GetObjectField(rcvr_jobj,
+                                               WellKnownClasses::java_lang_reflect_Proxy_h);
+  jvalue invocation_args[3];
+  invocation_args[0].l = rcvr_jobj;
+  invocation_args[1].l = interface_method_jobj;
+  invocation_args[2].l = args_jobj;
+  jobject result =
+      soa.Env()->CallObjectMethodA(inv_hand,
+                                   WellKnownClasses::java_lang_reflect_InvocationHandler_invoke,
+                                   invocation_args);
+
+  // Unbox result and handle error conditions.
+  if (!soa.Self()->IsExceptionPending()) {
+    if (shorty[0] == 'V' || result == NULL) {
+      // Do nothing.
+      return zero;
+    } else {
+      JValue result_unboxed;
+      MethodHelper mh(soa.Decode<AbstractMethod*>(interface_method_jobj));
+      Class* result_type = mh.GetReturnType();
+      Object* result_ref = soa.Decode<Object*>(result);
+      bool unboxed_okay = UnboxPrimitiveForResult(result_ref, result_type, result_unboxed);
+      if (!unboxed_okay) {
+        soa.Self()->ThrowNewWrappedException("Ljava/lang/ClassCastException;",
+                                             StringPrintf("Couldn't convert result of type %s to %s",
+                                                          PrettyTypeOf(result_ref).c_str(),
+                                                          PrettyDescriptor(result_type).c_str()
+                                             ).c_str());
+      }
+      return result_unboxed;
+    }
+  } else {
+    // In the case of checked exceptions that aren't declared, the exception must be wrapped by
+    // a UndeclaredThrowableException.
+    Throwable* exception = soa.Self()->GetException();
+    if (exception->IsCheckedException()) {
+      Object* rcvr = soa.Decode<Object*>(rcvr_jobj);
+      SynthesizedProxyClass* proxy_class = down_cast<SynthesizedProxyClass*>(rcvr->GetClass());
+      AbstractMethod* interface_method = soa.Decode<AbstractMethod*>(interface_method_jobj);
+      AbstractMethod* proxy_method =
+          rcvr->GetClass()->FindVirtualMethodForInterface(interface_method);
+      int throws_index = -1;
+      size_t num_virt_methods = proxy_class->NumVirtualMethods();
+      for (size_t i = 0; i < num_virt_methods; i++) {
+        if (proxy_class->GetVirtualMethod(i) == proxy_method) {
+          throws_index = i;
+          break;
+        }
+      }
+      CHECK_NE(throws_index, -1);
+      ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index);
+      Class* exception_class = exception->GetClass();
+      bool declares_exception = false;
+      for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
+        Class* declared_exception = declared_exceptions->Get(i);
+        declares_exception = declared_exception->IsAssignableFrom(exception_class);
+      }
+      if (!declares_exception) {
+        soa.Self()->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;",
+                                             NULL);
+      }
+    }
+    return zero;
+  }
+}
+
 }  // namespace art
diff --git a/src/runtime_support.h b/src/runtime_support.h
index adeedb7..1c8d174 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -28,6 +28,7 @@
 #include "thread.h"
 #include "verifier/method_verifier.h"
 
+extern "C" void art_interpreter_invoke_handler();
 extern "C" void art_proxy_invoke_handler();
 extern "C" void art_work_around_app_jni_bugs();
 
@@ -282,8 +283,7 @@
   }
 }
 
-static inline void CheckSuspend(Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   for (;;) {
     if (thread->ReadFlag(kCheckpointRequest)) {
       thread->RunCheckpointFunction();
@@ -296,6 +296,11 @@
   }
 }
 
+JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char* shorty,
+                                    jobject rcvr_jobj, jobject interface_method_jobj,
+                                    std::vector<jvalue>& args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ;
+
 }  // namespace art
 
 #endif  // ART_SRC_RUNTIME_SUPPORT_H_
diff --git a/src/well_known_classes.cc b/src/well_known_classes.cc
index caf42ee..9752c74 100644
--- a/src/well_known_classes.cc
+++ b/src/well_known_classes.cc
@@ -30,6 +30,7 @@
 jclass WellKnownClasses::java_lang_ClassNotFoundException;
 jclass WellKnownClasses::java_lang_Daemons;
 jclass WellKnownClasses::java_lang_Error;
+jclass WellKnownClasses::java_lang_Object;
 jclass WellKnownClasses::java_lang_reflect_InvocationHandler;
 jclass WellKnownClasses::java_lang_reflect_AbstractMethod;
 jclass WellKnownClasses::java_lang_reflect_Proxy;
@@ -123,6 +124,7 @@
   java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
   java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
   java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
+  java_lang_Object = CacheClass(env, "java/lang/Object");
   java_lang_Error = CacheClass(env, "java/lang/Error");
   java_lang_reflect_InvocationHandler = CacheClass(env, "java/lang/reflect/InvocationHandler");
   java_lang_reflect_AbstractMethod = CacheClass(env, "java/lang/reflect/AbstractMethod");
diff --git a/src/well_known_classes.h b/src/well_known_classes.h
index 3dbf8a1..10afca9 100644
--- a/src/well_known_classes.h
+++ b/src/well_known_classes.h
@@ -42,6 +42,7 @@
   static jclass java_lang_ClassNotFoundException;
   static jclass java_lang_Daemons;
   static jclass java_lang_Error;
+  static jclass java_lang_Object;
   static jclass java_lang_reflect_InvocationHandler;
   static jclass java_lang_reflect_AbstractMethod;
   static jclass java_lang_reflect_Proxy;