Add -Xcheck:jni.

I think this is as complete as possible right now. The remaining
two #if 0 sections require:

1. a way to get the Method* of the current native method.
2. a way to get the Class* of the type of a given Field*.

Change-Id: I331586022095fb36ccc10c9ac1890a59a9224d01
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index c66bb42..a71975b 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -15,8 +15,10 @@
   virtual void SetUp() {
     CommonTest::SetUp();
 
+    vm_ = Runtime::Current()->GetJavaVM();
+
     // Turn on -verbose:jni for the JNI tests.
-    Runtime::Current()->GetJavaVM()->verbose_jni = true;
+    vm_->verbose_jni = true;
 
     env_ = Thread::Current()->GetJniEnv();
 
@@ -27,7 +29,8 @@
     CHECK(sioobe_ != NULL);
   }
 
-  JNIEnv* env_;
+  JavaVMExt* vm_;
+  JNIEnvExt* env_;
   jclass aioobe_;
   jclass sioobe_;
 };
@@ -60,29 +63,39 @@
   EXPECT_TRUE(env_->ExceptionCheck()); \
   env_->ExceptionClear()
 
+std::string gCheckJniAbortMessage;
+void TestCheckJniAbortHook(const std::string& reason) {
+  gCheckJniAbortMessage = reason;
+}
+
 TEST_F(JniInternalTest, FindClass) {
   // TODO: when these tests start failing because you're calling FindClass
   // with a pending exception, fix EXPECT_CLASS_NOT_FOUND to assert that an
   // exception was thrown and clear the exception.
 
-  // TODO: . is only allowed as an alternative to / if CheckJNI is off.
-
   // Reference types...
-  // You can't include the "L;" in a JNI class descriptor.
   EXPECT_CLASS_FOUND("java/lang/String");
-  EXPECT_CLASS_NOT_FOUND("Ljava/lang/String;");
-  // We support . as well as / for compatibility.
-  EXPECT_CLASS_FOUND("java.lang.String");
-  EXPECT_CLASS_NOT_FOUND("Ljava.lang.String;");
   // ...for arrays too, where you must include "L;".
   EXPECT_CLASS_FOUND("[Ljava/lang/String;");
-  EXPECT_CLASS_NOT_FOUND("[java/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");
 
+  // 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;
+
   // 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;
   // But primitive types aren't allowed...
   EXPECT_CLASS_NOT_FOUND("C");
   EXPECT_CLASS_NOT_FOUND("K");
@@ -93,8 +106,8 @@
     EXPECT_TRUE(env_->ExceptionCheck()); \
     jthrowable exception = env_->ExceptionOccurred(); \
     EXPECT_NE(static_cast<jthrowable>(NULL), exception); \
-    EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class)); \
     env_->ExceptionClear(); \
+    EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class)); \
   } while (false)
 
 TEST_F(JniInternalTest, GetFieldID) {
@@ -514,7 +527,10 @@
 }
 
 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;
 
   jstring s = env_->NewStringUTF("hello");
   ASSERT_TRUE(s != NULL);
@@ -704,7 +720,9 @@
   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;
 
   s = env_->NewStringUTF("");
   ASSERT_TRUE(s != NULL);
@@ -741,8 +759,10 @@
   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;
 
   jobject o1 = env_->NewGlobalRef(s);
   ASSERT_TRUE(o1 != NULL);
@@ -779,8 +799,10 @@
   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;
 
   jobject o1 = env_->NewWeakGlobalRef(s);
   ASSERT_TRUE(o1 != NULL);
@@ -1508,8 +1530,9 @@
 
   EXPECT_EQ(JNI_OK, env_->Throw(exception));
   EXPECT_TRUE(env_->ExceptionCheck());
-  EXPECT_TRUE(env_->IsSameObject(exception, env_->ExceptionOccurred()));
+  jthrowable thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
+  EXPECT_TRUE(env_->IsSameObject(exception, thrown_exception));
 }
 
 TEST_F(JniInternalTest, ThrowNew) {
@@ -1520,8 +1543,9 @@
 
   EXPECT_EQ(JNI_OK, env_->ThrowNew(exception_class, "hello world"));
   EXPECT_TRUE(env_->ExceptionCheck());
-  EXPECT_TRUE(env_->IsInstanceOf(env_->ExceptionOccurred(), exception_class));
+  jthrowable thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
+  EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
 }
 
 // TODO: this test is DISABLED until we can actually run java.nio.Buffer's <init>.