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/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