Refactor reflective method invocation.

Move invocation code out of JNI internal into reflection, including ArgArray
code. Make reflective invocation use the ArgArray to build arguments rather
than allocating a jvalue[] and unboxing arguments into that.
Move reflection part of jni_internal_test into reflection_test.
Make greater use of fast JNI.

Change-Id: Ib381372df5f9a83679e30e7275de24fa0e6b1057
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 0bfa70f..4310557 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -19,7 +19,6 @@
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
-#include "invoke_arg_array_builder.h"
 #include "jni_internal.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -29,12 +28,440 @@
 #include "mirror/object_array-inl.h"
 #include "object_utils.h"
 #include "scoped_thread_state_change.h"
+#include "stack.h"
 #include "well_known_classes.h"
 
 namespace art {
 
-jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject javaReceiver,
-                     jobject javaArgs) {
+class ArgArray {
+ public:
+  explicit ArgArray(const char* shorty, uint32_t shorty_len)
+      : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) {
+    size_t num_slots = shorty_len + 1;  // +1 in case of receiver.
+    if (LIKELY((num_slots * 2) < kSmallArgArraySize)) {
+      // We can trivially use the small arg array.
+      arg_array_ = small_arg_array_;
+    } else {
+      // Analyze shorty to see if we need the large arg array.
+      for (size_t i = 1; i < shorty_len; ++i) {
+        char c = shorty[i];
+        if (c == 'J' || c == 'D') {
+          num_slots++;
+        }
+      }
+      if (num_slots <= kSmallArgArraySize) {
+        arg_array_ = small_arg_array_;
+      } else {
+        large_arg_array_.reset(new uint32_t[num_slots]);
+        arg_array_ = large_arg_array_.get();
+      }
+    }
+  }
+
+  uint32_t* GetArray() {
+    return arg_array_;
+  }
+
+  uint32_t GetNumBytes() {
+    return num_bytes_;
+  }
+
+  void Append(uint32_t value) {
+    arg_array_[num_bytes_ / 4] = value;
+    num_bytes_ += 4;
+  }
+
+  void Append(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Append(StackReference<mirror::Object>::FromMirrorPtr(obj).AsVRegValue());
+  }
+
+  void AppendWide(uint64_t value) {
+    // For ARM and MIPS portable, align wide values to 8 bytes (ArgArray starts at offset of 4).
+#if defined(ART_USE_PORTABLE_COMPILER) && (defined(__arm__) || defined(__mips__))
+    if (num_bytes_ % 8 == 0) {
+      num_bytes_ += 4;
+    }
+#endif
+    arg_array_[num_bytes_ / 4] = value;
+    arg_array_[(num_bytes_ / 4) + 1] = value >> 32;
+    num_bytes_ += 8;
+  }
+
+  void AppendFloat(float value) {
+    jvalue jv;
+    jv.f = value;
+    Append(jv.i);
+  }
+
+  void AppendDouble(double value) {
+    jvalue jv;
+    jv.d = value;
+    AppendWide(jv.j);
+  }
+
+  void BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver, va_list ap)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Set receiver if non-null (method is not static)
+    if (receiver != nullptr) {
+      Append(receiver);
+    }
+    for (size_t i = 1; i < shorty_len_; ++i) {
+      switch (shorty_[i]) {
+        case 'Z':
+        case 'B':
+        case 'C':
+        case 'S':
+        case 'I':
+          Append(va_arg(ap, jint));
+          break;
+        case 'F':
+          AppendFloat(va_arg(ap, jdouble));
+          break;
+        case 'L':
+          Append(soa.Decode<mirror::Object*>(va_arg(ap, jobject)));
+          break;
+        case 'D':
+          AppendDouble(va_arg(ap, jdouble));
+          break;
+        case 'J':
+          AppendWide(va_arg(ap, jlong));
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+    }
+  }
+
+  void BuildArgArray(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver,
+                     jvalue* args)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Set receiver if non-null (method is not static)
+    if (receiver != nullptr) {
+      Append(receiver);
+    }
+    for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
+      switch (shorty_[i]) {
+        case 'Z':
+          Append(args[args_offset].z);
+          break;
+        case 'B':
+          Append(args[args_offset].b);
+          break;
+        case 'C':
+          Append(args[args_offset].c);
+          break;
+        case 'S':
+          Append(args[args_offset].s);
+          break;
+        case 'I':
+        case 'F':
+          Append(args[args_offset].i);
+          break;
+        case 'L':
+          Append(soa.Decode<mirror::Object*>(args[args_offset].l));
+          break;
+        case 'D':
+        case 'J':
+          AppendWide(args[args_offset].j);
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+    }
+  }
+
+  void BuildArgArrayFromFrame(ShadowFrame* shadow_frame, uint32_t arg_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Set receiver if non-null (method is not static)
+    size_t cur_arg = arg_offset;
+    if (!shadow_frame->GetMethod()->IsStatic()) {
+      Append(shadow_frame->GetVReg(cur_arg));
+      cur_arg++;
+    }
+    for (size_t i = 1; i < shorty_len_; ++i) {
+      switch (shorty_[i]) {
+        case 'Z':
+        case 'B':
+        case 'C':
+        case 'S':
+        case 'I':
+        case 'F':
+        case 'L':
+          Append(shadow_frame->GetVReg(cur_arg));
+          cur_arg++;
+          break;
+        case 'D':
+        case 'J':
+          AppendWide(shadow_frame->GetVRegLong(cur_arg));
+          cur_arg++;
+          cur_arg++;
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+    }
+  }
+
+  static void ThrowIllegalPrimitiveArgumentException(const char* expected,
+                                                     const StringPiece& found_descriptor)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ThrowIllegalArgumentException(nullptr,
+        StringPrintf("Invalid primitive conversion from %s to %s", expected,
+                     PrettyDescriptor(found_descriptor.as_string()).c_str()).c_str());
+  }
+
+  bool BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver,
+                     mirror::ObjectArray<mirror::Object>* args, MethodHelper& mh)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    const DexFile::TypeList* classes = mh.GetParameterTypeList();
+    // Set receiver if non-null (method is not static)
+    if (receiver != nullptr) {
+      Append(receiver);
+    }
+    for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
+      mirror::Object* arg = args->Get(args_offset);
+      if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
+        mirror::Class* dst_class =
+            mh.GetClassFromTypeIdx(classes->GetTypeItem(args_offset).type_idx_);
+        if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
+          ThrowIllegalArgumentException(nullptr,
+              StringPrintf("method %s argument %d has type %s, got %s",
+                  PrettyMethod(mh.GetMethod(), false).c_str(),
+                  args_offset + 1,  // Humans don't count from 0.
+                  PrettyDescriptor(dst_class).c_str(),
+                  PrettyTypeOf(arg).c_str()).c_str());
+          return false;
+        }
+      }
+
+#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
+          const StringPiece src_descriptor(arg != nullptr \
+              ? ClassHelper(arg->GetClass<>()).GetDescriptor() \
+              : "null"); \
+          if (LIKELY(src_descriptor == match_descriptor)) { \
+            mirror::ArtField* primitive_field = arg->GetClass()->GetIFields()->Get(0); \
+            append(primitive_field-> get_fn(arg));
+
+#define DO_ARG(match_descriptor, get_fn, append) \
+          } else if (LIKELY(src_descriptor == match_descriptor)) { \
+            mirror::ArtField* primitive_field = arg->GetClass()->GetIFields()->Get(0); \
+            append(primitive_field-> get_fn(arg));
+
+#define DO_FAIL(expected) \
+          } else { \
+            if (arg->GetClass<>()->IsPrimitive()) { \
+              ThrowIllegalPrimitiveArgumentException(expected, src_descriptor); \
+            } else { \
+              ThrowIllegalArgumentException(nullptr, \
+                  StringPrintf("method %s argument %d has type %s, got %s", \
+                      PrettyMethod(mh.GetMethod(), false).c_str(), \
+                      args_offset + 1, \
+                      expected, \
+                      PrettyTypeOf(arg).c_str()).c_str()); \
+            } \
+            return false; \
+          } }
+
+      switch (shorty_[i]) {
+        case 'L':
+          Append(arg);
+          break;
+        case 'Z':
+          DO_FIRST_ARG("Ljava/lang/Boolean;", GetBoolean, Append)
+          DO_FAIL("boolean")
+          break;
+        case 'B':
+          DO_FIRST_ARG("Ljava/lang/Byte;", GetByte, Append)
+          DO_FAIL("byte")
+          break;
+        case 'C':
+          DO_FIRST_ARG("Ljava/lang/Character;", GetChar, Append)
+          DO_FAIL("char")
+          break;
+        case 'S':
+          DO_FIRST_ARG("Ljava/lang/Short;", GetShort, Append)
+          DO_ARG("Ljava/lang/Byte;", GetByte, Append)
+          DO_FAIL("short")
+          break;
+        case 'I':
+          DO_FIRST_ARG("Ljava/lang/Integer;", GetInt, Append)
+          DO_ARG("Ljava/lang/Character;", GetChar, Append)
+          DO_ARG("Ljava/lang/Short;", GetShort, Append)
+          DO_ARG("Ljava/lang/Byte;", GetByte, Append)
+          DO_FAIL("int")
+          break;
+        case 'J':
+          DO_FIRST_ARG("Ljava/lang/Long;", GetLong, AppendWide)
+          DO_ARG("Ljava/lang/Integer;", GetInt, AppendWide)
+          DO_ARG("Ljava/lang/Character;", GetChar, AppendWide)
+          DO_ARG("Ljava/lang/Short;", GetShort, AppendWide)
+          DO_ARG("Ljava/lang/Byte;", GetByte, AppendWide)
+          DO_FAIL("long")
+          break;
+        case 'F':
+          DO_FIRST_ARG("Ljava/lang/Float;", GetFloat, AppendFloat)
+          DO_ARG("Ljava/lang/Long;", GetLong, AppendFloat)
+          DO_ARG("Ljava/lang/Integer;", GetInt, AppendFloat)
+          DO_ARG("Ljava/lang/Character;", GetChar, AppendFloat)
+          DO_ARG("Ljava/lang/Short;", GetShort, AppendFloat)
+          DO_ARG("Ljava/lang/Byte;", GetByte, AppendFloat)
+          DO_FAIL("float")
+          break;
+        case 'D':
+          DO_FIRST_ARG("Ljava/lang/Double;", GetDouble, AppendDouble)
+          DO_ARG("Ljava/lang/Float;", GetFloat, AppendDouble)
+          DO_ARG("Ljava/lang/Long;", GetLong, AppendDouble)
+          DO_ARG("Ljava/lang/Integer;", GetInt, AppendDouble)
+          DO_ARG("Ljava/lang/Character;", GetChar, AppendDouble)
+          DO_ARG("Ljava/lang/Short;", GetShort, AppendDouble)
+          DO_ARG("Ljava/lang/Byte;", GetByte, AppendDouble)
+          DO_FAIL("double")
+          break;
+#ifndef NDEBUG
+        default:
+          LOG(FATAL) << "Unexpected shorty character: " << shorty_[i];
+#endif
+      }
+#undef DO_FIRST_ARG
+#undef DO_ARG
+#undef DO_FAIL
+    }
+    return true;
+  }
+
+ private:
+  enum { kSmallArgArraySize = 16 };
+  const char* const shorty_;
+  const uint32_t shorty_len_;
+  uint32_t num_bytes_;
+  uint32_t* arg_array_;
+  uint32_t small_arg_array_[kSmallArgArraySize];
+  UniquePtr<uint32_t[]> large_arg_array_;
+};
+
+static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const DexFile::TypeList* params = MethodHelper(m).GetParameterTypeList();
+  if (params == nullptr) {
+    return;  // No arguments so nothing to check.
+  }
+  uint32_t offset = 0;
+  uint32_t num_params = params->Size();
+  size_t error_count = 0;
+  if (!m->IsStatic()) {
+    offset = 1;
+  }
+  for (uint32_t i = 0; i < num_params; i++) {
+    uint16_t type_idx = params->GetTypeItem(i).type_idx_;
+    mirror::Class* param_type = MethodHelper(m).GetClassFromTypeIdx(type_idx);
+    if (param_type == nullptr) {
+      Thread* self = Thread::Current();
+      CHECK(self->IsExceptionPending());
+      LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
+          << MethodHelper(m).GetTypeDescriptorFromTypeIdx(type_idx) << "\n"
+          << self->GetException(nullptr)->Dump();
+      self->ClearException();
+      ++error_count;
+    } else if (!param_type->IsPrimitive()) {
+      // TODO: check primitives are in range.
+      mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]);
+      if (argument != nullptr && !argument->InstanceOf(param_type)) {
+        LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
+                   << PrettyTypeOf(argument) << " as argument " << (i + 1)
+                   << " to " << PrettyMethod(m);
+        ++error_count;
+      }
+    } else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) {
+      offset++;
+    }
+  }
+  if (error_count > 0) {
+    // TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort
+    // with an argument.
+    JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
+              PrettyMethod(m).c_str());
+  }
+}
+
+static mirror::ArtMethod* FindVirtualMethod(mirror::Object* receiver,
+                                            mirror::ArtMethod* method)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method);
+}
+
+
+static void InvokeWithArgArray(const ScopedObjectAccessUnchecked& soa, mirror::ArtMethod* method,
+                               ArgArray* arg_array, JValue* result, const char* shorty)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  uint32_t* args = arg_array->GetArray();
+  if (UNLIKELY(soa.Env()->check_jni)) {
+    CheckMethodArguments(method, args);
+  }
+  method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
+}
+
+JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj, jmethodID mid, va_list args)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtMethod* method = soa.DecodeMethod(mid);
+  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+JValue InvokeWithJValues(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver,
+                         jmethodID mid, jvalue* args) {
+  mirror::ArtMethod* method = soa.DecodeMethod(mid);
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccess& soa,
+                                           mirror::Object* receiver, jmethodID mid, jvalue* args) {
+  mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccess& soa,
+                                           jobject obj, jmethodID mid, va_list args) {
+  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
+  mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+  MethodHelper mh(method);
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(soa, receiver, args);
+  InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty());
+  return result;
+}
+
+void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset,
+                           MethodHelper& mh, JValue* result) {
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset);
+  shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result,
+                                    mh.GetShorty());
+}
+
+jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod,
+                     jobject javaReceiver, jobject javaArgs) {
   jmethodID mid = soa.Env()->FromReflectedMethod(javaMethod);
   mirror::ArtMethod* m = soa.DecodeMethod(mid);
 
@@ -47,17 +474,16 @@
     declaring_class = sirt_c.get();
   }
 
