// 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");
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);
// 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");
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");
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[]");
// Don't leak the elements array.
env_->ReleaseBooleanArrayElements(array, elements, 0);
}
{
jbyteArray array = env_->NewByteArray(10);
jboolean is_copy;
jbyte* elements = env_->GetByteArrayElements(array, &is_copy);
env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array), reinterpret_cast<jboolean*>(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), reinterpret_cast<jboolean*>(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");
// Don't leak the elements array.
env_->ReleaseByteArrayElements(array, elements, 0);
}
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");
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));
}
TEST_F(JniInternalTest, AllocObject) {
jclass c = env_->FindClass("java/lang/String");
ASSERT_NE(c, nullptr);
jobject o = env_->AllocObject(c);
ASSERT_NE(o, nullptr);
// We have an instance of the class we asked for...
ASSERT_TRUE(env_->IsInstanceOf(o, c)); // ...whose fields haven't been initialized because // we didn't call a constructor. // Even with string compression empty string has `count == 0`.
ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "count", "I")));
}
// Wrong type where type doesn't exist.
fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", "Lrod/jane/freddy;");
EXPECT_EQ(nullptr, fid);
ExpectException(jlnsfe);
// Check that no exceptions are pending.
ASSERT_FALSE(env_->ExceptionCheck());
// Check that java.lang.Object.foo() doesn't exist and NoSuchMethodError is // a pending exception.
jmethodID method = env_->GetMethodID(jlobject, "foo", "()V");
EXPECT_EQ(nullptr, method);
ExpectException(jlnsme);
// Check that java.lang.Object.equals() does exist.
method = env_->GetMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z");
EXPECT_NE(nullptr, method);
EXPECT_FALSE(env_->ExceptionCheck());
// Check that GetMethodID for java.lang.String.valueOf(int) fails as the // method is static.
method = env_->GetMethodID(jlstring, "valueOf", "(I)Ljava/lang/String;");
EXPECT_EQ(nullptr, method);
ExpectException(jlnsme);
// Check that GetMethodID for java.lang.NoSuchMethodError.<init>(String) finds the constructor.
method = env_->GetMethodID(jlnsme, "<init>", "(Ljava/lang/String;)V");
EXPECT_NE(nullptr, method);
EXPECT_FALSE(env_->ExceptionCheck());
// Check that GetMethodID can find a interface method inherited from another interface.
method = env_->GetMethodID(jncrbc, "close", "()V");
EXPECT_NE(nullptr, method);
EXPECT_FALSE(env_->ExceptionCheck());
// Bad arguments.
GetMethodIdBadArgumentTest(false);
GetMethodIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, CallVarArgMethodBadPrimitive) { // Check that bad primitive values cause check JNI to abort when // passed out-of-range primitive value var args. As var args can't // differentiate type sizes less than an int, and this isn't // corrected by JNI, this helps ensure JNI code is valid. #define DoCall(boxed_type, shorty, c_type, bad_value) \
{ \
jclass prim_class = env_->FindClass("java/lang/"#boxed_type); \
jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf", \ "("#shorty")Ljava/lang/"#boxed_type";"); \
EXPECT_NE(nullptr, method); \
EXPECT_FALSE(env_->ExceptionCheck()); \
CheckJniAbortCatcher check_jni_abort_catcher; \
env_->CallStaticObjectMethod(prim_class, method, bad_value); \
check_jni_abort_catcher.Check("unexpected "#c_type" value: "#bad_value); \
}
// Check that no exceptions are pending
ASSERT_FALSE(env_->ExceptionCheck());
// Check that java.lang.Object.foo() doesn't exist and NoSuchMethodError is // a pending exception
jmethodID method = env_->GetStaticMethodID(jlobject, "foo", "()V");
EXPECT_EQ(nullptr, method);
ExpectException(jlnsme);
// Check that GetStaticMethodID for java.lang.Object.equals(Object) fails as // the method is not static
method = env_->GetStaticMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z");
EXPECT_EQ(nullptr, method);
ExpectException(jlnsme);
// Check that java.lang.String.valueOf(int) does exist
jclass jlstring = env_->FindClass("java/lang/String");
method = env_->GetStaticMethodID(jlstring, "valueOf", "(I)Ljava/lang/String;");
EXPECT_NE(nullptr, method);
EXPECT_FALSE(env_->ExceptionCheck());
// Bad arguments.
GetStaticMethodIdBadArgumentTest(false);
GetStaticMethodIdBadArgumentTest(true);
}
jmethodID mid = env_->GetMethodID(c, "<init>", "()V");
ASSERT_NE(mid, nullptr); // Turn the mid into a java.lang.reflect.Constructor...
jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE);
size_t capacity_before = GetLocalsCapacity(env_); for (size_t i = 0; i <= 10; ++i) { // Regression test for b/18396311, ToReflectedMethod leaking local refs causing a local // reference table overflows with 512 references to ArtMethod
env_->DeleteLocalRef(env_->ToReflectedMethod(c, mid, JNI_FALSE));
}
size_t capacity_after = GetLocalsCapacity(env_);
ASSERT_EQ(capacity_before, capacity_after);
ASSERT_NE(method, nullptr);
ASSERT_TRUE(env_->IsInstanceOf(method, jlrConstructor)); // ...and back again.
jmethodID mid2 = env_->FromReflectedMethod(method);
ASSERT_NE(mid2, nullptr); // Make sure we can actually use it.
jstring s = reinterpret_cast<jstring>(env_->AllocObject(c));
ASSERT_NE(s, nullptr);
env_->CallVoidMethod(s, mid2);
ASSERT_EQ(JNI_FALSE, env_->ExceptionCheck());
env_->ExceptionClear();
mid = env_->GetMethodID(c, "length", "()I");
ASSERT_NE(mid, nullptr); // Turn the mid into a java.lang.reflect.Method...
method = env_->ToReflectedMethod(c, mid, JNI_FALSE);
ASSERT_NE(method, nullptr);
ASSERT_TRUE(env_->IsInstanceOf(method, jlrMethod)); // ...and back again.
mid2 = env_->FromReflectedMethod(method);
ASSERT_NE(mid2, nullptr); // Make sure we can actually use it.
s = env_->NewStringUTF("poop");
ASSERT_NE(s, nullptr);
ASSERT_EQ(4, env_->CallIntMethod(s, mid2));
// Bad arguments.
GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(false);
GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(true);
}
staticvoid BogusMethod() { // You can't pass null function pointers to RegisterNatives.
}
// Check that no exceptions are pending.
ASSERT_FALSE(env_->ExceptionCheck());
// The following can print errors to the log we'd like to ignore.
{
ScopedLogSeverity sls(LogSeverity::FATAL); // Check that registering method without name causes a NoSuchMethodError.
{
JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
}
ExpectException(jlnsme);
// Check that registering method without signature causes a NoSuchMethodError.
{
JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
}
ExpectException(jlnsme);
// Check that registering method without function causes a NoSuchMethodError.
{
JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
}
ExpectException(jlnsme);
// Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
{
JNINativeMethod methods[] = { { "foo", "()V", native_function } };
EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
}
ExpectException(jlnsme);
// Check that registering no methods isn't a failure.
{
JNINativeMethod methods[] = { };
EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 0), JNI_OK);
}
EXPECT_FALSE(env_->ExceptionCheck());
EXPECT_EQ(env_->UnregisterNatives(jlobject), JNI_OK);
// Check that registering a -ve number of methods is a failure.
CheckJniAbortCatcher check_jni_abort_catcher; for (int i = -10; i < 0; ++i) {
JNINativeMethod methods[] = { };
EXPECT_EQ(env_->RegisterNatives(jlobject, methods, i), JNI_ERR);
check_jni_abort_catcher.Check("negative method count: ");
}
EXPECT_FALSE(env_->ExceptionCheck());
// Unregistering a class with no natives is a warning.
EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK);
// Attempt to incorrect create an array of strings with initial value of string arrays.
CheckJniAbortCatcher jni_abort_catcher;
env_->NewObjectArray(2, element_class, a);
jni_abort_catcher.Check("cannot assign object of type 'java.lang.String[]' to array with element " "type of 'java.lang.String'");
}
TEST_F(JniInternalTest, GetArrayLength) { // Already tested in NewObjectArray/NewPrimitiveArray except for null.
CheckJniAbortCatcher jni_abort_catcher; bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(0, env_->GetArrayLength(nullptr));
jni_abort_catcher.Check("java_array == null");
EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
EXPECT_EQ(JNI_ERR, env_->GetArrayLength(nullptr));
jni_abort_catcher.Check("jarray was NULL");
EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
// A superclass is assignable from an instance of its // subclass but not vice versa.
ASSERT_TRUE(env_->IsAssignableFrom(string_class, object_class));
ASSERT_FALSE(env_->IsAssignableFrom(object_class, string_class));
// An interface is assignable from an instance of an implementing // class but not vice versa.
ASSERT_TRUE(env_->IsAssignableFrom(string_class, charsequence_interface));
ASSERT_FALSE(env_->IsAssignableFrom(charsequence_interface, string_class));
// The surrogate pair gets encoded into a 4 byte UTF sequence..
EXPECT_EQ(4, env_->GetStringUTFLength(s)); constchar* chars = env_->GetStringUTFChars(s, nullptr);
EXPECT_STREQ("\xf0\x90\x90\x80", chars);
env_->ReleaseStringUTFChars(s, chars);
// .. but is stored as is in the utf-16 representation. const jchar* jchars = env_->GetStringChars(s, nullptr);
EXPECT_EQ(0xd801, jchars[0]);
EXPECT_EQ(0xdc00, jchars[1]);
env_->ReleaseStringChars(s, jchars);
// 4 byte UTF sequence appended to an encoded surrogate pair.
s = env_->NewStringUTF("\xed\xa0\x81\xed\xb0\x80 \xf0\x9f\x8f\xa0");
EXPECT_NE(s, nullptr);
// The 4 byte sequence {0xf0, 0x9f, 0x8f, 0xa0} is converted into a surrogate // pair {0xd83c, 0xdfe0}.
EXPECT_EQ(5, env_->GetStringLength(s));
jchars = env_->GetStringChars(s, nullptr); // The first surrogate pair, encoded as such in the input.
EXPECT_EQ(0xd801, jchars[0]);
EXPECT_EQ(0xdc00, jchars[1]); // The second surrogate pair, from the 4 byte UTF sequence in the input.
EXPECT_EQ(0xd83c, jchars[3]);
EXPECT_EQ(0xdfe0, jchars[4]);
env_->ReleaseStringChars(s, jchars);
// A string with 1, 2, 3 and 4 byte UTF sequences with spaces // between them
s = env_->NewStringUTF("\x24 \xc2\xa2 \xe2\x82\xac \xf0\x9f\x8f\xa0");
EXPECT_NE(s, nullptr);
EXPECT_EQ(8, env_->GetStringLength(s));
EXPECT_EQ(13, env_->GetStringUTFLength(s));
}
TEST_F(JniInternalTest, NewStringUTF_Validation) { // For the following tests, allocate two pages, one R/W and the next inaccessible.
std::string error_msg;
MemMap head_map = MemMap::MapAnonymous( "head", 2 * gPageSize, PROT_READ | PROT_WRITE, /*low_4gb=*/ false, &error_msg);
ASSERT_TRUE(head_map.IsValid()) << error_msg;
MemMap tail_map = head_map.RemapAtEnd(
head_map.Begin() + gPageSize, "tail", PROT_NONE, &error_msg);
ASSERT_TRUE(tail_map.IsValid()) << error_msg; char* utf_src = reinterpret_cast<char*>(head_map.Begin());
// Prepare for checking the `count` field.
jclass c = env_->FindClass("java/lang/String");
ASSERT_NE(c, nullptr);
jfieldID count_fid = env_->GetFieldID(c, "count", "I");
ASSERT_TRUE(count_fid != nullptr);
// Prepare for testing with the unchecked interface. const JNINativeInterface* base_env = down_cast<JNIEnvExt*>(env_)->GetUncheckedFunctions();
// It's okay for the buffer to be null as long as the length is 0.
env_->GetStringRegion(s, 2, 0, nullptr); // Even if the offset is invalid...
env_->GetStringRegion(s, 123, 0, nullptr);
ExpectException(sioobe_);
env_->GetStringUTFRegion(s, -1, 0, nullptr);
ExpectException(sioobe_);
env_->GetStringUTFRegion(s, 0, -1, nullptr);
ExpectException(sioobe_);
env_->GetStringUTFRegion(s, 0, 10, nullptr);
ExpectException(sioobe_);
env_->GetStringUTFRegion(s, 10, 1, nullptr);
ExpectException(sioobe_); // Regression test against integer overflow in range check.
env_->GetStringUTFRegion(s, 0x7fffffff, 0x7fffffff, nullptr);
ExpectException(sioobe_);
char bytes[5] = { 'x', 'x', 'x', 'x', 'x' };
env_->GetStringUTFRegion(s, 1, 2, &bytes[1]);
EXPECT_EQ('x', bytes[0]);
EXPECT_EQ('e', bytes[1]);
EXPECT_EQ('l', bytes[2]); // NB: The output string is null terminated so this slot is overwritten.
EXPECT_EQ('\0', bytes[3]);
EXPECT_EQ('x', bytes[4]);
// It's okay for the buffer to be null as long as the length is 0.
env_->GetStringUTFRegion(s, 2, 0, nullptr); // Even if the offset is invalid...
env_->GetStringUTFRegion(s, 123, 0, nullptr);
ExpectException(sioobe_); // If not null we still have a 0 length string
env_->GetStringUTFRegion(s, 1, 0, &bytes[1]);
EXPECT_EQ('x', bytes[0]);
EXPECT_EQ('\0', bytes[1]);
EXPECT_EQ('l', bytes[2]);
EXPECT_EQ('\0', bytes[3]);
EXPECT_EQ('x', bytes[4]);
}
TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) { // Passing in a null 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);
}
{
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");
ASSERT_TRUE(s != nullptr);
TEST_F(JniInternalTest, DeleteLocalRef) { // This tests leads to warnings and errors in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
env_->DeleteLocalRef(s);
// Currently, deleting an already-deleted reference is just a CheckJNI abort.
{ bool old_check_jni = vm_->SetCheckJniEnabled(true);
CheckJniAbortCatcher check_jni_abort_catcher;
env_->DeleteLocalRef(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("");
ASSERT_NE(s, nullptr);
jobject o = env_->NewLocalRef(s);
ASSERT_NE(o, nullptr);
TEST_F(JniInternalTest, PushLocalFrame_10395422) { // The JNI specification is ambiguous about whether the given capacity is to be interpreted as a // maximum or as a minimum, but it seems like it's supposed to be a minimum, and that's how // Android historically treated it, and it's how the RI treats it. It's also the more useful // interpretation!
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(0));
env_->PopLocalFrame(nullptr);
// The following two tests will print errors to the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
// Negative capacities are not allowed.
ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(-1));
}
TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) { // This tests leads to errors in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
jobject original = env_->NewStringUTF("");
ASSERT_NE(original, nullptr);
EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(outer));
{
CheckJniAbortCatcher check_jni_abort_catcher;
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
check_jni_abort_catcher.Check("jobject is an invalid local reference");
}
// Our local reference for the survivor is invalid because the survivor // gets a new local reference...
{
CheckJniAbortCatcher check_jni_abort_catcher;
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
check_jni_abort_catcher.Check("jobject is an invalid local reference");
}
EXPECT_EQ(env_->PopLocalFrame(nullptr), nullptr);
}
EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
CheckJniAbortCatcher check_jni_abort_catcher;
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(outer));
check_jni_abort_catcher.Check("jobject is an invalid local reference");
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
check_jni_abort_catcher.Check("jobject is an invalid local reference");
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
check_jni_abort_catcher.Check("jobject is an invalid local reference");
}
TEST_F(JniInternalTest, PushLocalFrame_LimitAndOverflow) { // Try a very large value that should fail.
ASSERT_NE(JNI_OK, env_->PushLocalFrame(std::numeric_limits<jint>::max()));
ASSERT_TRUE(env_->ExceptionCheck());
env_->ExceptionClear();
// On 32-bit, also check for some overflow conditions. #ifndef __LP64__
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(10));
ASSERT_NE(JNI_OK, env_->PushLocalFrame(std::numeric_limits<jint>::max() - 10));
ASSERT_TRUE(env_->ExceptionCheck());
env_->ExceptionClear();
EXPECT_EQ(env_->PopLocalFrame(nullptr), nullptr); #endif
}
TEST_F(JniInternalTest, PushLocalFrame_b62223672) { // The 512 entry limit has been lifted, try a larger value.
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(1024));
EXPECT_EQ(env_->PopLocalFrame(nullptr), nullptr);
}
TEST_F(JniInternalTest, DeleteGlobalRef) { // This tests leads to warnings and errors in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
jobject o = env_->NewGlobalRef(s);
ASSERT_NE(o, nullptr);
env_->DeleteGlobalRef(o);
// Currently, deleting an already-deleted reference is just a CheckJNI abort.
{ bool old_check_jni = vm_->SetCheckJniEnabled(true);
CheckJniAbortCatcher check_jni_abort_catcher;
env_->DeleteGlobalRef(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));
}
TEST_F(JniInternalTest, DeleteWeakGlobalRef) { // This tests leads to warnings and errors in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
jobject o = env_->NewWeakGlobalRef(s);
ASSERT_NE(o, nullptr);
env_->DeleteWeakGlobalRef(o);
// Currently, deleting an already-deleted reference is just a CheckJNI abort.
{ bool old_check_jni = vm_->SetCheckJniEnabled(true);
CheckJniAbortCatcher check_jni_abort_catcher;
env_->DeleteWeakGlobalRef(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));
}
TEST_F(JniInternalTest, ExceptionDescribe) { // This checks how ExceptionDescribe handles call without exception.
env_->ExceptionClear();
env_->ExceptionDescribe();
}
// Check we don't crash if a nullptr is passed to field accessors.
ASSERT_EQ(env_->GetDirectBufferAddress(nullptr), nullptr);
ASSERT_EQ(env_->GetDirectBufferCapacity(nullptr), -1L);
// Check if j.n.Buffer types backed by heap memory return the invalid values described in the // RETURNS clauses of JNI spec for GetDirectBufferAddress() and GetDirectBufferCapacity().
ScopedLocalRef<jclass> bb(env_, env_->FindClass("java/nio/ByteBuffer"));
jmethodID bb_allocate = env_->GetStaticMethodID(bb.get(), "allocate", "(I)Ljava/nio/ByteBuffer;");
jobject heap_buffer = env_->CallStaticObjectMethod(bb.get(), bb_allocate, 128);
ASSERT_NE(heap_buffer, nullptr);
ASSERT_EQ(env_->GetDirectBufferAddress(heap_buffer), nullptr);
ASSERT_EQ(env_->GetDirectBufferCapacity(heap_buffer), -1L);
// Check invalid values are returned if the buffer argument has an object type is not a sub-type // of j.n.Buffer.
jobject not_buffer = env_->NewStringUTF("A String");
ASSERT_EQ(env_->GetDirectBufferAddress(not_buffer), nullptr);
ASSERT_EQ(env_->GetDirectBufferCapacity(not_buffer), -1L);
// Recursively lock a lot
size_t max_recursive_lock = 1024; for (size_t i = 0; i < max_recursive_lock; i++) {
env_->MonitorEnter(object);
EXPECT_FALSE(env_->ExceptionCheck());
} // Recursively unlock a lot for (size_t i = 0; i < max_recursive_lock; i++) {
env_->MonitorExit(object);
EXPECT_FALSE(env_->ExceptionCheck());
}
// It's an error to call MonitorEnter or MonitorExit on null.
{
CheckJniAbortCatcher check_jni_abort_catcher;
env_->MonitorEnter(nullptr);
check_jni_abort_catcher.Check("in call to MonitorEnter");
env_->MonitorExit(nullptr);
check_jni_abort_catcher.Check("in call to MonitorExit");
}
}
void Java_MyClassNatives_foo_exit(JNIEnv* env, jobject thisObj) { // Release the monitor on self. This should trigger an abort.
env->MonitorExit(thisObj);
}
void Java_MyClassNatives_foo_enter_no_exit(JNIEnv* env, jobject thisObj) { // Acquire but don't release the monitor on self. This should trigger an abort on return.
env->MonitorEnter(thisObj);
}
CheckJniAbortCatcher check_jni_abort_catcher;
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
check_jni_abort_catcher.Check("Still holding a locked object on JNI end");
}
staticbool IsLocked(JNIEnv* env, jobject jobj) {
ScopedObjectAccess soa(env);
LockWord lock_word = soa.Decode<mirror::Object>(jobj)->GetLockWord(true); switch (lock_word.GetState()) { case LockWord::kHashCode: case LockWord::kUnlocked: returnfalse; case LockWord::kThinLocked: returntrue; case LockWord::kFatLocked: return lock_word.FatLockMonitor()->IsLocked(); default: {
LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
UNREACHABLE();
}
}
}
TEST_F(JniInternalTest, DetachThreadUnlockJNIMonitors) { // We need to lock an object, detach, reattach, and check the locks. // // As re-attaching will create a different thread, we need to use a global // ref to keep the object around.
// Look at the global ref, check whether it's still locked.
ASSERT_FALSE(IsLocked(env_, global_ref));
// Delete the global ref.
env_->DeleteGlobalRef(global_ref);
}
// Test the offset computation of IndirectReferenceTable offsets. b/26071368.
TEST_F(JniInternalTest, IndirectReferenceTableOffsets) {
ScopedObjectAccess soa(Thread::Current()); // The segment_state_ field is private, and we want to avoid friend declaration. So we'll check // by modifying LRT state and checking the memory contents directly. // The parameters don't really matter here.
std::string error_msg;
jni::LocalReferenceTable lrt(/*check_jni=*/ true); bool success = lrt.Initialize(/*max_count=*/ 5, &error_msg);
ASSERT_TRUE(success) << error_msg;
// Pop the LRT frames and check state transitions.
lrt.PopFrame(cookie2);
ASSERT_EQ(expected_cookie2.top_index, previous_state->top_index);
ASSERT_EQ(expected_cookie2.top_index, segment_state->top_index);
lrt.PopFrame(cookie1);
ASSERT_EQ(jni::kLRTFirstSegment.top_index, previous_state->top_index);
ASSERT_EQ(expected_cookie2.top_index, segment_state->top_index);
lrt.PopFrame(cookie0);
ASSERT_EQ(jni::kLRTFirstSegment.top_index, previous_state->top_index);
ASSERT_EQ(jni::kLRTFirstSegment.top_index, segment_state->top_index);
}
// Test the offset computation of JNIEnvExt offsets. b/26071368.
TEST_F(JniInternalTest, JNIEnvExtOffsets) {
EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self_),
JNIEnvExt::SelfOffset(kRuntimePointerSize).Uint32Value());
// `previous_state_` amd `segment_state_` are private in the IndirectReferenceTable. // So this test isn't as good as we'd hope it to be.
uint32_t previous_state_now =
OFFSETOF_MEMBER(JNIEnvExt, locals_) +
jni::LocalReferenceTable::PreviousStateOffset().Uint32Value();
uint32_t previous_state_computed =
JNIEnvExt::LrtPreviousStateOffset(kRuntimePointerSize).Uint32Value();
EXPECT_EQ(previous_state_now, previous_state_computed);
uint32_t segment_state_now =
OFFSETOF_MEMBER(JNIEnvExt, locals_) +
jni::LocalReferenceTable::SegmentStateOffset().Uint32Value();
uint32_t segment_state_computed =
JNIEnvExt::LrtSegmentStateOffset(kRuntimePointerSize).Uint32Value();
EXPECT_EQ(segment_state_now, segment_state_computed);
}
TEST_F(JniInternalTest, NonAttachedThread) { // This tests leads to warnings and errors in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
CheckJniAbortCatcher check_jni_abort_catcher;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.