Implement reflective method invocation.

Change-Id: Ib3af9d7e00bf226398610b5ac6efbfe3eb2d15e8
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 2031f30..b921281 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -133,6 +133,7 @@
 	src/offsets.cc \
 	src/os_linux.cc \
 	src/reference_table.cc \
+	src/reflection.cc \
 	src/runtime.cc \
 	src/signal_catcher.cc \
 	src/space.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index be1de23..6daa27a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -72,9 +72,10 @@
 
 }
 
-const char* ClassLinker::class_roots_descriptors_[kClassRootsMax] = {
+const char* ClassLinker::class_roots_descriptors_[] = {
   "Ljava/lang/Class;",
   "Ljava/lang/Object;",
+  "[Ljava/lang/Class;",
   "[Ljava/lang/Object;",
   "Ljava/lang/String;",
   "Ljava/lang/reflect/Field;",
@@ -153,6 +154,7 @@
       array_iftable_(NULL),
       init_done_(false),
       intern_table_(intern_table) {
+  CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
 }
 
 void ClassLinker::Init(const std::vector<const DexFile*>& boot_class_path,
@@ -167,6 +169,10 @@
   java_lang_Class->SetClassSize(sizeof(ClassClass));
   // AllocClass(Class*) can now be used
 
+  // Class[] is used for reflection support.
+  Class* class_array_class = AllocClass(java_lang_Class, sizeof(Class));
+  class_array_class->SetComponentType(java_lang_Class);
+
   // java_lang_Object comes next so that object_array_class can be created
   Class* java_lang_Object = AllocClass(java_lang_Class, sizeof(Class));
   CHECK(java_lang_Object != NULL);
@@ -195,6 +201,7 @@
   // Backfill Class descriptors missing until this point
   java_lang_Class->SetDescriptor(intern_table_->InternStrong("Ljava/lang/Class;"));
   java_lang_Object->SetDescriptor(intern_table_->InternStrong("Ljava/lang/Object;"));
+  class_array_class->SetDescriptor(intern_table_->InternStrong("[Ljava/lang/Class;"));
   object_array_class->SetDescriptor(intern_table_->InternStrong("[Ljava/lang/Object;"));
   java_lang_String->SetDescriptor(intern_table_->InternStrong("Ljava/lang/String;"));
   char_array_class->SetDescriptor(intern_table_->InternStrong("[C"));
@@ -204,6 +211,7 @@
   class_roots_ = ObjectArray<Class>::Alloc(object_array_class, kClassRootsMax);
   SetClassRoot(kJavaLangClass, java_lang_Class);
   SetClassRoot(kJavaLangObject, java_lang_Object);
+  SetClassRoot(kClassArrayClass, class_array_class);
   SetClassRoot(kObjectArrayClass, object_array_class);
   SetClassRoot(kCharArrayClass, char_array_class);
   SetClassRoot(kJavaLangString, java_lang_String);
@@ -219,7 +227,7 @@
   SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass("V", Class::kPrimVoid));
 
   // Create array interface entries to populate once we can load system classes
-  array_interfaces_ = AllocObjectArray<Class>(2);
+  array_interfaces_ = AllocClassArray(2);
   array_iftable_ = AllocObjectArray<InterfaceEntry>(2);
 
   // Create int array type for AllocDexCache (done in AppendToBootClassPath)
@@ -303,6 +311,9 @@
   SetClassRoot(kDoubleArrayClass, FindSystemClass("[D"));
   DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
 
+  Class* found_class_array_class = FindSystemClass("[Ljava/lang/Class;");
+  CHECK_EQ(class_array_class, found_class_array_class);
+
   Class* found_object_array_class = FindSystemClass("[Ljava/lang/Object;");
   CHECK_EQ(object_array_class, found_object_array_class);
 
@@ -320,7 +331,9 @@
   array_iftable_->Set(0, AllocInterfaceEntry(array_interfaces_->Get(0)));
   array_iftable_->Set(1, AllocInterfaceEntry(array_interfaces_->Get(1)));
 
-  // Sanity check Object[]'s interfaces
+  // Sanity check Class[] and Object[]'s interfaces
+  CHECK_EQ(java_lang_Cloneable, class_array_class->GetInterface(0));
+  CHECK_EQ(java_io_Serializable, class_array_class->GetInterface(1));
   CHECK_EQ(java_lang_Cloneable, object_array_class->GetInterface(0));
   CHECK_EQ(java_io_Serializable, object_array_class->GetInterface(1));
 
@@ -598,7 +611,7 @@
   DexCache* dex_cache = down_cast<DexCache*>(AllocObjectArray<Object>(DexCache::LengthAsArray()));
   dex_cache->Init(intern_table_->InternStrong(dex_file.GetLocation().c_str()),
                   AllocObjectArray<String>(dex_file.NumStringIds()),
-                  AllocObjectArray<Class>(dex_file.NumTypeIds()),
+                  AllocClassArray(dex_file.NumTypeIds()),
                   AllocObjectArray<Method>(dex_file.NumMethodIds()),
                   AllocObjectArray<Field>(dex_file.NumFieldIds()),
                   AllocCodeAndDirectMethods(dex_file.NumMethodIds()),
@@ -908,7 +921,7 @@
                                  Class* klass) {
   const DexFile::TypeList* list = dex_file.GetInterfacesList(dex_class_def);
   if (list != NULL) {
-    klass->SetInterfaces(AllocObjectArray<Class>(list->Size()));
+    klass->SetInterfaces(AllocClassArray(list->Size()));
     IntArray* interfaces_idx = IntArray::Alloc(list->Size());
     klass->SetInterfacesTypeIdx(interfaces_idx);
     for (size_t i = 0; i < list->Size(); ++i) {
@@ -1099,7 +1112,9 @@
   Class* new_class = NULL;
   if (!init_done_) {
     // Classes that were hand created, ie not by FindSystemClass
-    if (descriptor == "[Ljava/lang/Object;") {
+    if (descriptor == "[Ljava/lang/Class;") {
+      new_class = GetClassRoot(kClassArrayClass);
+    } else if (descriptor == "[Ljava/lang/Object;") {
       new_class = GetClassRoot(kObjectArrayClass);
     } else if (descriptor == "[C") {
       new_class = GetClassRoot(kCharArrayClass);
diff --git a/src/class_linker.h b/src/class_linker.h
index 78be84b..083d641 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -1,4 +1,18 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #ifndef ART_SRC_CLASS_LINKER_H_
 #define ART_SRC_CLASS_LINKER_H_
@@ -155,11 +169,16 @@
   const DexFile& FindDexFile(const DexCache* dex_cache) const;
   DexCache* FindDexCache(const DexFile& dex_file) const;
 
+  // TODO: replace this with multiple methods that allocate the correct managed type.
   template <class T>
   ObjectArray<T>* AllocObjectArray(size_t length) {
     return ObjectArray<T>::Alloc(GetClassRoot(kObjectArrayClass), length);
   }
 
+  ObjectArray<Class>* AllocClassArray(size_t length) {
+    return ObjectArray<Class>::Alloc(GetClassRoot(kClassArrayClass), length);
+  }
+
   ObjectArray<StackTraceElement>* AllocStackTraceElementArray(size_t length);
 
   void VerifyClass(Class* klass);
@@ -295,6 +314,7 @@
   enum ClassRoot {
     kJavaLangClass,
     kJavaLangObject,
+    kClassArrayClass,
     kObjectArrayClass,
     kJavaLangString,
     kJavaLangReflectField,
@@ -345,7 +365,7 @@
     class_roots_->Set(class_root, klass);
   }
 
-  static const char* class_roots_descriptors_[kClassRootsMax];
+  static const char* class_roots_descriptors_[];
 
   const char* GetClassRootDescriptor(ClassRoot class_root) {
     const char* descriptor = class_roots_descriptors_[class_root];
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 9ca171b..69ad910 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -67,53 +67,43 @@
   return AddLocalReference<jclass>(env, Decode<Class*>(env, javaThis)->GetComponentType());
 }
 
+bool MethodMatches(Method* m, String* name, const std::string& signature) {
+  if (!m->GetName()->Equals(name)) {
+    return false;
+  }
+  std::string method_signature = m->GetSignature()->ToModifiedUtf8();
+  if (!StringPiece(method_signature).starts_with(signature)) {
+    return false;
+  }
+  m->InitJavaFields();
+  return true;
+}
+
 jobject Class_getDeclaredConstructorOrMethod(JNIEnv* env, jclass,
-                                             jclass jklass, jstring jname, jobjectArray jsignature) {
-  Class* klass = Decode<Class*>(env, jklass);
-  DCHECK(klass->IsClass());
-  String* name = Decode<String*>(env, jname);
-  DCHECK(name->IsString());
-  Object* signature_obj = Decode<Object*>(env, jsignature);
-  DCHECK(signature_obj->IsArrayInstance());
-  // check that this is a Class[] by checking that component type is Class
-  // foo->GetClass()->GetClass() is an idiom for getting java.lang.Class from an arbitrary object
-  DCHECK(signature_obj->GetClass()->GetComponentType() == signature_obj->GetClass()->GetClass());
-  ObjectArray<Class>* signature = down_cast<ObjectArray<Class>*>(signature_obj);
+    jclass javaClass, jstring javaName, jobjectArray javaSignature) {
+  Class* c = Decode<Class*>(env, javaClass);
+  String* name = Decode<String*>(env, javaName);
+  ObjectArray<Class>* signature_array = Decode<ObjectArray<Class>*>(env, javaSignature);
 
-  std::string name_string = name->ToModifiedUtf8();
-  std::string signature_string;
-  signature_string += "(";
-  for (int i = 0; i < signature->GetLength(); i++) {
-    Class* argument_class = signature->Get(0);
-    if (argument_class == NULL) {
-      UNIMPLEMENTED(FATAL) << "throw null pointer exception?";
-    }
-    signature_string += argument_class->GetDescriptor()->ToModifiedUtf8();
+  std::string signature;
+  signature += "(";
+  for (int i = 0; i < signature_array->GetLength(); i++) {
+    signature += signature_array->Get(i)->GetDescriptor()->ToModifiedUtf8();
   }
-  signature_string += ")";
+  signature += ")";
 
-  for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
-    Method* method = klass->GetVirtualMethod(i);
-    if (!method->GetName()->Equals(name)) {
-      continue;
+  for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
+    Method* m = c->GetVirtualMethod(i);
+    if (MethodMatches(m, name, signature)) {
+      return AddLocalReference<jobject>(env, m);
     }
-    std::string method_signature = method->GetSignature()->ToModifiedUtf8();
-    if (!StringPiece(method_signature).starts_with(signature_string)) {
-      continue;
-    }
-    return AddLocalReference<jobject>(env, method);
   }
 
-  for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
-    Method* method = klass->GetDirectMethod(i);
-    if (!method->GetName()->Equals(name)) {
-      continue;
+  for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
+    Method* m = c->GetDirectMethod(i);
+    if (MethodMatches(m, name, signature)) {
+      return AddLocalReference<jobject>(env, m);
     }
-    std::string method_signature = method->GetSignature()->ToModifiedUtf8();
-    if (!StringPiece(method_signature).starts_with(signature_string)) {
-      continue;
-    }
-    return AddLocalReference<jobject>(env, method);
   }
 
   return NULL;
@@ -326,7 +316,6 @@
   NATIVE_METHOD(Class, getEnclosingConstructor, "()Ljava/lang/reflect/Constructor;"),
   NATIVE_METHOD(Class, getEnclosingMethod, "()Ljava/lang/reflect/Method;"),
   //NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"),
-  //NATIVE_METHOD(Class, getInterfaces, "()[Ljava/lang/Class;"),
   //NATIVE_METHOD(Class, getModifiers, "(Ljava/lang/Class;Z)I"),
   NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
   NATIVE_METHOD(Class, getSuperclass, "()Ljava/lang/Class;"),
diff --git a/src/java_lang_reflect_Field.cc b/src/java_lang_reflect_Field.cc
index c6abe53..5584da3 100644
--- a/src/java_lang_reflect_Field.cc
+++ b/src/java_lang_reflect_Field.cc
@@ -17,6 +17,7 @@
 #include "jni_internal.h"
 #include "class_linker.h"
 #include "object.h"
+#include "reflection.h"
 
 #include "JniConstants.h" // Last to avoid problems with LOG redefinition.
 
@@ -28,240 +29,6 @@
   return Decode<Object*>(env, jfield)->AsField()->GetAccessFlags() & kAccFieldFlagsMask;
 }
 
-// TODO: we'll need this for Method too.
-bool VerifyObjectInClass(JNIEnv* env, Object* o, Class* c) {
-  if (o == NULL) {
-    jniThrowNullPointerException(env, "receiver for non-static field access was null");
-    return false;
-  }
-  if (!o->InstanceOf(c)) {
-    std::string expectedClassName(PrettyDescriptor(c->GetDescriptor()));
-    std::string actualClassName(PrettyTypeOf(o));
-    jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-        "expected receiver of type %s, but got %s",
-        expectedClassName.c_str(), actualClassName.c_str());
-    return false;
-  }
-  return true;
-}
-
-/*
- * Convert primitive, boxed data from "srcPtr" to "dstPtr".
- *
- * Section v2 2.6 lists the various conversions and promotions.  We
- * allow the "widening" and "identity" conversions, but don't allow the
- * "narrowing" conversions.
- *
- * Allowed:
- *  byte to short, int, long, float, double
- *  short to int, long, float double
- *  char to int, long, float, double
- *  int to long, float, double
- *  long to float, double
- *  float to double
- * Values of types byte, char, and short are "internally" widened to int.
- *
- * Returns the width in 32-bit words of the destination primitive, or
- * -1 if the conversion is not allowed.
- */
-bool ConvertPrimitiveValue(Class* src_class, Class* dst_class, const JValue& src, JValue& dst) {
-  Class::PrimitiveType srcType = src_class->GetPrimitiveType();
-  Class::PrimitiveType dstType = dst_class->GetPrimitiveType();
-  switch (dstType) {
-  case Class::kPrimBoolean:
-  case Class::kPrimChar:
-  case Class::kPrimByte:
-    if (srcType == dstType) {
-      dst.i = src.i;
-      return true;
-    }
-    break;
-  case Class::kPrimShort:
-    if (srcType == Class::kPrimByte || srcType == Class::kPrimShort) {
-      dst.i = src.i;
-      return true;
-    }
-    break;
-  case Class::kPrimInt:
-    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
-        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
-      dst.i = src.i;
-      return true;
-    }
-    break;
-  case Class::kPrimLong:
-    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
-        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
-      dst.j = src.i;
-      return true;
-    } else if (srcType == Class::kPrimLong) {
-      dst.j = src.j;
-      return true;
-    }
-    break;
-  case Class::kPrimFloat:
-    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
-        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
-      dst.f = src.i;
-      return true;
-    } else if (srcType == Class::kPrimLong) {
-      dst.f = src.j;
-      return true;
-    } else if (srcType == Class::kPrimFloat) {
-      dst.i = src.i;
-      return true;
-    }
-    break;
-  case Class::kPrimDouble:
-    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
-        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
-      dst.d = src.i;
-      return true;
-    } else if (srcType == Class::kPrimLong) {
-      dst.d = src.j;
-      return true;
-    } else if (srcType == Class::kPrimFloat) {
-      dst.d = src.f;
-      return true;
-    } else if (srcType == Class::kPrimDouble) {
-      dst.j = src.j;
-      return true;
-    }
-    break;
-  default:
-    break;
-  }
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
-      "invalid primitive conversion from %s to %s",
-      PrettyDescriptor(src_class->GetDescriptor()).c_str(),
-      PrettyDescriptor(dst_class->GetDescriptor()).c_str());
-  return false;
-}
-
-bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_value) {
-  if (dst_class->GetPrimitiveType() == Class::kPrimNot) {
-    if (o != NULL && !o->InstanceOf(dst_class)) {
-      jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-          "expected object of type %s, but got %s",
-          PrettyDescriptor(dst_class->GetDescriptor()).c_str(),
-          PrettyTypeOf(o).c_str());
-      return false;
-    }
-    unboxed_value.l = o;
-    return true;
-  } else if (dst_class->GetPrimitiveType() == Class::kPrimVoid) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
-        "can't unbox to void");
-    return false;
-  }
-
-  if (o == NULL) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
-        "null passed for boxed primitive type");
-    return false;
-  }
-
-  JValue boxed_value = { 0 };
-  const String* src_descriptor = o->GetClass()->GetDescriptor();
-  Class* src_class = NULL;
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Field* primitive_field = o->GetClass()->GetIFields()->Get(0);
-  if (src_descriptor->Equals("Ljava/lang/Boolean;")) {
-    src_class = class_linker->FindPrimitiveClass('Z');
-    boxed_value.z = primitive_field->GetBoolean(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Byte;")) {
-    src_class = class_linker->FindPrimitiveClass('B');
-    boxed_value.b = primitive_field->GetByte(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Character;")) {
-    src_class = class_linker->FindPrimitiveClass('C');
-    boxed_value.c = primitive_field->GetChar(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Float;")) {
-    src_class = class_linker->FindPrimitiveClass('F');
-    boxed_value.f = primitive_field->GetFloat(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Double;")) {
-    src_class = class_linker->FindPrimitiveClass('D');
-    boxed_value.d = primitive_field->GetDouble(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Integer;")) {
-    src_class = class_linker->FindPrimitiveClass('I');
-    boxed_value.i = primitive_field->GetInt(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Long;")) {
-    src_class = class_linker->FindPrimitiveClass('J');
-    boxed_value.j = primitive_field->GetLong(o);
-  } else if (src_descriptor->Equals("Ljava/lang/Short;")) {
-    src_class = class_linker->FindPrimitiveClass('S');
-    boxed_value.s = primitive_field->GetShort(o);
-  } else {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
-        "%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str());
-    return false;
-  }
-
-  return ConvertPrimitiveValue(src_class, dst_class, boxed_value, unboxed_value);
-}
-
-Method* gBoolean_valueOf;
-Method* gByte_valueOf;
-Method* gCharacter_valueOf;
-Method* gDouble_valueOf;
-Method* gFloat_valueOf;
-Method* gInteger_valueOf;
-Method* gLong_valueOf;
-Method* gShort_valueOf;
-
-void InitBoxingMethod(JNIEnv* env, Method*& m, jclass c, const char* method_signature) {
-  m = DecodeMethod(env->GetStaticMethodID(c, "valueOf", method_signature));
-}
-
-void BoxPrimitive(JNIEnv* env, Class* src_class, JValue& value) {
-  if (!src_class->IsPrimitive()) {
-    return;
-  }
-
-  Method* m = NULL;
-  UniquePtr<byte[]> args(new byte[8]);
-  memset(&args[0], 0, 8);
-  switch (src_class->GetPrimitiveType()) {
-  case Class::kPrimBoolean:
-    m = gBoolean_valueOf;
-    *reinterpret_cast<uint32_t*>(&args[0]) = value.z;
-    break;
-  case Class::kPrimByte:
-    m = gByte_valueOf;
-    *reinterpret_cast<uint32_t*>(&args[0]) = value.b;
-    break;
-  case Class::kPrimChar:
-    m = gCharacter_valueOf;
-    *reinterpret_cast<uint32_t*>(&args[0]) = value.c;
-    break;
-  case Class::kPrimDouble:
-    m = gDouble_valueOf;
-    *reinterpret_cast<double*>(&args[0]) = value.d;
-    break;
-  case Class::kPrimFloat:
-    m = gFloat_valueOf;
-    *reinterpret_cast<float*>(&args[0]) = value.f;
-    break;
-  case Class::kPrimInt:
-    m = gInteger_valueOf;
-    *reinterpret_cast<uint32_t*>(&args[0]) = value.i;
-    break;
-  case Class::kPrimLong:
-    m = gLong_valueOf;
-    *reinterpret_cast<uint64_t*>(&args[0]) = value.j;
-    break;
-  case Class::kPrimShort:
-    m = gShort_valueOf;
-    *reinterpret_cast<uint32_t*>(&args[0]) = value.s;
-    break;
-  default:
-    LOG(FATAL) << PrettyClass(src_class);
-  }
-
-  Thread* self = Thread::Current();
-  ScopedThreadStateChange tsc(self, Thread::kRunnable);
-  m->Invoke(self, NULL, args.get(), &value);
-}
-
 bool GetFieldValue(Object* o, Field* f, JValue& value, bool allow_references) {
   switch (f->GetType()->GetPrimitiveType()) {
   case Class::kPrimBoolean:
@@ -305,7 +72,7 @@
 }
 
 JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, jclass javaDeclaringClass, jchar targetDescriptor) {
-  Field* f = reinterpret_cast<Field*>(env->FromReflectedField(javaField));
+  Field* f = DecodeField(env->FromReflectedField(javaField));
 
   // Check that the receiver is non-null and an instance of the field's declaring class.
   Object* o = Decode<Object*>(env, javaObj);
@@ -406,7 +173,7 @@
 }
 
 void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, jclass javaDeclaringClass, jchar targetDescriptor, const JValue& new_value) {
-  Field* f = reinterpret_cast<Field*>(env->FromReflectedField(javaField));
+  Field* f = DecodeField(env->FromReflectedField(javaField));
 
   // Check that the receiver is non-null and an instance of the field's declaring class.
   Object* o = Decode<Object*>(env, javaObj);
@@ -478,7 +245,7 @@
 }
 
 void Field_setField(JNIEnv* env, jobject javaField, jobject javaObj, jclass javaDeclaringClass, jclass, jint, jboolean, jobject javaValue) {
-  Field* f = reinterpret_cast<Field*>(env->FromReflectedField(javaField));
+  Field* f = DecodeField(env->FromReflectedField(javaField));
 
   // Unbox the value, if necessary.
   Object* boxed_value = Decode<Object*>(env, javaValue);
@@ -501,7 +268,7 @@
 }
 
 jobject Field_getField(JNIEnv* env, jobject javaField, jobject javaObj, jclass javaDeclaringClass, jclass, jint, jboolean) {
-  Field* f = reinterpret_cast<Field*>(env->FromReflectedField(javaField));
+  Field* f = DecodeField(env->FromReflectedField(javaField));
 
   // Check that the receiver is non-null and an instance of the field's declaring class.
   Object* o = Decode<Object*>(env, javaObj);
@@ -549,14 +316,7 @@
 }  // namespace
 
 void register_java_lang_reflect_Field(JNIEnv* env) {
-  InitBoxingMethod(env, gBoolean_valueOf, JniConstants::booleanClass, "(Z)Ljava/lang/Boolean;");
-  InitBoxingMethod(env, gByte_valueOf, JniConstants::byteClass, "(B)Ljava/lang/Byte;");
-  InitBoxingMethod(env, gCharacter_valueOf, JniConstants::characterClass, "(C)Ljava/lang/Character;");
-  InitBoxingMethod(env, gDouble_valueOf, JniConstants::doubleClass, "(D)Ljava/lang/Double;");
-  InitBoxingMethod(env, gFloat_valueOf, JniConstants::floatClass, "(F)Ljava/lang/Float;");
-  InitBoxingMethod(env, gInteger_valueOf, JniConstants::integerClass, "(I)Ljava/lang/Integer;");
-  InitBoxingMethod(env, gLong_valueOf, JniConstants::longClass, "(J)Ljava/lang/Long;");
-  InitBoxingMethod(env, gShort_valueOf, JniConstants::shortClass, "(S)Ljava/lang/Short;");
+  InitBoxingMethods(env); // TODO: move to Runtime?
   jniRegisterNativeMethods(env, "java/lang/reflect/Field", gMethods, NELEM(gMethods));
 }
 
diff --git a/src/java_lang_reflect_Method.cc b/src/java_lang_reflect_Method.cc
index 188614f..6ddb436 100644
--- a/src/java_lang_reflect_Method.cc
+++ b/src/java_lang_reflect_Method.cc
@@ -17,6 +17,7 @@
 #include "jni_internal.h"
 #include "class_linker.h"
 #include "object.h"
+#include "reflection.h"
 
 #include "JniConstants.h" // Last to avoid problems with LOG redefinition.
 
@@ -24,7 +25,6 @@
 
 namespace {
 
-
 // We move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
 // position, because the callers of this function are trying to convey
 // the "traditional" meaning of the flags to their callers.
@@ -40,9 +40,73 @@
   return FixupMethodFlags(Decode<Object*>(env, jmethod)->AsMethod()->GetAccessFlags());
 }
 
+jobject Method_invokeNative(JNIEnv* env, jobject javaMethod, jobject javaReceiver, jobject javaArgs, jclass javaDeclaringClass, jobject javaParams, jclass, jint, jboolean) {
+  Thread* self = Thread::Current();
+  ScopedThreadStateChange tsc(self, Thread::kRunnable);
+
+  jmethodID mid = env->FromReflectedMethod(javaMethod);
+  Method* m = reinterpret_cast<Method*>(mid);
+  Object* receiver = NULL;
+  if (!m->IsStatic()) {
+    // Check that the receiver is non-null and an instance of the field's declaring class.
+    receiver = Decode<Object*>(env, javaReceiver);
+    Class* declaringClass = Decode<Class*>(env, javaDeclaringClass);
+    if (!VerifyObjectInClass(env, receiver, declaringClass)) {
+      return NULL;
+    }
+
+    // Find the actual implementation of the virtual method.
+    m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m);
+  }
+
+  // Get our arrays of arguments and their types, and check they're the same size.
+  ObjectArray<Object>* objects = Decode<ObjectArray<Object>*>(env, javaArgs);
+  ObjectArray<Class>* classes = Decode<ObjectArray<Class>*>(env, javaParams);
+  int32_t arg_count = (objects != NULL) ? objects->GetLength() : 0;
+  if (arg_count != classes->GetLength()) {
+    self->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+        "wrong number of arguments; expected %d, got %d",
+        classes->GetLength(), arg_count);
+    return NULL;
+  }
+
+  // Translate javaArgs to a jvalue[].
+  UniquePtr<jvalue[]> args(new jvalue[arg_count]);
+  JValue* decoded_args = reinterpret_cast<JValue*>(args.get());
+  for (int32_t i = 0; i < arg_count; ++i) {
+    Object* arg = objects->Get(i);
+    Class* dst_class = classes->Get(i);
+    if (dst_class->IsPrimitive()) {
+      if (!UnboxPrimitive(env, arg, dst_class, decoded_args[i])) {
+        return NULL;
+      }
+    } else {
+      args[i].l = AddLocalReference<jobject>(env, arg);
+    }
+  }
+
+  // Invoke the method.
+  JValue value = InvokeWithJValues(env, javaReceiver, mid, args.get());
+
+  // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early.
+  if (self->IsExceptionPending()) {
+    jthrowable th = env->ExceptionOccurred();
+    env->ExceptionClear();
+    jclass exception_class = env->FindClass("java/lang/reflect/InvocationTargetException");
+    jmethodID mid = env->GetMethodID(exception_class, "<init>", "(Ljava/lang/Throwable;)V");
+    jobject exception_instance = env->NewObject(exception_class, mid, th);
+    env->Throw(reinterpret_cast<jthrowable>(exception_instance));
+    return NULL;
+  }
+
+  // Box if necessary and return.
+  BoxPrimitive(env, m->GetReturnType(), value);
+  return AddLocalReference<jobject>(env, value.l);
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(Method, getMethodModifiers, "(Ljava/lang/Class;Ljava/lang/reflect/AccessibleObject;I)I"),
-  //NATIVE_METHOD(Method, invokeNative, "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;"),
+  NATIVE_METHOD(Method, invokeNative, "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;"),
 };
 
 }  // namespace
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index e196c0c..96b3b34 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -111,6 +111,8 @@
 template ClassLoader* Decode<ClassLoader*>(JNIEnv*, jobject);
 template Object* Decode<Object*>(JNIEnv*, jobject);
 template String* Decode<String*>(JNIEnv*, jobject);
+template ObjectArray<Class>* Decode<ObjectArray<Class>*>(JNIEnv*, jobject);
+template ObjectArray<Object>* Decode<ObjectArray<Object>*>(JNIEnv*, jobject);
 template ObjectArray<StackTraceElement>* Decode<ObjectArray<StackTraceElement>*>(JNIEnv*, jobject);
 
 namespace {
@@ -132,7 +134,8 @@
   return reinterpret_cast<T>(ts.Self()->DecodeJObject(obj));
 }
 
-byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, va_list ap) {
+byte* CreateArgArray(JNIEnv* public_env, Method* method, va_list ap) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
   size_t num_bytes = method->NumArgArrayBytes();
   UniquePtr<byte[]> arg_array(new byte[num_bytes]);
   const String* shorty = method->GetShorty();
@@ -151,7 +154,7 @@
         offset += 4;
         break;
       case 'L': {
-        Object* obj = Decode<Object*>(ts, va_arg(ap, jobject));
+        Object* obj = Decode<Object*>(env, va_arg(ap, jobject));
         *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
         offset += sizeof(Object*);
         break;
@@ -169,7 +172,8 @@
   return arg_array.release();
 }
 
-byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, jvalue* args) {
+byte* CreateArgArray(JNIEnv* public_env, Method* method, jvalue* args) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
   size_t num_bytes = method->NumArgArrayBytes();
   UniquePtr<byte[]> arg_array(new byte[num_bytes]);
   const String* shorty = method->GetShorty();
@@ -188,7 +192,7 @@
         offset += 4;
         break;
       case 'L': {
-        Object* obj = Decode<Object*>(ts, args[i - 1].l);
+        Object* obj = Decode<Object*>(env, args[i - 1].l);
         *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
         offset += sizeof(Object*);
         break;
@@ -206,43 +210,39 @@
   return arg_array.release();
 }
 
-JValue InvokeWithArgArray(ScopedJniThreadState& ts, Object* receiver,
-                          Method* method, byte* args) {
+JValue InvokeWithArgArray(JNIEnv* public_env, Object* receiver, Method* method, byte* args) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
   JValue result;
-  method->Invoke(ts.Self(), receiver, args, &result);
+  method->Invoke(env->self, receiver, args, &result);
   return result;
 }
 
-JValue InvokeWithJValues(ScopedJniThreadState& ts, jobject obj, jmethodID mid, jvalue* args) {
-  Object* receiver = Decode<Object*>(ts, obj);
+JValue InvokeWithVarArgs(JNIEnv* public_env, jobject obj, jmethodID mid, va_list args) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
+  Object* receiver = Decode<Object*>(env, obj);
   Method* method = DecodeMethod(mid);
-  UniquePtr<byte[]> arg_array(CreateArgArray(ts, method, args));
-  return InvokeWithArgArray(ts, receiver, method, arg_array.get());
-}
-
-JValue InvokeWithVarArgs(ScopedJniThreadState& ts, jobject obj, jmethodID mid, va_list args) {
-  Object* receiver = Decode<Object*>(ts, obj);
-  Method* method = DecodeMethod(mid);
-  UniquePtr<byte[]> arg_array(CreateArgArray(ts, method, args));
-  return InvokeWithArgArray(ts, receiver, method, arg_array.get());
+  UniquePtr<byte[]> arg_array(CreateArgArray(env, method, args));
+  return InvokeWithArgArray(env, receiver, method, arg_array.get());
 }
 
 Method* FindVirtualMethod(Object* receiver, Method* method) {
   return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method);
 }
 
