Avoid useless EncodeArtMethod followed by DecodeArtMethod

In a handful of places we would perform an EncodeArtMethod to call a
reflection invoke function which would immediately Decode the method.
This could cause issues when using opaque-jni-ids:true since the
Encode can cause an OOM exception.

To avoid this (and because Encode and Decode are perfect inverses) we
changed the Invoke* reflection functions to also accept ArtMethod*s
directly and changed callers to use these functions when appropriate.

Test: (with child CL) ./test.py --host --jit --debuggable
Bug: 134162467
Change-Id: Id2f5b0f49a99405e238ce5b61a22ef9245e523a5
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index edd92da..3132951 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -514,9 +514,11 @@
 
 }  // anonymous namespace
 
-JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
-                         va_list args)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+template <>
+JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
+                         jobject obj,
+                         ArtMethod* method,
+                         va_list args) REQUIRES_SHARED(Locks::mutator_lock_) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
   // check has been elided.
@@ -524,8 +526,6 @@
     ThrowStackOverflowError(soa.Self());
     return JValue();
   }
-
-  ArtMethod* method = jni::DecodeArtMethod(mid);
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
@@ -546,7 +546,19 @@
   return result;
 }
 
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+template <>
+JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
+                         jobject obj,
+                         jmethodID mid,
+                         va_list args) REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(mid != nullptr) << "Called with null jmethodID";
+  return InvokeWithVarArgs(soa, obj, jni::DecodeArtMethod(mid), args);
+}
+
+template <>
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
+                         jobject obj,
+                         ArtMethod* method,
                          const jvalue* args) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
@@ -555,8 +567,6 @@
     ThrowStackOverflowError(soa.Self());
     return JValue();
   }
-
-  ArtMethod* method = jni::DecodeArtMethod(mid);
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
@@ -577,8 +587,20 @@
   return result;
 }
 
+template <>
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
+                         jobject obj,
+                         jmethodID mid,
+                         const jvalue* args) {
+  DCHECK(mid != nullptr) << "Called with null jmethodID";
+  return InvokeWithJValues(soa, obj, jni::DecodeArtMethod(mid), args);
+}
+
+template <>
 JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
-                                           jobject obj, jmethodID mid, const jvalue* args) {
+                                           jobject obj,
+                                           ArtMethod* interface_method,
+                                           const jvalue* args) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
   // check has been elided.
@@ -586,9 +608,8 @@
     ThrowStackOverflowError(soa.Self());
     return JValue();
   }
-
   ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
-  ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
+  ArtMethod* method = FindVirtualMethod(receiver, interface_method);
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
@@ -609,8 +630,20 @@
   return result;
 }
 
+template <>
+JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
+                                           jobject obj,
+                                           jmethodID mid,
+                                           const jvalue* args) {
+  DCHECK(mid != nullptr) << "Called with null jmethodID";
+  return InvokeVirtualOrInterfaceWithJValues(soa, obj, jni::DecodeArtMethod(mid), args);
+}
+
+template <>
 JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
-                                           jobject obj, jmethodID mid, va_list args) {
+                                           jobject obj,
+                                           ArtMethod* interface_method,
+                                           va_list args) {
   // We want to make sure that the stack is not within a small distance from the
   // protected region in case we are calling into a leaf function whose stack
   // check has been elided.
@@ -620,7 +653,7 @@
   }
 
   ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
-  ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
+  ArtMethod* method = FindVirtualMethod(receiver, interface_method);
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
@@ -641,6 +674,15 @@
   return result;
 }
 
+template <>
+JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
+                                           jobject obj,
+                                           jmethodID mid,
+                                           va_list args) {
+  DCHECK(mid != nullptr) << "Called with null jmethodID";
+  return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, jni::DecodeArtMethod(mid), args);
+}
+
 jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod,
                      jobject javaReceiver, jobject javaArgs, size_t num_frames) {
   // We want to make sure that the stack is not within a small distance from the
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 574e302..5a2da35 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -60,27 +60,39 @@
                                          JValue* dst)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
+// Invokes the given method (either an ArtMethod or a jmethodID) with direct/static semantics.
+template<typename MethodType>
 JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
                          jobject obj,
-                         jmethodID mid,
+                         MethodType mid,
                          va_list args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
+// Invokes the given method (either an ArtMethod or a jmethodID) with reflection semantics.
+template<typename MethodType>
 JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
                          jobject obj,
-                         jmethodID mid,
+                         MethodType mid,
                          const jvalue* args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
+// Invokes the given method (either an ArtMethod or a jmethodID) with virtual/interface semantics.
+// Note this will perform lookup based on the 'obj' to determine which implementation of the given
+// method should be invoked.
+template<typename MethodType>
 JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
                                            jobject obj,
-                                           jmethodID mid,
+                                           MethodType mid,
                                            const jvalue* args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
+// Invokes the given method (either an ArtMethod or a jmethodID) with virtual/interface semantics.
+// Note this will perform lookup based on the 'obj' to determine which implementation of the given
+// method should be invoked.
+template<typename MethodType>
 JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
                                            jobject obj,
-                                           jmethodID mid,
+                                           MethodType mid,
                                            va_list args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 12fda43..f0d0538 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -772,7 +772,7 @@
 
   JValue result = InvokeWithJValues(soa,
                                     nullptr,
-                                    jni::EncodeArtMethod(getSystemClassLoader),
+                                    getSystemClassLoader,
                                     nullptr);
   JNIEnv* env = soa.Self()->GetJniEnv();
   ScopedLocalRef<jobject> system_class_loader(env, soa.AddLocalReference<jobject>(result.GetL()));
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3e83f65..da76e96 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3273,7 +3273,7 @@
       ++i;
     }
     ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(exception.Get()));
-    InvokeWithJValues(soa, ref.get(), jni::EncodeArtMethod(exception_init_method), jv_args);
+    InvokeWithJValues(soa, ref.get(), exception_init_method, jv_args);
     if (LIKELY(!IsExceptionPending())) {
       SetException(exception.Get());
     }