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