-  mirror::Object* receiver = NULL;
+  mirror::Object* receiver = nullptr;
   if (!m->IsStatic()) {
     // Check that the receiver is non-null and an instance of the field's declaring class.
     receiver = soa.Decode<mirror::Object*>(javaReceiver);
-    if (!VerifyObjectInClass(receiver, declaring_class)) {
+    if (!VerifyObjectIsClass(receiver, declaring_class)) {
       return NULL;
     }
 
     // Find the actual implementation of the virtual method.
     m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m);
-    mid = soa.EncodeMethod(m);
   }
 
   // Get our arrays of arguments and their types, and check they're the same size.
@@ -65,8 +491,8 @@
       soa.Decode<mirror::ObjectArray<mirror::Object>*>(javaArgs);
   MethodHelper mh(m);
   const DexFile::TypeList* classes = mh.GetParameterTypeList();
-  uint32_t classes_size = classes == NULL ? 0 : classes->Size();
-  uint32_t arg_count = (objects != NULL) ? objects->GetLength() : 0;
+  uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size();
+  uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0;
   if (arg_count != classes_size) {
     ThrowIllegalArgumentException(NULL,
                                   StringPrintf("Wrong number of arguments; expected %d, got %d",
@@ -74,22 +500,15 @@
     return NULL;
   }
 
-  // Translate javaArgs to a jvalue[].
-  UniquePtr<jvalue[]> args(new jvalue[arg_count]);
-  JValue* decoded_args = reinterpret_cast<JValue*>(args.get());
-  for (uint32_t i = 0; i < arg_count; ++i) {
-    mirror::Object* arg = objects->Get(i);
-    mirror::Class* dst_class = mh.GetClassFromTypeIdx(classes->GetTypeItem(i).type_idx_);
-    if (!UnboxPrimitiveForArgument(arg, dst_class, decoded_args[i], m, i)) {
-      return NULL;
-    }
-    if (!dst_class->IsPrimitive()) {
-      args[i].l = soa.AddLocalReference<jobject>(arg);
-    }
+  // Invoke the method.
+  JValue result;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  if (!arg_array.BuildArgArray(soa, receiver, objects, mh)) {
+    CHECK(soa.Self()->IsExceptionPending());
+    return nullptr;
   }
 
-  // Invoke the method.
-  JValue value(InvokeWithJValues(soa, javaReceiver, mid, args.get()));
+  InvokeWithArgArray(soa, m, &arg_array, &result, mh.GetShorty());
 
   // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early.
   if (soa.Self()->IsExceptionPending()) {
@@ -103,10 +522,11 @@
   }
 
   // Box if necessary and return.
-  return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(), value));
+  return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(),
+                                                     result));
 }
 
