Proxy invocation stub.

Also removes unnecessary restoration of callee saves in runtime support
code.

Change-Id: Ie6b91ce0297179947e176e0700ac3fbb90357e1b
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 65b89a3..55aff57 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -17,6 +17,8 @@
 #include "runtime_support.h"
 
 #include "dex_verifier.h"
+#include "reflection.h"
+#include "ScopedLocalRef.h"
 
 namespace art {
 
@@ -39,8 +41,6 @@
 // Return value helper for jobject return types
 extern Object* DecodeJObjectInThread(Thread* thread, jobject obj) {
   if (thread->IsExceptionPending()) {
-    // clear any result if an exception is pending to avoid making a
-    // local reference out of garbage.
     return NULL;
   }
   return thread->DecodeJObject(obj);
@@ -315,8 +315,7 @@
   thread->SetTopOfStack(caller_sp, caller_pc);
   // Start new JNI local reference state
   JNIEnvExt* env = thread->GetJniEnv();
-  uint32_t saved_local_ref_cookie = env->local_ref_cookie;
-  env->local_ref_cookie = env->locals.GetSegmentState();
+  ScopedJniEnvLocalRefState env_state(env);
   // Discover shorty (avoid GCs)
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   const char* shorty = linker->MethodShorty(method_idx, *caller_sp);
@@ -360,10 +359,6 @@
     // We got this far, ensure that the declaring class is initialized
     linker->EnsureInitialized(called->GetDeclaringClass(), true);
   }
-  // Restore JNI env state
-  env->locals.SetSegmentState(env->local_ref_cookie);
-  env->local_ref_cookie = saved_local_ref_cookie;
-
   void* code;
   if (thread->IsExceptionPending()) {
     // Something went wrong, go into deliver exception with the pending exception in r0
@@ -782,6 +777,146 @@
   return result;
 }
 
+// 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(Method* proxy_method, Object* receiver,
+                                      byte* stack_args, Thread* self) {
+  // Register the top of the managed stack
+  self->SetTopOfStack(reinterpret_cast<Method**>(stack_args + 8), 0);
+  // TODO: ARM specific
+  DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), 48u);
+  // Start new JNI local reference state
+  JNIEnvExt* env = self->GetJniEnv();
+  ScopedJniEnvLocalRefState env_state(env);
+  // Create local ref. copies of proxy method and the receiver
+  jobject rcvr_jobj = AddLocalReference<jobject>(env, receiver);
+  jobject proxy_method_jobj = AddLocalReference<jobject>(env, proxy_method);
+
+  // Place incoming objects in local reference table, replacing original Object* with jobject
+  size_t num_args = proxy_method->NumArgs();
+  if (num_args > 1) {
+    if (proxy_method->IsParamAReference(1)) {
+      Object* obj = *reinterpret_cast<Object**>(stack_args);  // reference from r1
+      jobject jobj = AddLocalReference<jobject>(env, obj);
+      *reinterpret_cast<jobject*>(stack_args) = jobj;
+    }
+    if (num_args > 2) {
+      if (proxy_method->IsParamAReference(2)) {
+        Object* obj = *reinterpret_cast<Object**>(stack_args + kPointerSize);  // reference from r2
+        jobject jobj = AddLocalReference<jobject>(env, obj);
+        *reinterpret_cast<jobject*>(stack_args + kPointerSize) = jobj;
+      }
+      // Possible out arguments are 7 words above the stack args:
+      // r2, r3, LR, Method*, r1 (spill), r2 (spill), r3 (spill)
+      size_t offset = 7 * kPointerSize;
+      for (size_t i = 3; i < num_args; i++) {
+        if (proxy_method->IsParamAReference(i)) {
+          // reference from caller's stack arguments
+          Object* obj = *reinterpret_cast<Object**>(stack_args + offset);
+          jobject jobj = AddLocalReference<jobject>(env, obj);
+          *reinterpret_cast<jobject*>(stack_args + offset) = jobj;
+        } else if (proxy_method->IsParamALongOrDouble(i)) {
+          offset += kPointerSize;
+        }
+        offset += kPointerSize;
+      }
+    }
+  }
+  // Create args array
+  ObjectArray<Object>* args =
+      Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_args - 1);
+  if(args == NULL) {
+    CHECK(self->IsExceptionPending());
+    return;
+  }
+  // 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_jobj[2].l = AddLocalReference<jobjectArray>(env, args);
+  // Box arguments
+  ObjectArray<Class>* param_types = proxy_method->GetJavaParameterTypes();
+  if (num_args > 1) {
+    CHECK(param_types != NULL);
+    Object* obj;
+    // Argument from r2
+    Class* param_type = param_types->Get(0);
+    if (!param_type->IsPrimitive()) {
+      obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args));
+    } else {
+      JValue val = *reinterpret_cast<JValue*>(stack_args);
+      BoxPrimitive(env, param_type, val);
+      if (self->IsExceptionPending()) {
+        return;
+      }
+      obj = val.l;
+    }
+    args->Set(0, obj);
+    if (num_args > 2) {
+      // Argument from r3
+      param_type = param_types->Get(1);
+      if (!param_type->IsPrimitive()) {
+        obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args + kPointerSize));
+      } else {
+        JValue val = *reinterpret_cast<JValue*>(stack_args + kPointerSize);
+        BoxPrimitive(env, param_type, val);
+        if (self->IsExceptionPending()) {
+          return;
+        }
+        obj = val.l;
+      }
+      args->Set(1, obj);
+      // Arguments are on the stack, again offset to out arguments is 7 words
+      size_t offset = 7 * kPointerSize;
+      for (size_t i = 3; i < num_args && !self->IsExceptionPending(); i++) {
+        param_type = param_types->Get(i - 1);
+        if (proxy_method->IsParamAReference(i)) {
+          obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args + offset));
+        } else {
+          JValue val = *reinterpret_cast<JValue*>(stack_args + offset);
+          BoxPrimitive(env, param_type, val);
+          if (self->IsExceptionPending()) {
+            return;
+          }
+          obj = val.l;
+          if (proxy_method->IsParamALongOrDouble(i)) {
+            offset += kPointerSize;
+          }
+        }
+        args->Set(i - 1, obj);
+        offset += kPointerSize;
+      }
+    }
+  }
+  // Get the InvocationHandler method and the field that holds it within the Proxy object
+  static jmethodID inv_hand_invoke_mid = NULL;
+  static jfieldID proxy_inv_hand_fid = NULL;
+  if (proxy_inv_hand_fid == NULL) {
+    ScopedLocalRef<jclass> proxy(env, env->FindClass("java.lang.reflect.Proxy"));
+    proxy_inv_hand_fid = env->GetFieldID(proxy.get(), "h", "Ljava/lang/reflect/InvocationHandler;");
+    ScopedLocalRef<jclass> inv_hand_class(env, env->FindClass("java.lang.reflect.InvocationHandler"));
+    inv_hand_invoke_mid = env->GetMethodID(inv_hand_class.get(), "invoke",
+        "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+  }
+  DCHECK(env->IsInstanceOf(rcvr_jobj, env->FindClass("java.lang.reflect.Proxy")));
+  jobject inv_hand = env->GetObjectField(rcvr_jobj, proxy_inv_hand_fid);
+  // Call InvocationHandler.invoke
+  jobject result = env->CallObjectMethodA(inv_hand, inv_hand_invoke_mid, args_jobj);
+  // Place result in stack args
+  if (!self->IsExceptionPending()) {
+    Object* result_ref = self->DecodeJObject(result);
+    if (result_ref != NULL) {
+      JValue result_unboxed;
+      UnboxPrimitive(env, result_ref, proxy_method->GetReturnType(), result_unboxed);
+      *reinterpret_cast<JValue*>(stack_args) = result_unboxed;
+    } else {
+      *reinterpret_cast<jobject*>(stack_args) = NULL;
+    }
+  }
+}
+
 /*
  * Float/double conversion requires clamping to min and max of integer form.  If
  * target doesn't support this normally, use these.