-JValue InvokeVirtualOrInterfaceWithJValues(ScopedJniThreadState& ts, jobject obj, jmethodID mid, jvalue* args) {
-  Object* receiver = Decode<Object*>(ts, obj);
+JValue InvokeVirtualOrInterfaceWithJValues(JNIEnv* public_env, jobject obj, jmethodID mid, jvalue* args) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
+  Object* receiver = Decode<Object*>(env, obj);
   Method* method = FindVirtualMethod(receiver, DecodeMethod(mid));
-  UniquePtr<byte[]> arg_array(CreateArgArray(ts, method, args));
-  return InvokeWithArgArray(ts, receiver, method, arg_array.get());
+  UniquePtr<byte[]> arg_array(CreateArgArray(env, method, args));
+  return InvokeWithArgArray(env, receiver, method, arg_array.get());
 }
 
-JValue InvokeVirtualOrInterfaceWithVarArgs(ScopedJniThreadState& ts, jobject obj, jmethodID mid, va_list args) {
-  Object* receiver = Decode<Object*>(ts, obj);
+JValue InvokeVirtualOrInterfaceWithVarArgs(JNIEnv* public_env, jobject obj, jmethodID mid, va_list args) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
+  Object* receiver = Decode<Object*>(env, obj);
   Method* method = FindVirtualMethod(receiver, DecodeMethod(mid));
-  UniquePtr<byte[]> arg_array(CreateArgArray(ts, method, args));
-  return InvokeWithArgArray(ts, receiver, method, arg_array.get());
+  UniquePtr<byte[]> arg_array(CreateArgArray(env, method, args));
+  return InvokeWithArgArray(env, receiver, method, arg_array.get());
 }
 
 // Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
