Wire up check JNI force copy mode.

Increase check JNI checks.
Break apart jni_internal.h in to jni_env_ext.h and java_vm_ext.h.
Fix the abuse of ScopedObjectAccess/annotalysis by ScopedCheck in the case
of VM routines.
Make class loader override and shared library class loader JNI global
references rather than mirror pointers.
Clean-ups to native bridge.

Change-Id: If7c6110b5aade7a402bfb67534af86a7b2cdeb55
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index da3080f..844d14a 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -17,6 +17,7 @@
 #include "jni_internal.h"
 
 #include "common_compiler_test.h"
+#include "java_vm_ext.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/string-inl.h"
 #include "scoped_thread_state_change.h"
@@ -53,24 +54,15 @@
   }
 
   void ExpectException(jclass exception_class) {
-    EXPECT_TRUE(env_->ExceptionCheck());
+    ScopedObjectAccess soa(env_);
+    EXPECT_TRUE(env_->ExceptionCheck())
+        << PrettyDescriptor(soa.Decode<mirror::Class*>(exception_class));
     jthrowable exception = env_->ExceptionOccurred();
     EXPECT_NE(nullptr, exception);
     env_->ExceptionClear();
     EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class));
   }
 
-  void ExpectClassFound(const char* name) {
-    EXPECT_NE(env_->FindClass(name), nullptr) << name;
-    EXPECT_FALSE(env_->ExceptionCheck()) << name;
-  }
-
-  void ExpectClassNotFound(const char* name) {
-    EXPECT_EQ(env_->FindClass(name), nullptr) << name;
-    EXPECT_TRUE(env_->ExceptionCheck()) << name;
-    env_->ExceptionClear();
-  }
-
   void CleanUpJniEnv() {
     if (aioobe_ != nullptr) {
       env_->DeleteGlobalRef(aioobe_);
@@ -98,6 +90,510 @@
     return soa.AddLocalReference<jclass>(c);
   }
 
+  void ExpectClassFound(const char* name) {
+    EXPECT_NE(env_->FindClass(name), nullptr) << name;
+    EXPECT_FALSE(env_->ExceptionCheck()) << name;
+  }
+
+  void ExpectClassNotFound(const char* name, bool check_jni, const char* check_jni_msg,
+                           CheckJniAbortCatcher* abort_catcher) {
+    EXPECT_EQ(env_->FindClass(name), nullptr) << name;
+    if (!check_jni || check_jni_msg == nullptr) {
+      EXPECT_TRUE(env_->ExceptionCheck()) << name;
+      env_->ExceptionClear();
+    } else {
+      abort_catcher->Check(check_jni_msg);
+    }
+  }
+
+  void FindClassTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    // Null argument is always an abort.
+    env_->FindClass(nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+
+    // Reference types...
+    ExpectClassFound("java/lang/String");
+    // ...for arrays too, where you must include "L;".
+    ExpectClassFound("[Ljava/lang/String;");
+    // Primitive arrays are okay too, if the primitive type is valid.
+    ExpectClassFound("[C");
+
+    // But primitive types aren't allowed...
+    ExpectClassNotFound("C", check_jni, nullptr, &check_jni_abort_catcher);
+    ExpectClassNotFound("V", check_jni, nullptr, &check_jni_abort_catcher);
+    ExpectClassNotFound("K", check_jni, nullptr, &check_jni_abort_catcher);
+
+    if (check_jni) {
+      // Check JNI will reject invalid class names as aborts but without pending exceptions.
+      EXPECT_EQ(env_->FindClass("java.lang.String"), nullptr);
+      EXPECT_FALSE(env_->ExceptionCheck());
+      check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
+
+      EXPECT_EQ(env_->FindClass("[Ljava.lang.String;"), nullptr);
+      EXPECT_FALSE(env_->ExceptionCheck());
+      check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
+    } else {
+      // Without check JNI we're tolerant and replace '.' with '/'.
+      ExpectClassFound("java.lang.String");
+      ExpectClassFound("[Ljava.lang.String;");
+    }
+
+    ExpectClassNotFound("Ljava.lang.String;", check_jni, "illegal class name 'Ljava.lang.String;'",
+                        &check_jni_abort_catcher);
+    ExpectClassNotFound("[java.lang.String", check_jni, "illegal class name '[java.lang.String'",
+                        &check_jni_abort_catcher);
+
+    // You can't include the "L;" in a JNI class descriptor.
+    ExpectClassNotFound("Ljava/lang/String;", check_jni, "illegal class name 'Ljava/lang/String;'",
+                        &check_jni_abort_catcher);
+
+    // But you must include it for an array of any reference type.
+    ExpectClassNotFound("[java/lang/String", check_jni, "illegal class name '[java/lang/String'",
+                        &check_jni_abort_catcher);
+
+    ExpectClassNotFound("[K", check_jni, "illegal class name '[K'", &check_jni_abort_catcher);
+
+    // Void arrays aren't allowed.
+    ExpectClassNotFound("[V", check_jni, "illegal class name '[V'", &check_jni_abort_catcher);
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetFieldIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+
+    jfieldID fid = env_->GetFieldID(nullptr, "count", "I");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "GetFieldID received NULL jclass"
+                                            : "java_class == null");
+    fid = env_->GetFieldID(c, nullptr, "I");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    fid = env_->GetFieldID(c, "count", nullptr);
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetStaticFieldIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+
+    jfieldID fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "GetStaticFieldID received NULL jclass"
+                                            : "java_class == null");
+    fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetMethodIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jmethodID method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "GetMethodID received NULL jclass"
+                                            : "java_class == null");
+    jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
+    ASSERT_TRUE(jlnsme != nullptr);
+    method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    method = env_->GetMethodID(jlnsme, "<init>", nullptr);
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetStaticMethodIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jmethodID method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "GetStaticMethodID received NULL jclass"
+                                            : "java_class == null");
+    jclass jlstring = env_->FindClass("java/lang/String");
+    method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetFromReflectedField_ToReflectedFieldBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+    jfieldID fid = env_->GetFieldID(c, "count", "I");
+    ASSERT_NE(fid, nullptr);
+
+    // Check class argument for null argument, not checked in non-check JNI.
+    jobject field = env_->ToReflectedField(nullptr, fid, JNI_FALSE);
+    if (check_jni) {
+      EXPECT_EQ(field, nullptr);
+      check_jni_abort_catcher.Check("ToReflectedField received NULL jclass");
+    } else {
+      EXPECT_NE(field, nullptr);
+    }
+
+    field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
+    EXPECT_EQ(field, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "jfieldID was NULL"
+                                            : "fid == null");
+
+    fid = env_->FromReflectedField(nullptr);
+    ASSERT_EQ(fid, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "expected non-null java.lang.reflect.Field"
+                                            : "jlr_field == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+    jmethodID mid = env_->GetMethodID(c, "<init>", "()V");
+    ASSERT_NE(mid, nullptr);
+
+    // Check class argument for null argument, not checked in non-check JNI.
+    jobject method = env_->ToReflectedMethod(nullptr, mid, JNI_FALSE);
+    if (check_jni) {
+      EXPECT_EQ(method, nullptr);
+      check_jni_abort_catcher.Check("ToReflectedMethod received NULL jclass");
+    } else {
+      EXPECT_NE(method, nullptr);
+    }
+
+    method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
+    EXPECT_EQ(method, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "jmethodID was NULL"
+                                            : "mid == null");
+    mid = env_->FromReflectedMethod(method);
+    ASSERT_EQ(mid, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "expected non-null method" : "jlr_method == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void RegisterAndUnregisterNativesBadArguments(bool check_jni,
+                                                CheckJniAbortCatcher* check_jni_abort_catcher) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    // Passing a class of null is a failure.
+    {
+      JNINativeMethod methods[] = { };
+      EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
+      check_jni_abort_catcher->Check(check_jni ? "RegisterNatives received NULL jclass"
+                                               : "java_class == null");
+    }
+
+    // Passing methods as null is a failure.
+    jclass jlobject = env_->FindClass("java/lang/Object");
+    EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
+    check_jni_abort_catcher->Check("methods == null");
+
+    // Unregisters null is a failure.
+    EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
+    check_jni_abort_catcher->Check(check_jni ? "UnregisterNatives received NULL jclass"
+                                             : "java_class == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+
+  void GetPrimitiveArrayElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jbooleanArray array = env_->NewBooleanArray(10);
+    jboolean is_copy;
+    EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to get byte primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to get short primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to get char primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to get int primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to get long primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to get float primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to get double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
+              nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to get boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
+              nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "attempt to get boolean primitive array elements with an object of type java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jbooleanArray array = env_->NewBooleanArray(10);
+    ASSERT_TRUE(array != nullptr);
+    jboolean is_copy;
+    jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
+    ASSERT_TRUE(elements != nullptr);
+    env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
+                                   reinterpret_cast<jbyte*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to release byte primitive array elements with an object of type boolean[]");
+    env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
+                                    reinterpret_cast<jshort*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to release short primitive array elements with an object of type boolean[]");
+    env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
+                                   reinterpret_cast<jchar*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to release char primitive array elements with an object of type boolean[]");
+    env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
+                                  reinterpret_cast<jint*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to release int primitive array elements with an object of type boolean[]");
+    env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
+                                   reinterpret_cast<jlong*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to release long primitive array elements with an object of type boolean[]");
+    env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
+                                    reinterpret_cast<jfloat*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to release float primitive array elements with an object of type boolean[]");
+    env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
+                                     reinterpret_cast<jdouble*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to release double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to release boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
+    jni_abort_catcher.Check(
+        check_jni ? "jarray argument has non-array type: java.lang.String"
+            : "attempt to release boolean primitive array elements with an object of type "
+              "java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetReleasePrimitiveArrayCriticalOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jobject object = env_->NewStringUTF("Test String");
+    jboolean is_copy;
+    void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "expected primitive array, given java.lang.String");
+    env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "expected primitive array, given java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+    constexpr size_t kLength = 10;
+    jbooleanArray array = env_->NewBooleanArray(kLength);
+    ASSERT_TRUE(array != nullptr);
+    jboolean elements[kLength];
+    env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+                             reinterpret_cast<jbyte*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to get region of byte primitive array elements with an object of type boolean[]");
+    env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+                              reinterpret_cast<jshort*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to get region of short primitive array elements with an object of type boolean[]");
+    env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+                             reinterpret_cast<jchar*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to get region of char primitive array elements with an object of type boolean[]");
+    env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+                            reinterpret_cast<jint*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to get region of int primitive array elements with an object of type boolean[]");
+    env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+                             reinterpret_cast<jlong*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to get region of long primitive array elements with an object of type boolean[]");
+    env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+                              reinterpret_cast<jfloat*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to get region of float primitive array elements with an object of type boolean[]");
+    env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+                               reinterpret_cast<jdouble*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to get region of double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to get region of boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "attempt to get region of boolean primitive array elements with an object of type "
+          "java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void SetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+    constexpr size_t kLength = 10;
+    jbooleanArray array = env_->NewBooleanArray(kLength);
+    ASSERT_TRUE(array != nullptr);
+    jboolean elements[kLength];
+    env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+                             reinterpret_cast<jbyte*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to set region of byte primitive array elements with an object of type boolean[]");
+    env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+                              reinterpret_cast<jshort*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to set region of short primitive array elements with an object of type boolean[]");
+    env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+                             reinterpret_cast<jchar*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to set region of char primitive array elements with an object of type boolean[]");
+    env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+                            reinterpret_cast<jint*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to set region of int primitive array elements with an object of type boolean[]");
+    env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+                             reinterpret_cast<jlong*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to set region of long primitive array elements with an object of type boolean[]");
+    env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+                              reinterpret_cast<jfloat*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to set region of float primitive array elements with an object of type boolean[]");
+    env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+                               reinterpret_cast<jdouble*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to set region of double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to set region of boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "attempt to set region of boolean primitive array elements with an object of type "
+          "java.lang.String");
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void NewObjectArrayBadArguments(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jclass element_class = env_->FindClass("java/lang/String");
+    ASSERT_NE(element_class, nullptr);
+
+    env_->NewObjectArray(-1, element_class, nullptr);
+    jni_abort_catcher.Check(check_jni ? "negative jsize: -1" : "negative array length: -1");
+
+    env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
+    jni_abort_catcher.Check(check_jni ? "negative jsize: -2147483648"
+        : "negative array length: -2147483648");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
   JavaVMExt* vm_;
   JNIEnv* env_;
   jclass aioobe_;
@@ -125,48 +621,8 @@
 }
 
 TEST_F(JniInternalTest, FindClass) {
-  // Reference types...
-  ExpectClassFound("java/lang/String");
-  // ...for arrays too, where you must include "L;".
-  ExpectClassFound("[Ljava/lang/String;");
-  // Primitive arrays are okay too, if the primitive type is valid.
-  ExpectClassFound("[C");
-
-  {
-    CheckJniAbortCatcher check_jni_abort_catcher;
-    env_->FindClass(nullptr);
-    check_jni_abort_catcher.Check("name == null");
-
-    // We support . as well as / for compatibility, if -Xcheck:jni is off.
-    ExpectClassFound("java.lang.String");
-    check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
-    ExpectClassNotFound("Ljava.lang.String;");
-    check_jni_abort_catcher.Check("illegal class name 'Ljava.lang.String;'");
-    ExpectClassFound("[Ljava.lang.String;");
-    check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
-    ExpectClassNotFound("[java.lang.String");
-    check_jni_abort_catcher.Check("illegal class name '[java.lang.String'");
-
-    // You can't include the "L;" in a JNI class descriptor.
-    ExpectClassNotFound("Ljava/lang/String;");
-    check_jni_abort_catcher.Check("illegal class name 'Ljava/lang/String;'");
-
-    // But you must include it for an array of any reference type.
-    ExpectClassNotFound("[java/lang/String");
-    check_jni_abort_catcher.Check("illegal class name '[java/lang/String'");
-
-    ExpectClassNotFound("[K");
-    check_jni_abort_catcher.Check("illegal class name '[K'");
-
-    // Void arrays aren't allowed.
-    ExpectClassNotFound("[V");
-    check_jni_abort_catcher.Check("illegal class name '[V'");
-  }
-
-  // But primitive types aren't allowed...
-  ExpectClassNotFound("C");
-  ExpectClassNotFound("V");
-  ExpectClassNotFound("K");
+  FindClassTest(false);
+  FindClassTest(true);
 }
 
 TEST_F(JniInternalTest, GetFieldID) {
@@ -208,16 +664,8 @@
   ExpectException(jlnsfe);
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  fid = env_->GetFieldID(nullptr, "count", "I");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("java_class == null");
-  fid = env_->GetFieldID(c, nullptr, "I");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("name == null");
-  fid = env_->GetFieldID(c, "count", nullptr);
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("sig == null");
+  GetFieldIdBadArgumentTest(false);
+  GetFieldIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, GetStaticFieldID) {
@@ -253,16 +701,8 @@
   ExpectException(jlnsfe);
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("java_class == null");
-  fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("name == null");
-  fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("sig == null");
+  GetStaticFieldIdBadArgumentTest(false);
+  GetStaticFieldIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, GetMethodID) {
@@ -302,16 +742,8 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("java_class == null");
-  method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("name == null");
-  method = env_->GetMethodID(jlnsme, "<init>", nullptr);
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("sig == null");
+  GetMethodIdBadArgumentTest(false);
+  GetMethodIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, GetStaticMethodID) {
@@ -340,16 +772,8 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("java_class == null");
-  method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("name == null");
-  method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("sig == null");
+  GetStaticMethodIdBadArgumentTest(false);
+  GetStaticMethodIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
@@ -370,13 +794,8 @@
   ASSERT_EQ(4, env_->GetIntField(s, fid2));
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
-  EXPECT_EQ(field, nullptr);
-  check_jni_abort_catcher.Check("fid == null");
-  fid2 = env_->FromReflectedField(nullptr);
-  ASSERT_EQ(fid2, nullptr);
-  check_jni_abort_catcher.Check("jlr_field == null");
+  GetFromReflectedField_ToReflectedFieldBadArgumentTest(false);
+  GetFromReflectedField_ToReflectedFieldBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
@@ -417,13 +836,8 @@
   ASSERT_EQ(4, env_->CallIntMethod(s, mid2));
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
-  EXPECT_EQ(method, nullptr);
-  check_jni_abort_catcher.Check("mid == null");
-  mid2 = env_->FromReflectedMethod(method);
-  ASSERT_EQ(mid2, nullptr);
-  check_jni_abort_catcher.Check("jlr_method == null");
+  GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(false);
+  GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(true);
 }
 
 static void BogusMethod() {
@@ -498,23 +912,11 @@
   }
   EXPECT_FALSE(env_->ExceptionCheck());
 
-  // Passing a class of null is a failure.
-  {
-    JNINativeMethod methods[] = { };
-    EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
-    check_jni_abort_catcher.Check("java_class == null");
-  }
-
-  // Passing methods as null is a failure.
-  EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
-  check_jni_abort_catcher.Check("methods == null");
-
-  // Unregisters null is a failure.
-  EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
-  check_jni_abort_catcher.Check("java_class == null");
-
   // Unregistering a class with no natives is a warning.
   EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK);
