Proxy invocation stub.

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

Change-Id: Ie6b91ce0297179947e176e0700ac3fbb90357e1b
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 332f3a0..e715bdf 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -160,6 +160,26 @@
 const JNINativeInterface* GetCheckJniNativeInterface();
 const JNIInvokeInterface* GetCheckJniInvokeInterface();
 
+// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
+// compiler
+class ScopedJniEnvLocalRefState {
+ public:
+  ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
+    saved_local_ref_cookie_ = env->local_ref_cookie;
+    env->local_ref_cookie = env->locals.GetSegmentState();
+  }
+
+  ~ScopedJniEnvLocalRefState() {
+    env_->locals.SetSegmentState(env_->local_ref_cookie);
+    env_->local_ref_cookie = saved_local_ref_cookie_;
+  }
+
+ private:
+  JNIEnvExt* env_;
+  uint32_t saved_local_ref_cookie_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
+};
+
 }  // namespace art
 
 std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
diff --git a/src/object.h b/src/object.h
index 4b2b54a..4a7d4bb 100644
--- a/src/object.h
+++ b/src/object.h
@@ -949,6 +949,11 @@
     SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_), fp_spill_mask, false);
   }
 
+  ObjectArray<Class>* GetJavaParameterTypes() const {
+    return GetFieldObject<ObjectArray<Class>*>(
+        OFFSET_OF_OBJECT_MEMBER(Method, java_parameter_types_), false);
+  }
+
   // Is this a hand crafted method used for something like describing callee saves?
   bool IsCalleeSaveMethod() const {
     Runtime* runtime = Runtime::Current();
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.
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index f65f703..cc854e9 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -21,7 +21,7 @@
 
     /*
      * Macro that sets up the callee save frame to conform with
-     * Runtime::CreateCalleeSaveMethod(kRefsOnly)
+     * Runtime::CreateCalleeSaveMethod(kRefsOnly). Restoration assumes non-moving GC.
      */
 .macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
     push {r5-r8, r10-r11, lr} @ 7 words of callee saves
@@ -29,27 +29,30 @@
 .endm
 
 .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    add sp, #4
-    pop {r5-r8, r10-r11, lr}  @ 7 words of callee saves
+    ldr lr, [sp, #28]  @ restore lr for return
+    add sp, #32        @ unwind stack
 .endm
 
 .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
-    add sp, #4
-    pop {r5-r8, r10-r11, pc}  @ 7 words of callee saves
+    ldr lr, [sp, #28]  @ restore lr for return
+    add sp, #32        @ unwind stack
+    bx  lr             @ return
 .endm
 
     /*
      * Macro that sets up the callee save frame to conform with
-     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
+     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes non-moving GC.
      */
 .macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
-    sub sp, #8                       @ 2 words of space, bottom word will hold Method*
+    push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
+    sub sp, #8                        @ 2 words of space, bottom word will hold Method*
 .endm
 
 .macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    add sp, #8
-    pop {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
+    ldr  r1, [sp, #8]          @ restore non-callee save r1
+    ldrd r2, [sp, #12]         @ restore non-callee saves r2-r3
+    ldr  lr, [sp, #44]         @ restore lr
+    add  sp, #48               @ rewind sp
 .endm
 
     /*
@@ -64,10 +67,10 @@
 .endm
 
     .global art_do_long_jump
-art_do_long_jump:
     /*
      * On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_
      */
+art_do_long_jump:
     vldm r1, {s0-s31}     @ load all fprs from argument fprs_
     ldr  r2, [r0, #60]    @ r2 = r15 (PC from gprs_ 60=4*15)
     add  r0, r0, #12      @ increment r0 to skip gprs_[0..2] 12=4*3
@@ -453,6 +456,9 @@
 
     .global art_test_suspend
     .extern artTestSuspendFromCode
+    /*
+     * Called by managed code when the value in rSUSPEND has been decremented to 0
+     */
 art_test_suspend:
     ldr    r0, [rSELF, #THREAD_SUSPEND_COUNT_OFFSET]
     mov    rSUSPEND, #SUSPEND_CHECK_INTERVAL  @ reset rSUSPEND to SUSPEND_CHECK_INTERVAL
@@ -464,6 +470,25 @@
     bl     artTestSuspendFromCode             @ (Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 
+    .global art_proxy_invoke_handler
+    .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
+     */
+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...)
+    ldr     r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    ldrd    r0, [sp, #12]          @ load r0/r1 from r2/r3 that were overwritten with the out args
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp     r12, #0                @ success if no exception is pending
+    bxeq    lr                     @ return on success
+    DELIVER_PENDING_EXCEPTION
+
     .global art_shl_long
 art_shl_long:
     /*