@@ -298,6 +298,8 @@
     return NULL;
   }
 
+  method->InitJavaFields();
+
   return EncodeMethod(method);
 }
 
@@ -606,6 +608,14 @@
   std::map<std::string, SharedLibrary*> libraries_;
 };
 
+JValue InvokeWithJValues(JNIEnv* public_env, jobject obj, jmethodID mid, jvalue* args) {
+  JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
+  Object* receiver = Decode<Object*>(env, obj);
+  Method* method = DecodeMethod(mid);
+  UniquePtr<byte[]> arg_array(CreateArgArray(env, method, args));
+  return InvokeWithArgArray(env, receiver, method, arg_array.get());
+}
+
 class JNI {
  public:
 
@@ -952,20 +962,20 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return AddLocalReference<jobject>(env, result.l);
   }
 
   static jobject CallObjectMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args);
     return AddLocalReference<jobject>(env, result.l);
   }
 
   static jobject CallObjectMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    JValue result = InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args);
+    JValue result = InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args);
     return AddLocalReference<jobject>(env, result.l);
   }
 
@@ -973,170 +983,170 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.z;
   }
 
   static jboolean CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).z;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).z;
   }
 
   static jboolean CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).z;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).z;
   }
 
   static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.b;
   }
 
   static jbyte CallByteMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).b;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).b;
   }
 
   static jbyte CallByteMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).b;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).b;
   }
 
   static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.c;
   }
 
   static jchar CallCharMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).c;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).c;
   }
 
   static jchar CallCharMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).c;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).c;
   }
 
   static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.d;
   }
 
   static jdouble CallDoubleMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).d;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).d;
   }
 
   static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).d;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).d;
   }
 
   static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.f;
   }
 
   static jfloat CallFloatMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).f;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).f;
   }
 
   static jfloat CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).f;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).f;
   }
 
   static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.i;
   }
 
   static jint CallIntMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).i;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).i;
   }
 
   static jint CallIntMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).i;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).i;
   }
 
   static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.j;
   }
 
   static jlong CallLongMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).j;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).j;
   }
 
   static jlong CallLongMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).j;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).j;
   }
 
   static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.s;
   }
 
   static jshort CallShortMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args).s;