+
+  RegisterAndUnregisterNativesBadArguments(false, &check_jni_abort_catcher);
+  RegisterAndUnregisterNativesBadArguments(true, &check_jni_abort_catcher);
 }
 
 #define EXPECT_PRIMITIVE_ARRAY(new_fn, \
@@ -528,6 +930,7 @@
   \
   { \
     CheckJniAbortCatcher jni_abort_catcher; \
+    down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(false); \
     /* Allocate an negative sized array and check it has the right failure type. */ \
     EXPECT_EQ(env_->new_fn(-1), nullptr); \
     jni_abort_catcher.Check("negative array length: -1"); \
@@ -550,6 +953,7 @@
     jni_abort_catcher.Check("buf == null"); \
     env_->set_region_fn(a, 0, size, nullptr); \
     jni_abort_catcher.Check("buf == null"); \
+    down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(true); \
   } \
   /* Allocate an array and check it has the right type and length. */ \
   scalar_type ## Array a = env_->new_fn(size); \
@@ -654,189 +1058,28 @@
 }
 
 TEST_F(JniInternalTest, GetPrimitiveArrayElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  jbooleanArray array = env_->NewBooleanArray(10);
-  jboolean is_copy;
-  EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get byte primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get short primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get char primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get int primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get long primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get float primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
-            nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
-            nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get boolean primitive array elements with an object of type java.lang.String");
+  GetPrimitiveArrayElementsOfWrongType(false);
+  GetPrimitiveArrayElementsOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, ReleasePrimitiveArrayElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  jbooleanArray array = env_->NewBooleanArray(10);
-  ASSERT_TRUE(array != nullptr);
-  jboolean is_copy;
-  jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
-  ASSERT_TRUE(elements != nullptr);
-  env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
-                                 reinterpret_cast<jbyte*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release byte primitive array elements with an object of type boolean[]");
-  env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
-                                  reinterpret_cast<jshort*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release short primitive array elements with an object of type boolean[]");
-  env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
-                                 reinterpret_cast<jchar*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release char primitive array elements with an object of type boolean[]");
-  env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
-                                reinterpret_cast<jint*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release int primitive array elements with an object of type boolean[]");
-  env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
-                                 reinterpret_cast<jlong*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release long primitive array elements with an object of type boolean[]");
-  env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
-                                  reinterpret_cast<jfloat*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release float primitive array elements with an object of type boolean[]");
-  env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
-                                  reinterpret_cast<jdouble*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
-  jni_abort_catcher.Check(
-      "attempt to release boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
-  jni_abort_catcher.Check(
-      "attempt to release boolean primitive array elements with an object of type "
-      "java.lang.String");
+  ReleasePrimitiveArrayElementsOfWrongType(false);
+  ReleasePrimitiveArrayElementsOfWrongType(true);
 }
+
 TEST_F(JniInternalTest, GetReleasePrimitiveArrayCriticalOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  jobject object = env_->NewStringUTF("Test String");
-  jboolean is_copy;
-  void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
-  jni_abort_catcher.Check("expected primitive array, given java.lang.String");
-  env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
-  jni_abort_catcher.Check("expected primitive array, given java.lang.String");
+  GetReleasePrimitiveArrayCriticalOfWrongType(false);
+  GetReleasePrimitiveArrayCriticalOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, GetPrimitiveArrayRegionElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  constexpr size_t kLength = 10;
-  jbooleanArray array = env_->NewBooleanArray(kLength);
-  ASSERT_TRUE(array != nullptr);
-  jboolean elements[kLength];
-  env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
-                           reinterpret_cast<jbyte*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of byte primitive array elements with an object of type boolean[]");
-  env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
-                            reinterpret_cast<jshort*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of short primitive array elements with an object of type boolean[]");
-  env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
-                           reinterpret_cast<jchar*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of char primitive array elements with an object of type boolean[]");
-  env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
-                          reinterpret_cast<jint*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of int primitive array elements with an object of type boolean[]");
-  env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
-                           reinterpret_cast<jlong*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of long primitive array elements with an object of type boolean[]");
-  env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
-                            reinterpret_cast<jfloat*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of float primitive array elements with an object of type boolean[]");
-  env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
-                           reinterpret_cast<jdouble*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of boolean primitive array elements with an object of type "
-      "java.lang.String");
+  GetPrimitiveArrayRegionElementsOfWrongType(false);
+  GetPrimitiveArrayRegionElementsOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, SetPrimitiveArrayRegionElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  constexpr size_t kLength = 10;
-  jbooleanArray array = env_->NewBooleanArray(kLength);
-  ASSERT_TRUE(array != nullptr);
-  jboolean elements[kLength];
-  env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
-                           reinterpret_cast<jbyte*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of byte primitive array elements with an object of type boolean[]");
-  env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
-                            reinterpret_cast<jshort*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of short primitive array elements with an object of type boolean[]");
-  env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
-                           reinterpret_cast<jchar*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of char primitive array elements with an object of type boolean[]");
-  env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
-                          reinterpret_cast<jint*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of int primitive array elements with an object of type boolean[]");
-  env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
-                           reinterpret_cast<jlong*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of long primitive array elements with an object of type boolean[]");
-  env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
-                            reinterpret_cast<jfloat*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of float primitive array elements with an object of type boolean[]");
-  env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
-                           reinterpret_cast<jdouble*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of boolean primitive array elements with an object of type "
-      "java.lang.String");
+  SetPrimitiveArrayRegionElementsOfWrongType(false);
+  SetPrimitiveArrayRegionElementsOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, NewObjectArray) {
@@ -857,12 +1100,8 @@
   EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr));
 
   // Negative array length checks.
