Move JNI compiler tests to use pure JNI.

Implement JNI upcalls for x86.
Fix off by 1 bug in JNI calling convention for x86.
Fix bugs in ARM JNI upcalls.
Straw man JNI invoke nonvirtual implementations.
Match va_start with va_end in JNI internals.

Change-Id: I64d62eca41ac726ae0d007c1f41d2193db5be82e
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 854df88..39ba6ed 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -6,7 +6,9 @@
 #include "class_linker.h"
 #include "common_test.h"
 #include "dex_file.h"
+#include "indirect_reference_table.h"
 #include "jni_compiler.h"
+#include "jni_internal.h"
 #include "mem_map.h"
 #include "runtime.h"
 #include "scoped_ptr.h"
@@ -19,143 +21,173 @@
  protected:
   virtual void SetUp() {
     CommonTest::SetUp();
-    // Create thunk code that performs the native to managed transition
-    thunk_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC));
-    CHECK(thunk_code_ !=  NULL);
-    Assembler thk_asm;
-    // TODO: shouldn't have machine specific code in a general purpose file
-#if defined(__i386__)
-    thk_asm.pushl(EDI);                   // preserve EDI
-    thk_asm.movl(EAX, Address(ESP, 8));   // EAX = method->GetCode()
-    thk_asm.movl(EDI, Address(ESP, 12));  // EDI = method
-    thk_asm.pushl(Immediate(0));          // push pad
-    thk_asm.pushl(Immediate(0));          // push pad
-    thk_asm.pushl(Address(ESP, 44));      // push pad  or jlong high
-    thk_asm.pushl(Address(ESP, 44));      // push jint or jlong low
-    thk_asm.pushl(Address(ESP, 44));      // push jint or jlong high
-    thk_asm.pushl(Address(ESP, 44));      // push jint or jlong low
-    thk_asm.pushl(Address(ESP, 44));      // push jobject
-    thk_asm.call(EAX);                    // Continue in method->GetCode()
-    thk_asm.addl(ESP, Immediate(28));     // pop arguments
-    thk_asm.popl(EDI);                    // restore EDI
-    thk_asm.ret();
-#elif defined(__arm__)
-    thk_asm.AddConstant(SP, -32);         // Build frame
-    thk_asm.StoreToOffset(kStoreWord, LR, SP, 28);  // Spill link register
-    thk_asm.StoreToOffset(kStoreWord, R9, SP, 24);  // Spill R9
-    thk_asm.mov(R12, ShifterOperand(R0));  // R12 = method->GetCode()
-    thk_asm.mov(R0,  ShifterOperand(R1));  // R0  = method
-    thk_asm.mov(R9,  ShifterOperand(R2));  // R9  = Thread::Current()
-    thk_asm.mov(R1,  ShifterOperand(R3));  // R1  = arg1 (jint/jlong low)
-    thk_asm.LoadFromOffset(kLoadWord, R3, SP, 44);  // R3 = arg5 (pad/jlong high)
-    thk_asm.StoreToOffset(kStoreWord, R3, SP, 4);
-    thk_asm.LoadFromOffset(kLoadWord, R3, SP, 40);  // R3 = arg4 (jint/jlong low)
-    thk_asm.StoreToOffset(kStoreWord, R3, SP, 0);
-    thk_asm.LoadFromOffset(kLoadWord, R3, SP, 36);  // R3 = arg3 (jint/jlong high)
-    thk_asm.LoadFromOffset(kLoadWord, R2, SP, 32);  // R2 = arg2 (jint/jlong high)
-    thk_asm.blx(R12);                     // Branch and link R12
-    thk_asm.LoadFromOffset(kLoadWord, LR, SP, 28);  // Fill link register
-    thk_asm.LoadFromOffset(kLoadWord, R9, SP, 24);  // Fill R9
-    thk_asm.AddConstant(SP, 32);          // Remove frame
-    thk_asm.mov(PC, ShifterOperand(LR));  // Return
-#else
-#error Unimplemented
-#endif
-    size_t cs = thk_asm.CodeSize();
-    MemoryRegion code(thunk_code_->GetAddress(), cs);
-    thk_asm.FinalizeInstructions(code);
-    thunk_entry1_ = reinterpret_cast<jint (*)(const void*, art::Method*,
-                                              Thread*, jobject, jint, jint,
-                                              jint)
-                                    >(code.pointer());
-    thunk_entry2_ = reinterpret_cast<jdouble (*)(const void*, art::Method*,
-                                                 Thread*, jobject, jdouble,
-                                                 jdouble)
-                                    >(code.pointer());
+    dex_.reset(OpenDexFileBase64(kMyClassNativesDex));
+    class_loader_ = AllocPathClassLoader(dex_.get());
   }
 