+    return InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args).s;
   }
 
   static jshort CallShortMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args).s;
+    return InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args).s;
   }
 
   static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, ap);
     va_end(ap);
   }
 
   static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    InvokeVirtualOrInterfaceWithVarArgs(ts, obj, mid, args);
+    InvokeVirtualOrInterfaceWithVarArgs(env, obj, mid, args);
   }
 
   static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    InvokeVirtualOrInterfaceWithJValues(ts, obj, mid, args);
+    InvokeVirtualOrInterfaceWithJValues(env, obj, mid, args);
   }
 
   static jobject CallNonvirtualObjectMethod(JNIEnv* env,
@@ -1144,7 +1154,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     jobject local_result = AddLocalReference<jobject>(env, result.l);
     va_end(ap);
     return local_result;
@@ -1153,14 +1163,14 @@
   static jobject CallNonvirtualObjectMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, args);
+    JValue result = InvokeWithVarArgs(env, obj, mid, args);
     return AddLocalReference<jobject>(env, result.l);
   }
 
   static jobject CallNonvirtualObjectMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    JValue result = InvokeWithJValues(ts, obj, mid, args);
+    JValue result = InvokeWithJValues(env, obj, mid, args);
     return AddLocalReference<jobject>(env, result.l);
   }
 
@@ -1169,7 +1179,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.z;
   }