-  CheckJniAbortCatcher jni_abort_catcher;
-  env_->NewObjectArray(-1, element_class, nullptr);
-  jni_abort_catcher.Check("negative array length: -1");
-
-  env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
-  jni_abort_catcher.Check("negative array length: -2147483648");
+  NewObjectArrayBadArguments(false);
+  NewObjectArrayBadArguments(true);
 }
 
 TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) {
@@ -872,6 +1111,7 @@
   };
   ASSERT_EQ(strlen(primitive_descriptors), arraysize(primitive_names));
 
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   CheckJniAbortCatcher jni_abort_catcher;
   for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
     env_->NewObjectArray(0, nullptr, nullptr);
@@ -881,6 +1121,16 @@
     std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
     jni_abort_catcher.Check(error_msg.c_str());
   }
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
+    env_->NewObjectArray(0, nullptr, nullptr);
+    jni_abort_catcher.Check("NewObjectArray received NULL jclass");
+    jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]);
+    env_->NewObjectArray(1, primitive_class, nullptr);
+    std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
+    jni_abort_catcher.Check(error_msg.c_str());
+  }
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) {
@@ -940,8 +1190,13 @@
 
   // Null as class should fail.
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
   jni_abort_catcher.Check("java_class == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
+  jni_abort_catcher.Check("GetSuperclass received NULL jclass");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, IsAssignableFrom) {
@@ -975,10 +1230,17 @@
 
   // Null as either class should fail.
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
   jni_abort_catcher.Check("java_class1 == null");
   EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
   jni_abort_catcher.Check("java_class2 == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
+  jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+  EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
+  jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, GetObjectRefType) {
@@ -1063,10 +1325,17 @@
 
 TEST_F(JniInternalTest, NewStringNegativeLength) {
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   env_->NewString(nullptr, -1);
   jni_abort_catcher.Check("char_count < 0: -1");
   env_->NewString(nullptr, std::numeric_limits<jint>::min());
   jni_abort_catcher.Check("char_count < 0: -2147483648");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  env_->NewString(nullptr, -1);
+  jni_abort_catcher.Check("negative jsize: -1");
+  env_->NewString(nullptr, std::numeric_limits<jint>::min());
+  jni_abort_catcher.Check("negative jsize: -2147483648");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) {
@@ -1124,10 +1393,17 @@
 
 TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
   // Passing in a nullptr jstring is ignored normally, but caught by -Xcheck:jni.
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   {
     CheckJniAbortCatcher check_jni_abort_catcher;
     EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
-    check_jni_abort_catcher.Check("GetStringUTFChars received null jstring");
+  }
+  {
+    CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+    EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
+    check_jni_abort_catcher.Check("GetStringUTFChars received NULL jstring");
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   jstring s = env_->NewStringUTF("hello");
@@ -1222,10 +1498,17 @@
 
   // Null as array should fail.
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
   jni_abort_catcher.Check("java_array == null");
   env_->SetObjectArrayElement(nullptr, 0, nullptr);
   jni_abort_catcher.Check("java_array == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
+  jni_abort_catcher.Check("jarray was NULL");
+  env_->SetObjectArrayElement(nullptr, 0, nullptr);
+  jni_abort_catcher.Check("jarray was NULL");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 #define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \
@@ -1237,15 +1520,28 @@
     env_->SetStatic ## type ## Field(c, fid, value2); \
     EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \
     \
+    bool old_check_jni = vm_->SetCheckJniEnabled(false); \
+    { \
+      CheckJniAbortCatcher jni_abort_catcher; \
+      env_->GetStatic ## type ## Field(nullptr, fid); \
+      env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+    } \
     CheckJniAbortCatcher jni_abort_catcher; \
-    env_->GetStatic ## type ## Field(nullptr, fid); \
-    jni_abort_catcher.Check("received null jclass"); \
-    env_->SetStatic ## type ## Field(nullptr, fid, value1); \
-    jni_abort_catcher.Check("received null jclass"); \
     env_->GetStatic ## type ## Field(c, nullptr); \
     jni_abort_catcher.Check("fid == null"); \
     env_->SetStatic ## type ## Field(c, nullptr, value1); \
     jni_abort_catcher.Check("fid == null"); \
+    \
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+    env_->GetStatic ## type ## Field(nullptr, fid); \
+    jni_abort_catcher.Check("received NULL jclass"); \
+    env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+    jni_abort_catcher.Check("received NULL jclass"); \
+    env_->GetStatic ## type ## Field(c, nullptr); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    env_->SetStatic ## type ## Field(c, nullptr, value1); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
   } while (false)
 
 #define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \
@@ -1257,6 +1553,7 @@
     env_->Set ## type ## Field(instance, fid, value2); \
     EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \
     \
+    bool old_check_jni = vm_->SetCheckJniEnabled(false); \
     CheckJniAbortCatcher jni_abort_catcher; \
     env_->Get ## type ## Field(nullptr, fid); \
     jni_abort_catcher.Check("obj == null"); \
@@ -1266,6 +1563,16 @@
     jni_abort_catcher.Check("fid == null"); \
     env_->Set ## type ## Field(instance, nullptr, value1); \
     jni_abort_catcher.Check("fid == null"); \
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+    env_->Get ## type ## Field(nullptr, fid); \
+    jni_abort_catcher.Check("field operation on NULL object:"); \
+    env_->Set ## type ## Field(nullptr, fid, value1); \
+    jni_abort_catcher.Check("field operation on NULL object:"); \
+    env_->Get ## type ## Field(instance, nullptr); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    env_->Set ## type ## Field(instance, nullptr, value1); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
   } while (false)
 
 
@@ -1357,12 +1664,17 @@
 
   // Currently, deleting an already-deleted reference is just a CheckJNI warning.
   {
+    bool old_check_jni = vm_->SetCheckJniEnabled(false);
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      env_->DeleteLocalRef(s);
+    }
     CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
     env_->DeleteLocalRef(s);
-
-    std::string expected(StringPrintf("native code passing in reference to "
-                                      "invalid local reference: %p", s));
+    std::string expected(StringPrintf("jobject is an invalid local reference: %p", s));
     check_jni_abort_catcher.Check(expected.c_str());
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   s = env_->NewStringUTF("");
@@ -1453,12 +1765,17 @@
 
   // Currently, deleting an already-deleted reference is just a CheckJNI warning.
   {
+    bool old_check_jni = vm_->SetCheckJniEnabled(false);
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      env_->DeleteGlobalRef(o);
+    }
     CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
     env_->DeleteGlobalRef(o);
-
-    std::string expected(StringPrintf("native code passing in reference to "
-                                      "invalid global reference: %p", o));
+    std::string expected(StringPrintf("jobject is an invalid global reference: %p", o));
     check_jni_abort_catcher.Check(expected.c_str());
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   jobject o1 = env_->NewGlobalRef(s);
@@ -1498,12 +1815,17 @@
 
   // Currently, deleting an already-deleted reference is just a CheckJNI warning.
   {
+    bool old_check_jni = vm_->SetCheckJniEnabled(false);
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      env_->DeleteWeakGlobalRef(o);
+    }
     CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
     env_->DeleteWeakGlobalRef(o);
-
-    std::string expected(StringPrintf("native code passing in reference to "
-                                      "invalid weak global reference: %p", o));
+    std::string expected(StringPrintf("jobject is an invalid weak global reference: %p", o));
     check_jni_abort_catcher.Check(expected.c_str());
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   jobject o1 = env_->NewWeakGlobalRef(s);
@@ -1522,8 +1844,6 @@
 }
 
 TEST_F(JniInternalTest, Throw) {
-  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
   jclass exception_class = env_->FindClass("java/lang/RuntimeException");
   ASSERT_TRUE(exception_class != nullptr);
   jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class));
@@ -1534,11 +1854,18 @@
   jthrowable thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
   EXPECT_TRUE(env_->IsSameObject(exception, thrown_exception));
+
+  // Bad argument.
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
+  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+  check_jni_abort_catcher.Check("Throw received NULL jthrowable");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, ThrowNew) {
-  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
   jclass exception_class = env_->FindClass("java/lang/RuntimeException");
   ASSERT_TRUE(exception_class != nullptr);
 
@@ -1555,6 +1882,16 @@
   thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
   EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
+
+  // Bad argument.
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+  check_jni_abort_catcher.Check("c == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+  check_jni_abort_catcher.Check("ThrowNew received NULL jclass");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) {