Implement native method return value and upcall argument type checking.
Also clean up the CheckJNI testing a bit. I still need to do some work so that
JniAbort catches more of the detail, but this is a step forward.
Change-Id: Ibf5e32867d56123cff902ebf602406b731f567d2
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 8bdc8c6..8f7884d 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -41,6 +41,10 @@
CHECK(aioobe.get() != NULL);
aioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(aioobe.get()));
+ ScopedLocalRef<jclass> ase(env_, env_->FindClass("java/lang/ArrayStoreException"));
+ CHECK(ase.get() != NULL);
+ ase_ = reinterpret_cast<jclass>(env_->NewGlobalRef(ase.get()));
+
ScopedLocalRef<jclass> sioobe(env_, env_->FindClass("java/lang/StringIndexOutOfBoundsException"));
CHECK(sioobe.get() != NULL);
sioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(sioobe.get()));
@@ -48,6 +52,7 @@
virtual void TearDown() {
env_->DeleteGlobalRef(aioobe_);
+ env_->DeleteGlobalRef(ase_);
env_->DeleteGlobalRef(sioobe_);
CommonTest::TearDown();
}
@@ -494,6 +499,7 @@
JavaVMExt* vm_;
JNIEnvExt* env_;
jclass aioobe_;
+ jclass ase_;
jclass sioobe_;
};
@@ -525,35 +531,33 @@
EXPECT_TRUE(env_->ExceptionCheck()); \
env_->ExceptionClear()
-std::string gCheckJniAbortMessage;
-void TestCheckJniAbortHook(const std::string& reason) {
- gCheckJniAbortMessage = reason;
-}
-
TEST_F(JniInternalTest, FindClass) {
// Reference types...
EXPECT_CLASS_FOUND("java/lang/String");
// ...for arrays too, where you must include "L;".
EXPECT_CLASS_FOUND("[Ljava/lang/String;");
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- // We support . as well as / for compatibility, if -Xcheck:jni is off.
- EXPECT_CLASS_FOUND("java.lang.String");
- EXPECT_CLASS_NOT_FOUND("Ljava.lang.String;");
- EXPECT_CLASS_FOUND("[Ljava.lang.String;");
- EXPECT_CLASS_NOT_FOUND("[java.lang.String");
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
- // You can't include the "L;" in a JNI class descriptor.
- EXPECT_CLASS_NOT_FOUND("Ljava/lang/String;");
- // But you must include it for an array of any reference type.
- EXPECT_CLASS_NOT_FOUND("[java/lang/String");
- vm_->check_jni_abort_hook = NULL;
+ // We support . as well as / for compatibility, if -Xcheck:jni is off.
+ EXPECT_CLASS_FOUND("java.lang.String");
+ EXPECT_CLASS_NOT_FOUND("Ljava.lang.String;");
+ EXPECT_CLASS_FOUND("[Ljava.lang.String;");
+ EXPECT_CLASS_NOT_FOUND("[java.lang.String");
+
+ // You can't include the "L;" in a JNI class descriptor.
+ EXPECT_CLASS_NOT_FOUND("Ljava/lang/String;");
+ // But you must include it for an array of any reference type.
+ EXPECT_CLASS_NOT_FOUND("[java/lang/String");
+ }
// Primitive arrays are okay (if the primitive type is valid)...
EXPECT_CLASS_FOUND("[C");
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- EXPECT_CLASS_NOT_FOUND("[K");
- vm_->check_jni_abort_hook = NULL;
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_CLASS_NOT_FOUND("[K");
+ }
// But primitive types aren't allowed...
EXPECT_CLASS_NOT_FOUND("C");
EXPECT_CLASS_NOT_FOUND("K");
@@ -1007,10 +1011,11 @@
}
TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- // Passing in a NULL jstring is ignored normally, but caught by -Xcheck:jni.
- EXPECT_TRUE(env_->GetStringUTFChars(NULL, NULL) == NULL);
- vm_->check_jni_abort_hook = NULL;
+ {
+ // Passing in a NULL jstring is ignored normally, but caught by -Xcheck:jni.
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_TRUE(env_->GetStringUTFChars(NULL, NULL) == NULL);
+ }
jstring s = env_->NewStringUTF("hello");
ASSERT_TRUE(s != NULL);
@@ -1075,24 +1080,26 @@
}
TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
- jclass c = env_->FindClass("java/lang/Object");
- ASSERT_TRUE(c != NULL);
+ jclass java_lang_Class = env_->FindClass("java/lang/Class");
+ ASSERT_TRUE(java_lang_Class != NULL);
- jobjectArray array = env_->NewObjectArray(1, c, NULL);
+ jobjectArray array = env_->NewObjectArray(1, java_lang_Class, NULL);
EXPECT_TRUE(array != NULL);
EXPECT_TRUE(env_->GetObjectArrayElement(array, 0) == NULL);
- env_->SetObjectArrayElement(array, 0, c);
- EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(array, 0), c));
+ env_->SetObjectArrayElement(array, 0, java_lang_Class);
+ EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(array, 0), java_lang_Class));
// ArrayIndexOutOfBounds for negative index.
- env_->SetObjectArrayElement(array, -1, c);
+ env_->SetObjectArrayElement(array, -1, java_lang_Class);
EXPECT_EXCEPTION(aioobe_);
// ArrayIndexOutOfBounds for too-large index.
- env_->SetObjectArrayElement(array, 1, c);
+ env_->SetObjectArrayElement(array, 1, java_lang_Class);
EXPECT_EXCEPTION(aioobe_);
- // TODO: check ArrayStoreException thrown for bad types.
+ // ArrayStoreException thrown for bad types.
+ env_->SetObjectArrayElement(array, 0, env_->NewStringUTF("not a jclass!"));
+ EXPECT_EXCEPTION(ase_);
}
#define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \
@@ -1199,10 +1206,11 @@
ASSERT_TRUE(s != NULL);
env_->DeleteLocalRef(s);
- // Currently, deleting an already-deleted reference is just a warning.
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- env_->DeleteLocalRef(s);
- vm_->check_jni_abort_hook = NULL;
+ {
+ // Currently, deleting an already-deleted reference is just a CheckJNI warning.
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteLocalRef(s);
+ }
s = env_->NewStringUTF("");
ASSERT_TRUE(s != NULL);
@@ -1276,10 +1284,11 @@
ASSERT_TRUE(o != NULL);
env_->DeleteGlobalRef(o);
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- // Currently, deleting an already-deleted reference is just a warning.
- env_->DeleteGlobalRef(o);
- vm_->check_jni_abort_hook = NULL;
+ {
+ // Currently, deleting an already-deleted reference is just a CheckJNI warning.
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteGlobalRef(o);
+ }
jobject o1 = env_->NewGlobalRef(s);
ASSERT_TRUE(o1 != NULL);
@@ -1316,10 +1325,11 @@
ASSERT_TRUE(o != NULL);
env_->DeleteWeakGlobalRef(o);
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- // Currently, deleting an already-deleted reference is just a warning.
- env_->DeleteWeakGlobalRef(o);
- vm_->check_jni_abort_hook = NULL;
+ {
+ // Currently, deleting an already-deleted reference is just a CheckJNI warning.
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteWeakGlobalRef(o);
+ }
jobject o1 = env_->NewWeakGlobalRef(s);
ASSERT_TRUE(o1 != NULL);
@@ -1540,12 +1550,17 @@
EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, imse_class));
// It's an error to call MonitorEnter or MonitorExit on NULL.
- vm_->check_jni_abort_hook = TestCheckJniAbortHook;
- env_->MonitorEnter(NULL);
- EXPECT_TRUE(gCheckJniAbortMessage.find("in call to MonitorEnter") != std::string::npos);
- env_->MonitorExit(NULL);
- EXPECT_TRUE(gCheckJniAbortMessage.find("in call to MonitorExit") != std::string::npos);
- vm_->check_jni_abort_hook = NULL;
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->MonitorEnter(NULL);
+ check_jni_abort_catcher.Check("in call to MonitorEnter");
+ }
+
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->MonitorExit(NULL);
+ check_jni_abort_catcher.Check("in call to MonitorExit");
+ }
}
} // namespace art