@@ -1177,13 +1187,13 @@
   static jboolean CallNonvirtualBooleanMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).z;
+    return InvokeWithVarArgs(env, obj, mid, args).z;
   }
 
   static jboolean CallNonvirtualBooleanMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).z;
+    return InvokeWithJValues(env, obj, mid, args).z;
   }
 
   static jbyte CallNonvirtualByteMethod(JNIEnv* env,
@@ -1191,7 +1201,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.b;
   }
@@ -1199,13 +1209,13 @@
   static jbyte CallNonvirtualByteMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).b;
+    return InvokeWithVarArgs(env, obj, mid, args).b;
   }
 
   static jbyte CallNonvirtualByteMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).b;
+    return InvokeWithJValues(env, obj, mid, args).b;
   }
 
   static jchar CallNonvirtualCharMethod(JNIEnv* env,
@@ -1213,7 +1223,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.c;
   }
@@ -1221,13 +1231,13 @@
   static jchar CallNonvirtualCharMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).c;
+    return InvokeWithVarArgs(env, obj, mid, args).c;
   }
 
   static jchar CallNonvirtualCharMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).c;
+    return InvokeWithJValues(env, obj, mid, args).c;
   }
 
   static jshort CallNonvirtualShortMethod(JNIEnv* env,
@@ -1235,7 +1245,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.s;
   }
@@ -1243,21 +1253,20 @@
   static jshort CallNonvirtualShortMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).s;
+    return InvokeWithVarArgs(env, obj, mid, args).s;
   }
 
   static jshort CallNonvirtualShortMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).s;
+    return InvokeWithJValues(env, obj, mid, args).s;
   }
 
-  static jint CallNonvirtualIntMethod(JNIEnv* env,
-      jobject obj, jclass clazz, jmethodID mid, ...) {
+  static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.i;
   }
@@ -1265,13 +1274,13 @@
   static jint CallNonvirtualIntMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).i;
+    return InvokeWithVarArgs(env, obj, mid, args).i;
   }
 
   static jint CallNonvirtualIntMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).i;
+    return InvokeWithJValues(env, obj, mid, args).i;
   }
 
   static jlong CallNonvirtualLongMethod(JNIEnv* env,
@@ -1279,7 +1288,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.j;
   }
@@ -1287,13 +1296,13 @@
   static jlong CallNonvirtualLongMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).j;
+    return InvokeWithVarArgs(env, obj, mid, args).j;
   }
 
   static jlong CallNonvirtualLongMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).j;
+    return InvokeWithJValues(env, obj, mid, args).j;
   }
 
   static jfloat CallNonvirtualFloatMethod(JNIEnv* env,
@@ -1301,7 +1310,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.f;
   }
@@ -1309,13 +1318,13 @@
   static jfloat CallNonvirtualFloatMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).f;
+    return InvokeWithVarArgs(env, obj, mid, args).f;
   }
 
   static jfloat CallNonvirtualFloatMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).f;
+    return InvokeWithJValues(env, obj, mid, args).f;
   }
 
   static jdouble CallNonvirtualDoubleMethod(JNIEnv* env,
@@ -1323,7 +1332,7 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, obj, mid, ap);
+    JValue result = InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
     return result.d;
   }
@@ -1331,13 +1340,13 @@
   static jdouble CallNonvirtualDoubleMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, obj, mid, args).d;
+    return InvokeWithVarArgs(env, obj, mid, args).d;
   }
 
   static jdouble CallNonvirtualDoubleMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, obj, mid, args).d;
+    return InvokeWithJValues(env, obj, mid, args).d;
   }
 
   static void CallNonvirtualVoidMethod(JNIEnv* env,
@@ -1345,20 +1354,20 @@
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    InvokeWithVarArgs(ts, obj, mid, ap);
+    InvokeWithVarArgs(env, obj, mid, ap);
     va_end(ap);
   }
 
   static void CallNonvirtualVoidMethodV(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    InvokeWithVarArgs(ts, obj, mid, args);
+    InvokeWithVarArgs(env, obj, mid, args);
   }
 
   static void CallNonvirtualVoidMethodA(JNIEnv* env,
       jobject obj, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    InvokeWithJValues(ts, obj, mid, args);
+    InvokeWithJValues(env, obj, mid, args);
   }
 
   static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
