// TODO: In the Baker read barrier configuration, add checks to ensure // the Marking Register's value is correct.
namespace art HIDDEN {
enumclass JniKind {
kNormal, // Regular kind of un-annotated natives.
kFast, // Native method annotated with @FastNative.
kCritical, // Native method annotated with @CriticalNative.
kCount // How many different types of JNIs we can have.
};
// Used to initialize array sizes that want to have different state per current jni. static constexpr size_t kJniKindCount = static_cast<size_t>(JniKind::kCount); // Do not use directly, use the helpers instead.
uint32_t gCurrentJni = static_cast<uint32_t>(JniKind::kNormal);
// Is the current native method under test @CriticalNative? staticbool IsCurrentJniCritical() { return gCurrentJni == static_cast<uint32_t>(JniKind::kCritical);
}
// Is the current native method under test @FastNative? staticbool IsCurrentJniFast() { return gCurrentJni == static_cast<uint32_t>(JniKind::kFast);
}
// Is the current native method a plain-old non-annotated native? staticbool IsCurrentJniNormal() { return gCurrentJni == static_cast<uint32_t>(JniKind::kNormal);
}
// Signify that a different kind of JNI is about to be tested. staticvoid UpdateCurrentJni(JniKind kind) {
gCurrentJni = static_cast<uint32_t>(kind);
}
// (Match the name suffixes of native methods in MyClassNatives.java) static std::string CurrentJniStringSuffix() { switch (gCurrentJni) { casestatic_cast<uint32_t>(JniKind::kNormal): { return"";
} casestatic_cast<uint32_t>(JniKind::kFast): { return"_Fast";
} casestatic_cast<uint32_t>(JniKind::kCritical): { return"_Critical";
} default:
LOG(FATAL) << "Invalid current JNI value: " << gCurrentJni;
UNREACHABLE();
}
}
// Fake values passed to our JNI handlers when we enter @CriticalNative. // Normally @CriticalNative calling convention strips out the "JNIEnv*, jclass" parameters. // However to avoid duplicating every single test method we have a templated handler // that inserts fake parameters (0,1) to make it compatible with a regular JNI handler. static JNIEnv* const kCriticalFakeJniEnv = reinterpret_cast<JNIEnv*>(0xDEADFEAD); static jclass const kCriticalFakeJniClass = reinterpret_cast<jclass>(0xBEAFBEEF);
// Type trait. Returns true if "T" is the same type as one of the types in Args... // // Logically equal to OR(std::same_type<T, U> for all U in Args). template <typename T, typename ... Args> struct is_any_of;
template <typename T, typename U, typename ... Args> struct is_any_of<T, U, Args ...> { using value_type = bool; static constexpr constbool value = std::is_same<T, U>::value || is_any_of<T, Args ...>::value;
};
// Type traits for JNI types. template <typename T> struct jni_type_traits { // True if type T ends up holding an object reference. False otherwise. // (Non-JNI types will also be false). static constexpr constbool is_ref =
is_any_of<T, jclass, jobject, jstring, jobjectArray, jintArray,
jcharArray, jfloatArray, jshortArray, jdoubleArray, jlongArray>::value;
};
// Base case: No parameters = 0 refs.
size_t count_nonnull_refs_helper() { return0;
}
// Given any list of parameters, check how many object refs there are and only count // them if their runtime value is non-null. // // For example given (jobject, jint, jclass) we can get (2) if both #0/#2 are non-null, // (1) if either #0/#2 are null but not both, and (0) if all parameters are null. // Primitive parameters (including JNIEnv*, if present) are ignored. template <typename ... Args>
size_t count_nonnull_refs(Args ... args) { return count_nonnull_refs_helper(args...);
}
template <typename R, typename Arg1, typename Arg2, typename ... Args, R (*fn)(Arg1, Arg2, Args...)> struct remove_extra_parameters_helper<R(Arg1, Arg2, Args...), fn> { // Note: Do not use Args&& here to maintain C-style parameter types. static R apply(Args... args) {
JNIEnv* env = kCriticalFakeJniEnv;
jclass kls = kCriticalFakeJniClass; return fn(env, kls, args...);
}
};
// Given a function 'fn' create a function 'apply' which will omit the JNIEnv/jklass parameters // // i.e. if fn(JNIEnv*,jklass,a,b,c,d,e...) then apply(a,b,c,d,e,...) template <typename T, T* fn> struct jni_remove_extra_parameters : public remove_extra_parameters_helper<T, fn> {};
class JniCompilerTest : public CommonCompilerTest { protected: void SetUp() override {
CommonCompilerTest::SetUp();
check_generic_jni_ = false;
}
private: void CompileForTest(jobject class_loader, bool direct, constchar* method_name, constchar* method_sig) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
StackHandleScope<2> hs(self);
Handle<mirror::ClassLoader> loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); // Compile the native method before starting the runtime
Handle<mirror::Class> c = hs.NewHandle(FindClass("LMyClassNatives;", loader)); constauto pointer_size = class_linker_->GetImagePointerSize();
ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size);
ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
ASSERT_EQ(direct, method->IsDirect()) << method_name << " " << method_sig; if (direct) { // Class initialization could replace the entrypoint, so force // the initialization before we set up the entrypoint below.
class_linker_->EnsureInitialized(
self, c, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
{
ScopedThreadSuspension sts(self, ThreadState::kNative);
class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
}
} if (check_generic_jni_) {
method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
} else { constvoid* code = method->GetEntryPointFromQuickCompiledCode(); if (code == nullptr || class_linker_->IsQuickGenericJniStub(code)) {
CompileMethod(method);
ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr)
<< method_name << " " << method_sig;
}
}
}
protected: void CompileForTestWithCurrentJni(jobject class_loader, bool direct, constchar* method_name_orig, constchar* method_sig) { // Append the JNI kind to the method name, so that we automatically get the // fast or critical versions of the same method.
std::string method_name_str = std::string(method_name_orig) + CurrentJniStringSuffix(); constchar* method_name = method_name_str.c_str();
void SetUpForTest(bool direct, constchar* method_name_orig, constchar* method_sig, void* native_fnptr) { // Append the JNI kind to the method name, so that we automatically get the // fast or critical versions of the same method.
std::string method_name_str = std::string(method_name_orig) + CurrentJniStringSuffix(); constchar* method_name = method_name_str.c_str();
// Initialize class loader and compile method when runtime not started. if (!runtime_->IsStarted()) {
{
ScopedObjectAccess soa(Thread::Current());
class_loader_ = LoadDex("MyClassNatives");
}
CompileForTest(class_loader_, direct, method_name, method_sig); // Start runtime.
Thread::Current()->TransitionFromSuspendedToRunnable();
android::InitializeNativeLoader(); bool started = runtime_->Start();
CHECK(started);
} // JNI operations after runtime start.
env_ = Thread::Current()->GetJniEnv();
jklass_ = env_->FindClass("MyClassNatives");
ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig;
// Make sure the test class is visibly initialized so that the RegisterNatives() below // sets the JNI entrypoint rather than leaving it as null (this test pretends to be an // AOT compiler and therefore the ClassLinker skips entrypoint initialization). Even // if the ClassLinker initialized it with a stub, we would not want to test that here.
class_linker_->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
// Test everything: (normal, @FastNative, @CriticalNative) x (compiler, generic). #define JNI_TEST_CRITICAL(TestName) \
JNI_TEST(TestName) \
JNI_TEST_CRITICAL_ONLY(TestName) \
staticvoid expectValidThreadState() { // Normal JNI always transitions to "Native". Other JNIs stay in the "Runnable" state. if (IsCurrentJniNormal()) {
EXPECT_EQ(ThreadState::kNative, Thread::Current()->GetState());
} else {
EXPECT_EQ(ThreadState::kRunnable, Thread::Current()->GetState());
}
}
staticvoid expectValidJniEnvAndObject(JNIEnv* env, jobject thisObj) { if (!IsCurrentJniCritical()) {
EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
ASSERT_TRUE(thisObj != nullptr);
EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
} else {
LOG(FATAL) << "Objects are not supported for @CriticalNative, why is this being tested?";
UNREACHABLE();
}
}
// Validates the JNIEnv to be the same as the current thread's JNIEnv, and makes sure // that the object here is an instance of the class we registered the method with. // // Hard-fails if this somehow gets invoked for @CriticalNative since objects are unsupported. #define EXPECT_JNI_ENV_AND_OBJECT_FOR_CURRENT_JNI(env, thisObj) \
expectValidJniEnvAndObject(env, thisObj)
staticvoid expectValidJniEnvAndClass(JNIEnv* env, jclass kls) { if (!IsCurrentJniCritical()) {
EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
ASSERT_TRUE(kls != nullptr);
EXPECT_TRUE(env->IsSameObject(static_cast<jobject>(JniCompilerTest::jklass_), static_cast<jobject>(kls)));
} else { // This is pretty much vacuously true but catch any testing setup mistakes.
EXPECT_EQ(env, kCriticalFakeJniEnv);
EXPECT_EQ(kls, kCriticalFakeJniClass);
}
}
// Validates the JNIEnv is the same as the current thread's JNIenv, and makes sure // that the jclass we got in the JNI handler is the same one as the class the method was looked // up for. // // (Checks are skipped for @CriticalNative since the two values are fake). #define EXPECT_JNI_ENV_AND_CLASS_FOR_CURRENT_JNI(env, kls) expectValidJniEnvAndClass(env, kls)
// Temporarily disable the EXPECT_NUM_STACK_REFERENCES check (for a single test). struct ScopedDisableCheckNumStackReferences {
ScopedDisableCheckNumStackReferences() {
CHECK(sCheckNumStackReferences); // No nested support.
sCheckNumStackReferences = false;
}
// Check that the handle scope at the start of this block is the same // as the handle scope at the end of the block. struct ScopedCheckHandleScope {
ScopedCheckHandleScope() : handle_scope_(Thread::Current()->GetTopHandleScope()) {
}
~ScopedCheckHandleScope() {
EXPECT_EQ(handle_scope_, Thread::Current()->GetTopHandleScope())
<< "Top-most handle scope must be the same after all the JNI "
<< "invocations have finished (as before they were invoked).";
}
// Number of references allocated in handle scopes & JNI shadow frames on this thread. static size_t NumStackReferences(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
CountReferencesVisitor visitor;
self->VisitRoots(&visitor, kVisitRootFlagAllRoots); return visitor.NumReferences();
}
staticvoid expectNumStackReferences(size_t expected) { // In rare cases when JNI functions call themselves recursively, // disable this test because it will have a false negative. if (!IsCurrentJniCritical() && ScopedDisableCheckNumStackReferences::sCheckNumStackReferences) { /* @CriticalNative doesn't build a HandleScope, so this test is meaningless then. */
ScopedObjectAccess soa(Thread::Current());
// Decorator for "static" JNI callbacks. template <typename R, typename ... Args, R (*fn)(JNIEnv*, jclass, Args...)> struct make_jni_test_decorator<R(JNIEnv*, jclass kls, Args...), fn> { static R apply(JNIEnv* env, jclass kls, Args ... args) {
EXPECT_THREAD_STATE_FOR_CURRENT_JNI();
EXPECT_MUTATOR_LOCK_FOR_CURRENT_JNI();
EXPECT_JNI_ENV_AND_CLASS_FOR_CURRENT_JNI(env, kls); // All incoming parameters get spilled into the JNI transition frame. // The `jclass` is just a reference to the method's declaring class field.
EXPECT_NUM_STACK_REFERENCES(count_nonnull_refs(args...));
return fn(env, kls, args...);
}
};
// Decorator for instance JNI callbacks. template <typename R, typename ... Args, R (*fn)(JNIEnv*, jobject, Args...)> struct make_jni_test_decorator<R(JNIEnv*, jobject, Args...), fn> { static R apply(JNIEnv* env, jobject thisObj, Args ... args) {
EXPECT_THREAD_STATE_FOR_CURRENT_JNI();
EXPECT_MUTATOR_LOCK_FOR_CURRENT_JNI();
EXPECT_JNI_ENV_AND_OBJECT_FOR_CURRENT_JNI(env, thisObj); // All incoming parameters + the implicit 'this' get spilled into the JNI transition frame.
EXPECT_NUM_STACK_REFERENCES(count_nonnull_refs(thisObj, args...));
return fn(env, thisObj, args...);
}
};
// Decorate the regular JNI callee with the extra gtest checks. // This way we can have common test logic for everything generic like checking if a lock is held, // checking handle scope state, etc. #define MAKE_JNI_TEST_DECORATOR(fn) make_jni_test_decorator<decltype(fn), (fn)>::apply
// Convert function f(JNIEnv*,jclass,a,b,c,d...) into f2(a,b,c,d...) // -- This way we don't have to write out each implementation twice for @CriticalNative. #define JNI_CRITICAL_WRAPPER(func) jni_remove_extra_parameters<decltype(func), (func)>::apply // Get a function pointer whose calling convention either matches a regular native // or a critical native depending on which kind of jni is currently under test. // -- This also has the benefit of genering a compile time error if the 'func' doesn't properly // have JNIEnv and jclass parameters first. #define CURRENT_JNI_WRAPPER(func) \
(IsCurrentJniCritical() \
? reinterpret_cast<void*>(&JNI_CRITICAL_WRAPPER(MAKE_JNI_TEST_DECORATOR(func))) \
: reinterpret_cast<void*>(&MAKE_JNI_TEST_DECORATOR(func)))
// Do the opposite of the above. Do *not* wrap the function, instead just cast it to a void*. // Only for "TEST_JNI_NORMAL_ONLY" configs, and it inserts a test assert to ensure this is the case. #define NORMAL_JNI_ONLY_NOWRAP(func) \
({ ASSERT_TRUE(IsCurrentJniNormal()); reinterpret_cast<void*>(&(func)); }) // Same as above, but with nullptr. When we want to test the stub functionality. #define NORMAL_OR_FAST_JNI_ONLY_NULLPTR \
({ ASSERT_TRUE(IsCurrentJniNormal() || IsCurrentJniFast()); nullptr; })
int gJava_MyClassNatives_foo_calls[kJniKindCount] = {}; void Java_MyClassNatives_foo(JNIEnv*, jobject) {
gJava_MyClassNatives_foo_calls[gCurrentJni]++;
}
void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() {
SetUpForTest(false, "bar", "(I)I", NORMAL_OR_FAST_JNI_ONLY_NULLPTR); // calling through stub will link with &Java_MyClassNatives_bar{,_1Fast}
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
EXPECT_EQ(25, result);
}
// Note: @CriticalNative is only for static methods.
JNI_TEST(CompileAndRunIntMethodThroughStub)
void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() {
SetUpForTest(true, "sbar", "(I)I", nullptr); // calling through stub will link with &Java_MyClassNatives_sbar{,_1Fast,_1Critical}
// The x86 generic JNI code had a bug where it assumed a floating // point return value would be in xmm0. We use log, to somehow ensure // the compiler will use the floating point stack.
void JniCompilerTest::RunStaticLogDoubleMethodImpl() { void* jni_handler; if (IsCurrentJniNormal()) { // This test seems a bit special, don't use a JNI wrapper here.
jni_handler = NORMAL_JNI_ONLY_NOWRAP(Java_MyClassNatives_logD);
} else {
jni_handler = CURRENT_JNI_WRAPPER(Java_MyClassNatives_logD_notNormal);
}
SetUpForTest(true, "logD", "(D)D", jni_handler);
jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0);
EXPECT_DOUBLE_EQ(log(2.0), result);
}
void JniCompilerTest::RunStaticLogFloatMethodImpl() { void* jni_handler; if (IsCurrentJniNormal()) { // This test seems a bit special, don't use a JNI wrapper here.
jni_handler = NORMAL_JNI_ONLY_NOWRAP(Java_MyClassNatives_logF);
} else {
jni_handler = CURRENT_JNI_WRAPPER(Java_MyClassNatives_logF);
}
SetUpForTest(true, "logF", "(F)F", jni_handler);
jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0);
EXPECT_FLOAT_EQ(logf(2.0), result);
}
// all compilation needs to happen before Runtime::Start
CompileForTestWithCurrentJni(class_loader_, false, "foo", "()V");
CompileForTestWithCurrentJni(class_loader_, false, "throwException", "()V"); if (gCurrentJni == enum_cast<uint32_t>(JniKind::kNormal)) {
CompileForTestWithCurrentJni(class_loader_, false, "synchronizedThrowException", "()V");
}
} // Start runtime to avoid re-initialization in SetUpForTest.
Thread::Current()->TransitionFromSuspendedToRunnable(); bool started = runtime_->Start();
CHECK(started);
gJava_MyClassNatives_foo_calls[gCurrentJni] = 0;
// Check a single call of a JNI method is ok
SetUpForTest(false, "foo", "()V", CURRENT_JNI_WRAPPER(Java_MyClassNatives_foo));
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_EQ(1, gJava_MyClassNatives_foo_calls[gCurrentJni]);
EXPECT_FALSE(Thread::Current()->IsExceptionPending());
// Get class for exception we expect to be thrown
ScopedLocalRef<jclass> jlre(env_, env_->FindClass("java/lang/RuntimeException"));
SetUpForTest(false, "throwException", "()V",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_throwException)); // Call Java_MyClassNatives_throwException (JNI method that throws exception)
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
ScopedLocalRef<jthrowable> exception(env_, env_->ExceptionOccurred());
env_->ExceptionClear();
EXPECT_TRUE(env_->IsInstanceOf(exception.get(), jlre.get()));
// Check a single call of a JNI method is ok
EXPECT_EQ(1, gJava_MyClassNatives_foo_calls[gCurrentJni]);
SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_EQ(2, gJava_MyClassNatives_foo_calls[gCurrentJni]);
if (gCurrentJni == enum_cast<uint32_t>(JniKind::kNormal)) {
SetUpForTest(false, "synchronizedThrowException", "()V",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_synchronizedThrowException));
LockWord lock_word = GetLockWord(jobj_);
ASSERT_EQ(lock_word.GetState(), LockWord::kUnlocked); // Call Java_MyClassNatives_synchronizedThrowException (synchronized JNI method // that throws exception) to check that we correctly unlock the object.
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
ScopedLocalRef<jthrowable> exception2(env_, env_->ExceptionOccurred());
env_->ExceptionClear();
EXPECT_TRUE(env_->IsInstanceOf(exception2.get(), jlre.get()));
lock_word = GetLockWord(jobj_);
EXPECT_EQ(lock_word.GetState(), LockWord::kUnlocked);
// Check a single call of a JNI method is ok
EXPECT_EQ(2, gJava_MyClassNatives_foo_calls[gCurrentJni]);
SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_EQ(3, gJava_MyClassNatives_foo_calls[gCurrentJni]);
}
jint Java_MyClassNatives_nativeUpCall(JNIEnv* env, jobject thisObj, jint i) { if (i <= 0) { // We want to check raw Object* / Array* below
ScopedObjectAccess soa(env);
// Usual # local references on stack check fails because nativeUpCall calls itself recursively, // each time the # of local references will therefore go up.
ScopedDisableCheckNumStackReferences disable_num_stack_check;
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 10);
jint local_ref_test(JNIEnv* env, jobject thisObj, jint x) { // Add 10 local references
ScopedObjectAccess soa(env); for (int i = 0; i < 10; i++) {
soa.AddLocalReference<jobject>(soa.Decode<mirror::Object>(thisObj));
} return x+1;
}
void JniCompilerTest::LocalReferenceTableClearingTestImpl() {
SetUpForTest(false, "fooI", "(I)I", CURRENT_JNI_WRAPPER(local_ref_test)); // 1000 invocations of a method that adds 10 local references for (int i = 0; i < 1000; i++) {
jint result = env_->CallIntMethod(jobj_, jmethod_, i);
EXPECT_TRUE(result == i + 1);
}
}
// @FastNative doesn't support 'synchronized' keyword and // never will -- locking functions aren't fast.
JNI_TEST_NORMAL_ONLY(GetSinkPropertiesNative)
// This should return jclass, but we're imitating a bug pattern.
jobject Java_MyClassNatives_instanceMethodThatShouldReturnClass(JNIEnv* env, jobject) { return env->NewStringUTF("not a class!");
}
// This should return jclass, but we're imitating a bug pattern.
jobject Java_MyClassNatives_staticMethodThatShouldReturnClass(JNIEnv* env, jclass) { return env->NewStringUTF("not a class!");
}
void JniCompilerTest::UpcallReturnTypeChecking_InstanceImpl() { // Set debuggable so that the JNI compiler does not emit a fast-path that would skip the // runtime call where we do these checks. Note that while normal gtests use the debug build // which disables the fast path, `art_standalone_compiler_tests` run in the release build.
compiler_options_->SetDebuggable(true);
SetUpForTest(false, "instanceMethodThatShouldReturnClass", "()Ljava/lang/Class;",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_instanceMethodThatShouldReturnClass));
CheckJniAbortCatcher check_jni_abort_catcher; // This native method is bad, and tries to return a jstring as a jclass.
env_->CallObjectMethod(jobj_, jmethod_);
check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " + "of java.lang.String from java.lang.Class " + "MyClassNatives.instanceMethodThatShouldReturnClass" +
CurrentJniStringSuffix() + "()");
// Here, we just call the method incorrectly; we should catch that too.
env_->CallObjectMethod(jobj_, jmethod_);
check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " + "of java.lang.String from java.lang.Class " + "MyClassNatives.instanceMethodThatShouldReturnClass" +
CurrentJniStringSuffix() + "()");
env_->CallStaticObjectMethod(jklass_, jmethod_);
check_jni_abort_catcher.Check(std::string() + "calling non-static method " + "java.lang.Class " + "MyClassNatives.instanceMethodThatShouldReturnClass" +
CurrentJniStringSuffix() + "() with CallStaticObjectMethodV");
}
JNI_TEST(UpcallReturnTypeChecking_Instance)
void JniCompilerTest::UpcallReturnTypeChecking_StaticImpl() { // Set debuggable so that the JNI compiler does not emit a fast-path that would skip the // runtime call where we do these checks. Note that while normal gtests use the debug build // which disables the fast path, `art_standalone_compiler_tests` run in the release build.
compiler_options_->SetDebuggable(true);
SetUpForTest(true, "staticMethodThatShouldReturnClass", "()Ljava/lang/Class;",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_staticMethodThatShouldReturnClass));
CheckJniAbortCatcher check_jni_abort_catcher; // This native method is bad, and tries to return a jstring as a jclass.
env_->CallStaticObjectMethod(jklass_, jmethod_);
check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " + "of java.lang.String from java.lang.Class " + "MyClassNatives.staticMethodThatShouldReturnClass" +
CurrentJniStringSuffix() + "()");
// Here, we just call the method incorrectly; we should catch that too.
env_->CallStaticObjectMethod(jklass_, jmethod_);
check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " + "of java.lang.String from java.lang.Class " + "MyClassNatives.staticMethodThatShouldReturnClass" +
CurrentJniStringSuffix() + "()");
env_->CallObjectMethod(jobj_, jmethod_);
check_jni_abort_catcher.Check(std::string() + "calling static method " + "java.lang.Class " + "MyClassNatives.staticMethodThatShouldReturnClass" +
CurrentJniStringSuffix() + "() with CallObjectMethodV");
}
JNI_TEST(UpcallReturnTypeChecking_Static)
// This should take jclass, but we're imitating a bug pattern. void Java_MyClassNatives_instanceMethodThatShouldTakeClass(JNIEnv*, jobject, jclass) {
}
// This should take jclass, but we're imitating a bug pattern. void Java_MyClassNatives_staticMethodThatShouldTakeClass(JNIEnv*, jclass, jclass) {
}
void JniCompilerTest::UpcallArgumentTypeChecking_InstanceImpl() { // This will lead to error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
CheckJniAbortCatcher check_jni_abort_catcher; // We deliberately pass a bad second argument here.
env_->CallVoidMethod(jobj_, jmethod_, 123, env_->NewStringUTF("not a class!"));
check_jni_abort_catcher.Check(std::string() + "bad arguments passed to void " + "MyClassNatives.instanceMethodThatShouldTakeClass" +
CurrentJniStringSuffix() + "(int, java.lang.Class)");
}
JNI_TEST(UpcallArgumentTypeChecking_Instance)
void JniCompilerTest::UpcallArgumentTypeChecking_StaticImpl() { // This will lead to error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
// Second test: test with int[] objects with increasing lengths for (int i = 0; i < 254; ++i) {
jintArray tmp = env_->NewIntArray(i);
args[i].l = tmp;
EXPECT_NE(args[i].l, nullptr);
}
void JniCompilerTest::WithoutImplementationRefReturnImpl() { // This will lead to error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
void Java_MyClassNatives_normalNative(JNIEnv*, jclass) { // Intentionally left empty.
}
// Methods not annotated with anything are not considered "fast native" // -- Check that the annotation lookup does not find it. void JniCompilerTest::NormalNativeImpl() {
SetUpForTest(/* direct= */ true, "normalNative", "()V",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_normalNative));
// TODO: just rename the java functions to the standard convention and remove duplicated tests
JNI_TEST_NORMAL_ONLY(NormalNative)
// Methods annotated with @FastNative are considered "fast native" // -- Check that the annotation lookup succeeds. void Java_MyClassNatives_fastNative(JNIEnv*, jclass) { // Intentionally left empty.
}
// TODO: just rename the java functions to the standard convention and remove duplicated tests
JNI_TEST_NORMAL_ONLY(FastNative)
int gJava_myClassNatives_criticalNative_calls[kJniKindCount] = {}; // Methods annotated with @CriticalNative are considered "critical native" // -- Check that the annotation lookup succeeds. void Java_MyClassNatives_criticalNative() {
gJava_myClassNatives_criticalNative_calls[gCurrentJni]++;
}
void JniCompilerTest::CriticalNativeImpl() {
SetUpForTest(/* direct= */ true, // Important: Don't change the "current jni" yet to avoid a method name suffix. "criticalNative", "()V", // TODO: Use CURRENT_JNI_WRAPPER instead which is more generic. reinterpret_cast<void*>(&Java_MyClassNatives_criticalNative));
// TODO: remove this manual updating of the current JNI. Merge with the other tests.
UpdateCurrentJni(JniKind::kCritical);
ASSERT_TRUE(IsCurrentJniCritical());
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.