-  virtual void TearDown() {
-    // Release thunk code
-    CHECK(runtime_->DetachCurrentThread());
+  void SetupForTest(bool direct, const char* method_name,
+                    const char* method_sig, void* native_fnptr) {
+    const char* class_name = "LMyClass;";
+    Class* klass = class_linker_->FindClass(class_name, class_loader_);
+    ASSERT_TRUE(klass != NULL);
+
+    Method* method;
+    if (direct) {
+      method = klass->FindDirectMethod(method_name, method_sig);
+    } else {
+      method = klass->FindVirtualMethod(method_name, method_sig);
+    }
+    ASSERT_TRUE(method != NULL);
+
+    // Compile the native method
+    jni_compiler.Compile(&jni_asm, method);
+
+    env_ = Thread::Current()->GetJniEnv();
+
+    // TODO: when we support class loaders - env->FindClass(class_name);
+    IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env_)->locals;
+    uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+    IndirectRef klass_ref = locals.Add(cookie, klass);
+    jklass_ = reinterpret_cast<jclass>(klass_ref);
+    if (direct) {
+      jmethod_ = env_->GetStaticMethodID(jklass_, method_name, method_sig);
+    } else {
+      jmethod_ = env_->GetMethodID(jklass_, method_name, method_sig);
+    }
+    ASSERT_TRUE(jmethod_ != NULL);
+
+    JNINativeMethod methods[] = {{method_name, method_sig, native_fnptr}};
+    ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1));
+
+    jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
+    jobj_ = env_->NewObject(jklass_, constructor);
+    ASSERT_TRUE(jobj_ != NULL);
   }
 
-  // Run generated code associated with method passing and returning int size
-  // arguments
-  jvalue RunMethod(Method* method, jvalue a, jvalue b, jvalue c, jvalue d) {
-    jvalue result;
-    // sanity checks
-    EXPECT_NE(static_cast<void*>(NULL), method->GetCode());
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    // perform call
-    result.i = (*thunk_entry1_)(method->GetCode(), method, Thread::Current(),
-                                a.l, b.i, c.i, d.i);
-    // sanity check post-call
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    return result;
-  }
-
-  // Run generated code associated with method passing and returning double size
-  // arguments
-  jvalue RunMethodD(Method* method, jvalue a, jvalue b, jvalue c) {
-    jvalue result;
-    // sanity checks
-    EXPECT_NE(static_cast<void*>(NULL), method->GetCode());
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    // perform call
-    result.d = (*thunk_entry2_)(method->GetCode(), method, Thread::Current(),
-                                a.l, b.d, c.d);
-    // sanity check post-call
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    return result;
-  }
-
-  scoped_ptr<MemMap> thunk_code_;
-  jint (*thunk_entry1_)(const void*, Method*, Thread*, jobject, jint, jint,
-                        jint);
-  jdouble (*thunk_entry2_)(const void*, Method*, Thread*, jobject, jdouble,
-                           jdouble);
+ public:
+  static jclass jklass_;
+  static jobject jobj_;
+ protected:
+  scoped_ptr<DexFile> dex_;
+  PathClassLoader* class_loader_;
+  Assembler jni_asm;
+  JniCompiler jni_compiler;
+  JNIEnv* env_;
+  jmethodID jmethod_;
 };
 
+jclass JniCompilerTest::jklass_;
+jobject JniCompilerTest::jobj_;
+
 int gJava_MyClass_foo_calls = 0;
-void Java_MyClass_foo(JNIEnv*, jobject) {
+void Java_MyClass_foo(JNIEnv* env, jobject thisObj) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_foo_calls++;
 }
 
+TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
+  SetupForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClass_foo));
+
+  EXPECT_EQ(0, gJava_MyClass_foo_calls);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+  EXPECT_EQ(1, gJava_MyClass_foo_calls);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+  EXPECT_EQ(2, gJava_MyClass_foo_calls);
+}
+
 int gJava_MyClass_fooI_calls = 0;