@@ -1540,218 +1549,196 @@
     SET_PRIMITIVE_FIELD(SetShort, NULL, v);
   }
 
-  static jobject CallStaticObjectMethod(JNIEnv* env,
-      jclass clazz, jmethodID mid, ...) {
+  static jobject CallStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     jobject local_result = AddLocalReference<jobject>(env, result.l);
     va_end(ap);
     return local_result;
   }
 
-  static jobject CallStaticObjectMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jobject CallStaticObjectMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, args);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, args);
     return AddLocalReference<jobject>(env, result.l);
   }
 
-  static jobject CallStaticObjectMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jobject CallStaticObjectMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    JValue result = InvokeWithJValues(ts, NULL, mid, args);
+    JValue result = InvokeWithJValues(env, NULL, mid, args);
     return AddLocalReference<jobject>(env, result.l);
   }
 
-  static jboolean CallStaticBooleanMethod(JNIEnv* env,
-      jclass clazz, jmethodID mid, ...) {
+  static jboolean CallStaticBooleanMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.z;
   }
 
-  static jboolean CallStaticBooleanMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jboolean CallStaticBooleanMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).z;
+    return InvokeWithVarArgs(env, NULL, mid, args).z;
   }
 
-  static jboolean CallStaticBooleanMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).z;
+    return InvokeWithJValues(env, NULL, mid, args).z;
   }
 
   static jbyte CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.b;
   }
 
-  static jbyte CallStaticByteMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jbyte CallStaticByteMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).b;
+    return InvokeWithVarArgs(env, NULL, mid, args).b;
   }
 
-  static jbyte CallStaticByteMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jbyte CallStaticByteMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).b;
+    return InvokeWithJValues(env, NULL, mid, args).b;
   }
 
   static jchar CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.c;
   }
 
-  static jchar CallStaticCharMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jchar CallStaticCharMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).c;
+    return InvokeWithVarArgs(env, NULL, mid, args).c;
   }
 
-  static jchar CallStaticCharMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jchar CallStaticCharMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).c;
+    return InvokeWithJValues(env, NULL, mid, args).c;
   }
 
   static jshort CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.s;
   }
 
-  static jshort CallStaticShortMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jshort CallStaticShortMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).s;
+    return InvokeWithVarArgs(env, NULL, mid, args).s;
   }
 
-  static jshort CallStaticShortMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jshort CallStaticShortMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).s;
+    return InvokeWithJValues(env, NULL, mid, args).s;
   }
 
   static jint CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.i;
   }
 
-  static jint CallStaticIntMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jint CallStaticIntMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).i;
+    return InvokeWithVarArgs(env, NULL, mid, args).i;
   }
 
-  static jint CallStaticIntMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jint CallStaticIntMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).i;
+    return InvokeWithJValues(env, NULL, mid, args).i;
   }
 
   static jlong CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.j;
   }
 
-  static jlong CallStaticLongMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jlong CallStaticLongMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).j;
+    return InvokeWithVarArgs(env, NULL, mid, args).j;
   }
 
-  static jlong CallStaticLongMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jlong CallStaticLongMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).j;
+    return InvokeWithJValues(env, NULL, mid, args).j;
   }
 
   static jfloat CallStaticFloatMethod(JNIEnv* env, jclass cls, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.f;
   }
 
-  static jfloat CallStaticFloatMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jfloat CallStaticFloatMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).f;
+    return InvokeWithVarArgs(env, NULL, mid, args).f;
   }
 
-  static jfloat CallStaticFloatMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jfloat CallStaticFloatMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).f;
+    return InvokeWithJValues(env, NULL, mid, args).f;
   }
 
   static jdouble CallStaticDoubleMethod(JNIEnv* env, jclass cls, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    JValue result = InvokeWithVarArgs(ts, NULL, mid, ap);
+    JValue result = InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
     return result.d;
   }
 
-  static jdouble CallStaticDoubleMethodV(JNIEnv* env,
-      jclass clazz, jmethodID mid, va_list args) {
+  static jdouble CallStaticDoubleMethodV(JNIEnv* env, jclass clazz, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithVarArgs(ts, NULL, mid, args).d;
+    return InvokeWithVarArgs(env, NULL, mid, args).d;
   }
 
-  static jdouble CallStaticDoubleMethodA(JNIEnv* env,
-      jclass clazz, jmethodID mid, jvalue* args) {
+  static jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass clazz, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    return InvokeWithJValues(ts, NULL, mid, args).d;
+    return InvokeWithJValues(env, NULL, mid, args).d;
   }
 
   static void CallStaticVoidMethod(JNIEnv* env, jclass cls, jmethodID mid, ...) {
     ScopedJniThreadState ts(env);
     va_list ap;
     va_start(ap, mid);
-    InvokeWithVarArgs(ts, NULL, mid, ap);
+    InvokeWithVarArgs(env, NULL, mid, ap);
     va_end(ap);
   }
 
-  static void CallStaticVoidMethodV(JNIEnv* env,
-      jclass cls, jmethodID mid, va_list args) {
+  static void CallStaticVoidMethodV(JNIEnv* env, jclass cls, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
-    InvokeWithVarArgs(ts, NULL, mid, args);
+    InvokeWithVarArgs(env, NULL, mid, args);
   }
 
-  static void CallStaticVoidMethodA(JNIEnv* env,
-      jclass cls, jmethodID mid, jvalue* args) {
+  static void CallStaticVoidMethodA(JNIEnv* env, jclass cls, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
-    InvokeWithJValues(ts, NULL, mid, args);
+    InvokeWithJValues(env, NULL, mid, args);
   }
 
   static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) {
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 392ad24..e5bcb75 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -18,6 +18,7 @@
 
 class ClassLoader;
 class Field;
+union JValue;
 class Libraries;
 class Method;
 class Thread;
@@ -58,6 +59,8 @@
   return reinterpret_cast<jmethodID>(method);
 }
 
+JValue InvokeWithJValues(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args);
+
 struct JavaVMExt : public JavaVM {
   JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options);
   ~JavaVMExt();
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 4602af9..2a88e3d 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -76,7 +76,7 @@
     os << reinterpret_cast<void*>(image_header.GetBaseAddr()) << "\n\n";
 
     os << "ROOTS:\n";
-    CHECK(sizeof(image_roots_descriptions_)/(sizeof(char*)) == ImageHeader::kImageRootsMax);
+    CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
     for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
       ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
       os << StringPrintf("%s: %p\n",
diff --git a/src/object.cc b/src/object.cc
index 12cd68d..813abce 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -321,6 +321,76 @@
   java_lang_reflect_Method_ = NULL;
 }
 
+Class* ExtractNextClassFromSignature(ClassLinker* class_linker, const ClassLoader* cl, const char*& p) {
+  if (*p == '[') {
+    // Something like "[[[Ljava/lang/String;".
+    const char* start = p;
+    while (*p == '[') {
+      ++p;
+    }
+    if (*p == 'L') {
+      while (*p != ';') {
+        ++p;
+      }
+    }
+    ++p; // Either the ';' or the primitive type.
+
+    StringPiece descriptor(start, (p - start));
+    return class_linker->FindClass(descriptor, cl);
+  } else if (*p == 'L') {
+    const char* start = p;
+    while (*p != ';') {
+      ++p;
+    }
+    ++p;
+    StringPiece descriptor(start, (p - start));
+    return class_linker->FindClass(descriptor, cl);
+  } else {
+    return class_linker->FindPrimitiveClass(*p++);
+  }
+}
+
+void Method::InitJavaFieldsLocked() {
+  // Create the array.
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  size_t arg_count = GetShorty()->GetLength() - 1;
+  Class* array_class = class_linker->FindSystemClass("[Ljava/lang/Class;");
+  ObjectArray<Class>* parameters = ObjectArray<Class>::Alloc(array_class, arg_count);
+  if (parameters == NULL) {
+    return;
+  }
+
+  // Parse the signature, filling the array.
+  const ClassLoader* cl = GetDeclaringClass()->GetClassLoader();
+  std::string signature(GetSignature()->ToModifiedUtf8());
+  const char* p = signature.c_str();
+  DCHECK_EQ(*p, '(');
+  ++p;
+  for (size_t i = 0; i < arg_count; ++i) {
+    Class* c = ExtractNextClassFromSignature(class_linker, cl, p);
+    if (c == NULL) {
+      return;
+    }
+    parameters->Set(i, c);
+  }
+
+  DCHECK_EQ(*p, ')');
+  ++p;
+
+  java_parameter_types_ = parameters;
+  java_return_type_ = ExtractNextClassFromSignature(class_linker, cl, p);
+}
+
+void Method::InitJavaFields() {
+  Thread* self = Thread::Current();
+  ScopedThreadStateChange tsc(self, Thread::kRunnable);
+  MonitorEnter(self);
+  if (java_parameter_types_ == NULL || java_return_type_ == NULL) {
+    InitJavaFieldsLocked();
+  }
+  MonitorExit(self);
+}
+
 ObjectArray<String>* Method::GetDexCacheStrings() const {
   return GetFieldObject<ObjectArray<String>*>(
       OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_strings_), false);
@@ -1304,9 +1374,17 @@
 }
 
 bool String::Equals(const StringPiece& modified_utf8) const {
-  // TODO: do not assume C-string representation. For now DCHECK.
-  DCHECK_EQ(modified_utf8.data()[modified_utf8.size()], 0);
-  return Equals(modified_utf8.data());
+  if (modified_utf8.size() != GetLength()) {
+    return false;
+  }
+  const char* p = modified_utf8.data();
+  for (int32_t i = 0; i < GetLength(); ++i) {
+    uint16_t ch = GetUtf16FromUtf8(&p);
+    if (ch != CharAt(i)) {
+      return false;
+    }
+  }
+  return true;
 }
 
 // Create a modified UTF-8 encoded std::string from a java/lang/String object.
diff --git a/src/object.h b/src/object.h
index ffc8caf..2ee90fc 100644
--- a/src/object.h
+++ b/src/object.h
@@ -916,8 +916,7 @@
 
   void SetCoreSpillMask(uint32_t core_spill_mask) {
     // Computed during compilation
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Method, core_spill_mask_),
-               core_spill_mask, false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Method, core_spill_mask_), core_spill_mask, false);
   }
 
   uint32_t GetFpSpillMask() const {
@@ -926,8 +925,7 @@
 
   void SetFpSpillMask(uint32_t fp_spill_mask) {
     // Computed during compilation
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_),
-               fp_spill_mask, false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_), fp_spill_mask, false);
   }
 
   // Is this a hand crafted method used for something like describing callee saves?
