jni: Test that pushed handle scopes are popped after JNI transitions
There was previously a bug in generic JNI for @CriticalNative where new
handle scopes were pushed, but then not popped after the native call
was done. This caused a bug with stale pointers being found by GC.
Bug: 31933313
Change-Id: I423f9e7a7d391ba3a4771c830691931f0025b354
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index dca290c..6b56fe0 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -392,11 +392,13 @@
// -- TODO: We can support (1) if we remove the mutator lock assert during stub lookup.
# define JNI_TEST_NORMAL_ONLY(TestName) \
TEST_F(JniCompilerTest, TestName ## NormalCompiler) { \
+ ScopedCheckHandleScope top_handle_scope_check; \
SCOPED_TRACE("Normal JNI with compiler"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
TestName ## Impl(); \
} \
TEST_F(JniCompilerTest, TestName ## NormalGeneric) { \
+ ScopedCheckHandleScope top_handle_scope_check; \
SCOPED_TRACE("Normal JNI with generic"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
TEST_DISABLED_FOR_MIPS(); \
@@ -408,12 +410,14 @@
#define JNI_TEST(TestName) \
JNI_TEST_NORMAL_ONLY(TestName) \
TEST_F(JniCompilerTest, TestName ## FastCompiler) { \
+ ScopedCheckHandleScope top_handle_scope_check; \
SCOPED_TRACE("@FastNative JNI with compiler"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
TestName ## Impl(); \
} \
\
TEST_F(JniCompilerTest, TestName ## FastGeneric) { \
+ ScopedCheckHandleScope top_handle_scope_check; \
SCOPED_TRACE("@FastNative JNI with generic"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
TEST_DISABLED_FOR_MIPS(); \
@@ -424,11 +428,13 @@
// Test (@CriticalNative) x (compiler, generic) only.
#define JNI_TEST_CRITICAL_ONLY(TestName) \
TEST_F(JniCompilerTest, TestName ## CriticalCompiler) { \
+ ScopedCheckHandleScope top_handle_scope_check; \
SCOPED_TRACE("@CriticalNative JNI with compiler"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
TestName ## Impl(); \
} \
TEST_F(JniCompilerTest, TestName ## CriticalGeneric) { \
+ ScopedCheckHandleScope top_handle_scope_check; \
SCOPED_TRACE("@CriticalNative JNI with generic"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
SetCheckGenericJni(true); \
@@ -515,6 +521,21 @@
bool ScopedDisableCheckNumStackReferences::sCheckNumStackReferences = true;
+// 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).";
+ }
+
+ HandleScope* handle_scope_;
+};
+
static void expectNumStackReferences(size_t val1, size_t val2) {
// In rare cases when JNI functions call themselves recursively,
// disable this test because it will have a false negative.