-jint Java_MyClass_fooI(JNIEnv*, jobject, jint x) {
+jint Java_MyClass_fooI(JNIEnv* env, jobject thisObj, jint x) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooI_calls++;
   return x;
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
+  SetupForTest(false, "fooI", "(I)I",
+               reinterpret_cast<void*>(&Java_MyClass_fooI));
+
+  EXPECT_EQ(0, gJava_MyClass_fooI_calls);
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
+  EXPECT_EQ(1, gJava_MyClass_fooI_calls);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFED00D);
+  EXPECT_EQ(static_cast<jint>(0xCAFED00D), result);
+  EXPECT_EQ(2, gJava_MyClass_fooI_calls);
+}
+
 int gJava_MyClass_fooII_calls = 0;
-jint Java_MyClass_fooII(JNIEnv*, jobject, jint x, jint y) {
+jint Java_MyClass_fooII(JNIEnv* env, jobject thisObj, jint x, jint y) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooII_calls++;
   return x - y;  // non-commutative operator
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
+  SetupForTest(false, "fooII", "(II)I",
+               reinterpret_cast<void*>(&Java_MyClass_fooII));
+
+  EXPECT_EQ(0, gJava_MyClass_fooII_calls);
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 99, 10);
+  EXPECT_EQ(99 - 10, result);
+  EXPECT_EQ(1, gJava_MyClass_fooII_calls);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFEBABE,
+                                         0xCAFED00D);
+  EXPECT_EQ(static_cast<jint>(0xCAFEBABE - 0xCAFED00D), result);
+  EXPECT_EQ(2, gJava_MyClass_fooII_calls);
+}
+
 int gJava_MyClass_fooDD_calls = 0;
-jdouble Java_MyClass_fooDD(JNIEnv*, jobject, jdouble x, jdouble y) {
+jdouble Java_MyClass_fooDD(JNIEnv* env, jobject thisObj, jdouble x, jdouble y) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooDD_calls++;
   return x - y;  // non-commutative operator
 }
 
+TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
+  SetupForTest(false, "fooDD", "(DD)D",
+               reinterpret_cast<void*>(&Java_MyClass_fooDD));
+
+  EXPECT_EQ(0, gJava_MyClass_fooDD_calls);
+  jdouble result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_,
+                                                    99.0, 10.0);
+  EXPECT_EQ(99.0 - 10.0, result);
+  EXPECT_EQ(1, gJava_MyClass_fooDD_calls);
+  jdouble a = 3.14159265358979323846;
+  jdouble b = 0.69314718055994530942;
+  result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_, a, b);
+  EXPECT_EQ(a - b, result);
+  EXPECT_EQ(2, gJava_MyClass_fooDD_calls);
+}
+
 int gJava_MyClass_fooIOO_calls = 0;