@@ -958,13 +956,16 @@
   static Class* GetMethodClass() { return java_lang_reflect_Method_; }
   static void ResetClass();
 
+  void InitJavaFields();
+
  private:
   uint32_t GetReturnTypeIdx() const;
+  void InitJavaFieldsLocked();
 
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   // the class we are a part of
   Class* declaring_class_;
-  ObjectArray<Class>* java_exception_types_;
+  ObjectArray<Class>* java_exception_types_; // TODO
   Object* java_formal_type_parameters_;
   Object* java_generic_exception_types_;
   Object* java_generic_parameter_types_;
@@ -972,9 +973,9 @@
 
   String* name_;
 
+  // Initialized by InitJavaFields.
   ObjectArray<Class>* java_parameter_types_;
-
-  Class* java_return_type_;  // Unused by ART
+  Class* java_return_type_;
 
   // Storage for code_
   const ByteArray* code_array_;
@@ -1728,7 +1729,7 @@
   }
 
   void SetInterface(uint32_t i, Class* f) {  // TODO: uint16_t
-    DCHECK_NE(NumInterfaces(), 0U);
+    DCHECK_LT(i, NumInterfaces());
     ObjectArray<Class>* interfaces =
         GetFieldObject<ObjectArray<Class>*>(
             OFFSET_OF_OBJECT_MEMBER(Class, interfaces_), false);
@@ -1736,7 +1737,7 @@
   }
 
   Class* GetInterface(uint32_t i) const {
-    DCHECK_NE(NumInterfaces(), 0U);
+    DCHECK_LT(i, NumInterfaces());
     return GetInterfaces()->Get(i);
   }
 