-bool VerifyObjectInClass(mirror::Object* o, mirror::Class* c) {
+bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
   if (o == NULL) {
     ThrowNullPointerException(NULL, "null receiver");
     return false;
@@ -218,6 +638,10 @@
   if (src_class == Primitive::kPrimNot) {
     return value.GetL();
   }
+  if (src_class == Primitive::kPrimVoid) {
+    // There's no such thing as a void field, and void methods invoked via reflection return null.
+    return nullptr;
+  }
 
   jmethodID m = NULL;
   const char* shorty;
@@ -254,20 +678,15 @@
     m = WellKnownClasses::java_lang_Short_valueOf;
     shorty = "LS";
     break;
-  case Primitive::kPrimVoid:
-    // There's no such thing as a void field, and void methods invoked via reflection return null.
-    return nullptr;
   default:
     LOG(FATAL) << static_cast<int>(src_class);
     shorty = nullptr;
   }
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  if (kIsDebugBuild) {
-    CHECK_EQ(soa.Self()->GetState(), kRunnable);
-  }
+  DCHECK_EQ(soa.Self()->GetState(), kRunnable);
 
-  ArgArray arg_array(nullptr, 0);
+  ArgArray arg_array(shorty, 2);
   JValue result;
   if (src_class == Primitive::kPrimDouble || src_class == Primitive::kPrimLong) {
     arg_array.AppendWide(value.GetJ());