-jobject Java_MyClass_fooIOO(JNIEnv*, jobject thisObject, jint x, jobject y,
+jobject Java_MyClass_fooIOO(JNIEnv* env, jobject thisObj, jint x, jobject y,
                             jobject z) {
   EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooIOO_calls++;
   switch (x) {
     case 1:
@@ -163,16 +195,50 @@
     case 2:
       return z;
     default:
-      return thisObject;
+      return thisObj;
   }
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
+  SetupForTest(false, "fooIOO",
+               "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+               reinterpret_cast<void*>(&Java_MyClass_fooIOO));
+
+  EXPECT_EQ(0, gJava_MyClass_fooIOO_calls);
+  jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, NULL, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(1, gJava_MyClass_fooIOO_calls);
+
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, NULL, jklass_);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(2, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 1, NULL, jklass_);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(3, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, NULL, jklass_);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(4, gJava_MyClass_fooIOO_calls);
+
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, jklass_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(5, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 1, jklass_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(6, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, jklass_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(7, gJava_MyClass_fooIOO_calls);
+}
+
 int gJava_MyClass_fooSIOO_calls = 0;
 jobject Java_MyClass_fooSIOO(JNIEnv* env, jclass klass, jint x, jobject y,
                              jobject z) {
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(klass != NULL);
+  // TODO: check JNIEnv and klass are sane
+  // EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   gJava_MyClass_fooSIOO_calls++;
   switch (x) {
     case 1:
@@ -184,11 +250,47 @@
   }
 }
 
+
+TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
+  SetupForTest(true, "fooSIOO",
+               "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+               reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
+
+  EXPECT_EQ(0, gJava_MyClass_fooSIOO_calls);
+  jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(1, gJava_MyClass_fooSIOO_calls);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(2, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(3, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(4, gJava_MyClass_fooSIOO_calls);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(5, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(6, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(7, gJava_MyClass_fooSIOO_calls);
+}
+
 int gJava_MyClass_fooSSIOO_calls = 0;
-jobject Java_MyClass_fooSSIOO(JNIEnv*, jclass klass, jint x, jobject y,
+jobject Java_MyClass_fooSSIOO(JNIEnv* env, jclass klass, jint x, jobject y,
                              jobject z) {
   EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(klass != NULL);
+  // TODO: check JNIEnv and klass are sane
+  // EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   gJava_MyClass_fooSSIOO_calls++;
   switch (x) {
     case 1:
@@ -200,307 +302,34 @@
   }
 }
 
-TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("foo", "()V");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  // JNIEnv* env = Thread::Current()->GetJniEnv();
-  // JNINativeMethod methods[] = {{"foo", "()V", (void*)&Java_MyClass_foo}};
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_foo));
-
-  jvalue a;
-  a.l = (jobject)NULL;
-  gJava_MyClass_foo_calls = 0;
-  RunMethod(method, a, a, a, a);
-  EXPECT_EQ(1, gJava_MyClass_foo_calls);
-  RunMethod(method, a, a, a, a);
-  EXPECT_EQ(2, gJava_MyClass_foo_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooI", "(I)I");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooI));
-
-  jvalue a, b, c;
-  a.l = (jobject)NULL;
-  b.i = 42;
-  EXPECT_EQ(0, gJava_MyClass_fooI_calls);
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
-  EXPECT_EQ(1, gJava_MyClass_fooI_calls);
-  b.i = 0xCAFED00D;
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ((jint)0xCAFED00D, c.i);
-  EXPECT_EQ(2, gJava_MyClass_fooI_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooII", "(II)I");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooII));
-
-  jvalue a, b, c, d;
-  a.l = (jobject)NULL;
-  b.i = 99;
-  c.i = 10;
-  EXPECT_EQ(0, gJava_MyClass_fooII_calls);
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ(99 - 10, d.i);
-  EXPECT_EQ(1, gJava_MyClass_fooII_calls);
-  b.i = 0xCAFEBABE;
-  c.i = 0xCAFED00D;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jint)(0xCAFEBABE - 0xCAFED00D), d.i);
-  EXPECT_EQ(2, gJava_MyClass_fooII_calls);
-}
-
-
-TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooDD", "(DD)D");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooDD));
-
-  jvalue a, b, c, d;
-  a.l = (jobject)NULL;
-  b.d = 99;
-  c.d = 10;
-  EXPECT_EQ(0, gJava_MyClass_fooDD_calls);
-  d = RunMethodD(method, a, b, c);
-  ASSERT_EQ(b.d - c.d, d.d);
-  EXPECT_EQ(1, gJava_MyClass_fooDD_calls);
-  b.d = 3.14159265358979323846;
-  c.d = 0.69314718055994530942;
-  d = RunMethodD(method, a, b, c);
-  ASSERT_EQ(b.d - c.d, d.d);
-  EXPECT_EQ(2, gJava_MyClass_fooDD_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod(
-      "fooIOO",
-      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooIOO));
-
-  jvalue a, b, c, d, e;
-  a.l = (jobject)NULL;
-  b.i = 0;
-  c.l = (jobject)NULL;
-  d.l = (jobject)NULL;
-  EXPECT_EQ(0, gJava_MyClass_fooIOO_calls);
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)NULL, e.l);
-  EXPECT_EQ(1, gJava_MyClass_fooIOO_calls);
-  a.l = (jobject)8;
-  b.i = 0;
-  c.l = (jobject)NULL;
-  d.l = (jobject)16;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)8, e.l);
-  EXPECT_EQ(2, gJava_MyClass_fooIOO_calls);
-  b.i = 1;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)NULL, e.l);
-  EXPECT_EQ(3, gJava_MyClass_fooIOO_calls);
-  b.i = 2;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)16, e.l);
-  EXPECT_EQ(4, gJava_MyClass_fooIOO_calls);
-  a.l = (jobject)8;
-  b.i = 0;
-  c.l = (jobject)16;
-  d.l = (jobject)NULL;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)8, e.l);
-  EXPECT_EQ(5, gJava_MyClass_fooIOO_calls);
-  b.i = 1;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)16, e.l);
-  EXPECT_EQ(6, gJava_MyClass_fooIOO_calls);
-  b.i = 2;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)NULL, e.l);
-  EXPECT_EQ(7, gJava_MyClass_fooIOO_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindDirectMethod(
-      "fooSIOO",
-      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
-
-  jvalue a, b, c, d;
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)NULL;
-  EXPECT_EQ(0, gJava_MyClass_fooSIOO_calls);
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
-  EXPECT_EQ(1, gJava_MyClass_fooSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)16;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
-  EXPECT_EQ(2, gJava_MyClass_fooSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
-  EXPECT_EQ(3, gJava_MyClass_fooSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
-  EXPECT_EQ(4, gJava_MyClass_fooSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)16;
-  c.l = (jobject)NULL;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
-  EXPECT_EQ(5, gJava_MyClass_fooSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
-  EXPECT_EQ(6, gJava_MyClass_fooSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
-  EXPECT_EQ(7, gJava_MyClass_fooSIOO_calls);
-}
-
 TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  SetupForTest(true, "fooSSIOO",
+               "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+               reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
 
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindDirectMethod(
-      "fooSSIOO",
-      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
-
-  jvalue a, b, c, d;
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)NULL;
   EXPECT_EQ(0, gJava_MyClass_fooSSIOO_calls);
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
+  jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
   EXPECT_EQ(1, gJava_MyClass_fooSSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)16;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
   EXPECT_EQ(2, gJava_MyClass_fooSSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
   EXPECT_EQ(3, gJava_MyClass_fooSSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
   EXPECT_EQ(4, gJava_MyClass_fooSSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)16;
-  c.l = (jobject)NULL;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
   EXPECT_EQ(5, gJava_MyClass_fooSSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
   EXPECT_EQ(6, gJava_MyClass_fooSSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
   EXPECT_EQ(7, gJava_MyClass_fooSSIOO_calls);
 }
 
@@ -510,41 +339,24 @@
   gSuspendCounterHandler_calls++;
   Thread::Current()->DecrementSuspendCount();
 }
+
 TEST_F(JniCompilerTest, SuspendCountAcknowledgement) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooI", "(I)I");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooI));
+  SetupForTest(false, "fooI", "(I)I",
+               reinterpret_cast<void*>(&Java_MyClass_fooI));
   Thread::Current()->RegisterSuspendCountEntryPoint(&SuspendCountHandler);
 
-  gSuspendCounterHandler_calls = 0;
   gJava_MyClass_fooI_calls = 0;
-  jvalue a, b, c;
-  a.l = (jobject)NULL;
-  b.i = 42;
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
   EXPECT_EQ(1, gJava_MyClass_fooI_calls);
   EXPECT_EQ(0, gSuspendCounterHandler_calls);
   Thread::Current()->IncrementSuspendCount();
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
   EXPECT_EQ(2, gJava_MyClass_fooI_calls);
   EXPECT_EQ(1, gSuspendCounterHandler_calls);
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
   EXPECT_EQ(3, gJava_MyClass_fooI_calls);
   EXPECT_EQ(1, gSuspendCounterHandler_calls);
 }
@@ -555,38 +367,21 @@
   gExceptionHandler_calls++;
   Thread::Current()->ClearException();
 }
+
 TEST_F(JniCompilerTest, ExceptionHandling) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("foo", "()V");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_foo));
+  SetupForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClass_foo));
   Thread::Current()->RegisterExceptionEntryPoint(&ExceptionHandler);
 
-  gExceptionHandler_calls = 0;
   gJava_MyClass_foo_calls = 0;
-  jvalue a;
-  a.l = (jobject)NULL;
-  RunMethod(method, a, a, a, a);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(1, gJava_MyClass_foo_calls);
   EXPECT_EQ(0, gExceptionHandler_calls);
   // TODO: create a real exception here
-  Thread::Current()->SetException(reinterpret_cast<Object*>(8));
-  RunMethod(method, a, a, a, a);
+  Thread::Current()->SetException(reinterpret_cast<Object*>(jobj_));
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(2, gJava_MyClass_foo_calls);
   EXPECT_EQ(1, gExceptionHandler_calls);
-  RunMethod(method, a, a, a, a);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(3, gJava_MyClass_foo_calls);
   EXPECT_EQ(1, gExceptionHandler_calls);
 }