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);
}