diff --git a/src/reflection.cc b/src/reflection.cc
new file mode 100644
index 0000000..3edb9f7
--- /dev/null
+++ b/src/reflection.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reflection.h"
+
+#include "class_linker.h"
+#include "jni_internal.h"
+#include "object.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+Method* gBoolean_valueOf;
+Method* gByte_valueOf;
+Method* gCharacter_valueOf;
+Method* gDouble_valueOf;
+Method* gFloat_valueOf;
+Method* gInteger_valueOf;
+Method* gLong_valueOf;
+Method* gShort_valueOf;
+
+void InitBoxingMethod(JNIEnv* env, Method*& m, jclass c, const char* method_signature) {
+  m = DecodeMethod(env->GetStaticMethodID(c, "valueOf", method_signature));
+}
+
+void InitBoxingMethods(JNIEnv* env) {
+  InitBoxingMethod(env, gBoolean_valueOf, JniConstants::booleanClass, "(Z)Ljava/lang/Boolean;");
+  InitBoxingMethod(env, gByte_valueOf, JniConstants::byteClass, "(B)Ljava/lang/Byte;");
+  InitBoxingMethod(env, gCharacter_valueOf, JniConstants::characterClass, "(C)Ljava/lang/Character;");
+  InitBoxingMethod(env, gDouble_valueOf, JniConstants::doubleClass, "(D)Ljava/lang/Double;");
+  InitBoxingMethod(env, gFloat_valueOf, JniConstants::floatClass, "(F)Ljava/lang/Float;");
+  InitBoxingMethod(env, gInteger_valueOf, JniConstants::integerClass, "(I)Ljava/lang/Integer;");
+  InitBoxingMethod(env, gLong_valueOf, JniConstants::longClass, "(J)Ljava/lang/Long;");
+  InitBoxingMethod(env, gShort_valueOf, JniConstants::shortClass, "(S)Ljava/lang/Short;");
+}
+
+bool VerifyObjectInClass(JNIEnv* env, Object* o, Class* c) {
+  if (o == NULL) {
+    jniThrowNullPointerException(env, "receiver for non-static field access was null");
+    return false;
+  }
+  if (!o->InstanceOf(c)) {
+    std::string expectedClassName(PrettyDescriptor(c->GetDescriptor()));
+    std::string actualClassName(PrettyTypeOf(o));
+    jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+        "expected receiver of type %s, but got %s",
+        expectedClassName.c_str(), actualClassName.c_str());
+    return false;
+  }
+  return true;
+}
+
+/*
+ * Convert primitive, boxed data from "srcPtr" to "dstPtr".
+ *
+ * Section v2 2.6 lists the various conversions and promotions.  We
+ * allow the "widening" and "identity" conversions, but don't allow the
+ * "narrowing" conversions.
+ *
+ * Allowed:
+ *  byte to short, int, long, float, double
+ *  short to int, long, float double
+ *  char to int, long, float, double
+ *  int to long, float, double
+ *  long to float, double
+ *  float to double
+ * Values of types byte, char, and short are "internally" widened to int.
+ *
+ * Returns the width in 32-bit words of the destination primitive, or
+ * -1 if the conversion is not allowed.
+ */
+bool ConvertPrimitiveValue(Class* src_class, Class* dst_class, const JValue& src, JValue& dst) {
+  Class::PrimitiveType srcType = src_class->GetPrimitiveType();
+  Class::PrimitiveType dstType = dst_class->GetPrimitiveType();
+  switch (dstType) {
+  case Class::kPrimBoolean:
+  case Class::kPrimChar:
+  case Class::kPrimByte:
+    if (srcType == dstType) {
+      dst.i = src.i;
+      return true;
+    }
+    break;
+  case Class::kPrimShort:
+    if (srcType == Class::kPrimByte || srcType == Class::kPrimShort) {
+      dst.i = src.i;
+      return true;
+    }
+    break;
+  case Class::kPrimInt:
+    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+      dst.i = src.i;
+      return true;
+    }
+    break;
+  case Class::kPrimLong:
+    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+      dst.j = src.i;
+      return true;
+    } else if (srcType == Class::kPrimLong) {
+      dst.j = src.j;
+      return true;
+    }
+    break;
+  case Class::kPrimFloat:
+    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+      dst.f = src.i;
+      return true;
+    } else if (srcType == Class::kPrimLong) {
+      dst.f = src.j;
+      return true;
+    } else if (srcType == Class::kPrimFloat) {
+      dst.i = src.i;
+      return true;
+    }
+    break;
+  case Class::kPrimDouble:
+    if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+        srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+      dst.d = src.i;
+      return true;
+    } else if (srcType == Class::kPrimLong) {
+      dst.d = src.j;
+      return true;
+    } else if (srcType == Class::kPrimFloat) {
+      dst.d = src.f;
+      return true;
+    } else if (srcType == Class::kPrimDouble) {
+      dst.j = src.j;
+      return true;
+    }
+    break;
+  default:
+    break;
+  }
+  Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+      "invalid primitive conversion from %s to %s",
+      PrettyDescriptor(src_class->GetDescriptor()).c_str(),
+      PrettyDescriptor(dst_class->GetDescriptor()).c_str());
+  return false;
+}
+
+void BoxPrimitive(JNIEnv* env, Class* src_class, JValue& value) {
+  if (!src_class->IsPrimitive()) {
+    return;
+  }
+
+  Method* m = NULL;
+  UniquePtr<byte[]> args(new byte[8]);
+  memset(&args[0], 0, 8);
+  switch (src_class->GetPrimitiveType()) {
+  case Class::kPrimBoolean:
+    m = gBoolean_valueOf;
+    *reinterpret_cast<uint32_t*>(&args[0]) = value.z;
+    break;
+  case Class::kPrimByte:
+    m = gByte_valueOf;
+    *reinterpret_cast<uint32_t*>(&args[0]) = value.b;
+    break;
+  case Class::kPrimChar:
+    m = gCharacter_valueOf;
+    *reinterpret_cast<uint32_t*>(&args[0]) = value.c;
+    break;
+  case Class::kPrimDouble:
+    m = gDouble_valueOf;
+    *reinterpret_cast<double*>(&args[0]) = value.d;
+    break;
+  case Class::kPrimFloat:
+    m = gFloat_valueOf;
+    *reinterpret_cast<float*>(&args[0]) = value.f;
+    break;
+  case Class::kPrimInt:
+    m = gInteger_valueOf;
+    *reinterpret_cast<uint32_t*>(&args[0]) = value.i;
+    break;
+  case Class::kPrimLong:
+    m = gLong_valueOf;
+    *reinterpret_cast<uint64_t*>(&args[0]) = value.j;
+    break;
+  case Class::kPrimShort:
+    m = gShort_valueOf;
+    *reinterpret_cast<uint32_t*>(&args[0]) = value.s;
+    break;
+  case Class::kPrimVoid:
+    // There's no such thing as a void field, and void methods invoked via reflection return null.
+    value.l = NULL;
+    return;
+  default:
+    LOG(FATAL) << PrettyClass(src_class);
+  }
+
+  Thread* self = Thread::Current();
+  ScopedThreadStateChange tsc(self, Thread::kRunnable);
+  m->Invoke(self, NULL, args.get(), &value);
+}
+
+bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_value) {
+  if (dst_class->GetPrimitiveType() == Class::kPrimNot) {
+    if (o != NULL && !o->InstanceOf(dst_class)) {
+      jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+          "expected object of type %s, but got %s",
+          PrettyDescriptor(dst_class->GetDescriptor()).c_str(),
+          PrettyTypeOf(o).c_str());
+      return false;
+    }
+    unboxed_value.l = o;
+    return true;
+  } else if (dst_class->GetPrimitiveType() == Class::kPrimVoid) {
+    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+        "can't unbox to void");
+    return false;
+  }
+
+  if (o == NULL) {
+    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+        "null passed for boxed primitive type");
+    return false;
+  }
+
+  JValue boxed_value = { 0 };
+  const String* src_descriptor = o->GetClass()->GetDescriptor();
+  Class* src_class = NULL;
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Field* primitive_field = o->GetClass()->GetIFields()->Get(0);
+  if (src_descriptor->Equals("Ljava/lang/Boolean;")) {
+    src_class = class_linker->FindPrimitiveClass('Z');
+    boxed_value.z = primitive_field->GetBoolean(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Byte;")) {
+    src_class = class_linker->FindPrimitiveClass('B');
+    boxed_value.b = primitive_field->GetByte(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Character;")) {
+    src_class = class_linker->FindPrimitiveClass('C');
+    boxed_value.c = primitive_field->GetChar(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Float;")) {
+    src_class = class_linker->FindPrimitiveClass('F');
+    boxed_value.f = primitive_field->GetFloat(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Double;")) {
+    src_class = class_linker->FindPrimitiveClass('D');
+    boxed_value.d = primitive_field->GetDouble(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Integer;")) {
+    src_class = class_linker->FindPrimitiveClass('I');
+    boxed_value.i = primitive_field->GetInt(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Long;")) {
+    src_class = class_linker->FindPrimitiveClass('J');
+    boxed_value.j = primitive_field->GetLong(o);
+  } else if (src_descriptor->Equals("Ljava/lang/Short;")) {
+    src_class = class_linker->FindPrimitiveClass('S');
+    boxed_value.s = primitive_field->GetShort(o);
+  } else {
+    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+        "%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str());
+    return false;
+  }
+
+  return ConvertPrimitiveValue(src_class, dst_class, boxed_value, unboxed_value);
+}
+
+}  // namespace art
diff --git a/src/reflection.h b/src/reflection.h
new file mode 100644
index 0000000..7256754
--- /dev/null
+++ b/src/reflection.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_REFLECTION_H_
+#define ART_SRC_REFLECTION_H_
+
+#include "jni.h"
+
+namespace art {
+
+class Class;
+union JValue;
+class Object;
+
+void InitBoxingMethods(JNIEnv* env);
+void BoxPrimitive(JNIEnv* env, Class* src_class, JValue& value);
+bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_value);
+
+bool ConvertPrimitiveValue(Class* src_class, Class* dst_class, const JValue& src, JValue& dst);
+
+bool VerifyObjectInClass(JNIEnv* env, Object* o, Class* c);
+
+}  // namespace art
+
+#endif  // ART_SRC_REFLECTION_H_
diff --git a/src/runtime.cc b/src/runtime.cc
index 751e8da..b1da77d 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -440,17 +440,19 @@
   Thread* self = Thread::Current();
   JNIEnv* env = self->GetJniEnv();
 
-  // Must be in the kNative state for JNI-based method registration.
+  // Must be in the kNative state for calling native methods (JNI_OnLoad code).
   ScopedThreadStateChange tsc(self, Thread::kNative);
 
+  // First set up JniConstants, which is used by both the runtime's built-in native
+  // methods and libcore.
   JniConstants::init(env);
 
-  // First set up the native methods provided by the runtime itself.
+  // Then set up the native methods provided by the runtime itself.
   RegisterRuntimeNativeMethods(env);
 
-  // Now set up libcore, which is just a JNI library with a JNI_OnLoad.
-  // Most JNI libraries can just use System.loadLibrary, but you can't
-  // if you're the library that implements System.loadLibrary!
+  // Then set up libcore, which is just a regular JNI library with a regular JNI_OnLoad.
+  // Most JNI libraries can just use System.loadLibrary, but libcore can't because it's
+  // the library that implements System.loadLibrary!
   LoadJniLibrary(instance_->GetJavaVM(), "javacore");
 }
 
diff --git a/src/thread.cc b/src/thread.cc
index 57bc09b..232c115 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -209,8 +209,9 @@
     return 0;  // Success
   } else {
     Thread::Current()->ThrowNewException("Ljava/lang/ClassCastException;",
-                                         "%s cannot be cast to %s",
-                                         PrettyClass(b).c_str(), PrettyClass(a).c_str());
+        "%s cannot be cast to %s",
+        PrettyDescriptor(a->GetDescriptor()).c_str(),
+        PrettyDescriptor(b->GetDescriptor()).c_str());
     return -1;  // Failure
   }
 }