Skip to content

Commit 49dbfc5

Browse files
RSNarafacebook-github-bot
authored andcommitted
Fix jni aborts when turbomodule creation throws
Summary: ## Summary This commit fixes a crash (JNI abort) that occurs when a Java TurboModule's creation throws an exception. The fix applies to two methods: getTurboJavaModule and getLegacyJavaModule. ## Problem When calling Java methods via fbjni with std::string arguments, fbjni creates temporary local_ref<JString> objects for the converted arguments. https://www.internalfb.com/code/fbsource/[74f313eee086]/fbandroid/libraries/fbjni/cxx/fbjni/detail/Meta-inl.h?lines=78-89 If the Java method throws an exception: 1. The JNI call returns with a pending exception flag set 2. C++ destroys the temporary jstring arguments (at end of full-expression) 3. The destructor calls GetObjectRefType() while there's a pending exception 4. This violates JNI rules and causes ART's CheckJNI to abort the process ## The Fix Pre-convert string arguments to jstring before the method call, controlling the lifetime of the local_ref<JString> so it extends past the exception check. Before: ``` static auto getTurboJavaModule = javaPart->getClass() ->getMethod<jni::alias_ref<JTurboModule>(const std::string&)>( "getTurboJavaModule"); auto moduleInstance = getTurboJavaModule(javaPart.get(), name); ``` After: ``` static auto getTurboJavaModule = javaPart->getClass()->getMethod<jni::alias_ref<JTurboModule>(jstring)>( "getTurboJavaModule"); auto jname = jni::make_jstring(name); auto moduleInstance = getTurboJavaModule(javaPart.get(), jname.get()); ``` The same change was applied to getLegacyJavaModule. ## Long-term solution This should be a system fix applied to fbjni. That is done in the subsequent diff. Changelog: [Android][Fixed] - Fix jni aborts when turbomodule constructors throw Differential Revision: D88910516
1 parent 80e384a commit 49dbfc5

File tree

1 file changed

+34
-8
lines changed

1 file changed

+34
-8
lines changed

packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,24 @@ std::shared_ptr<TurboModule> TurboModuleManager::getTurboModule(
176176
return turboModule;
177177
}
178178

179+
// TODO(T248203434): Remove this workaround once fixed in fbjni
180+
// NOTE: We use jstring instead of std::string for the method signature to
181+
// work around a bug in fbjni's exception handling. When a Java method throws
182+
// an exception, fbjni's JMethod::operator() needs to check for pending
183+
// exceptions via FACEBOOK_JNI_THROW_PENDING_EXCEPTION(). However, if we pass
184+
// std::string, fbjni creates a temporary local_ref<JString> for the argument.
185+
// C++ destroys temporaries at the end of the full-expression, which happens
186+
// AFTER the JNI call returns but BEFORE the exception check. The destructor
187+
// calls JNI functions (GetObjectRefType) while there's a pending exception,
188+
// which violates JNI rules and causes ART's CheckJNI to abort the process.
189+
//
190+
// By pre-converting to jstring here, we control the lifetime of the
191+
// local_ref<JString> so it extends past the exception check.
179192
static auto getTurboJavaModule =
180-
javaPart->getClass()
181-
->getMethod<jni::alias_ref<JTurboModule>(const std::string&)>(
182-
"getTurboJavaModule");
183-
auto moduleInstance = getTurboJavaModule(javaPart.get(), name);
193+
javaPart->getClass()->getMethod<jni::alias_ref<JTurboModule>(jstring)>(
194+
"getTurboJavaModule");
195+
auto jname = jni::make_jstring(name);
196+
auto moduleInstance = getTurboJavaModule(javaPart.get(), jname.get());
184197
if (moduleInstance) {
185198
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
186199
JavaTurboModule::InitParams params = {
@@ -243,11 +256,24 @@ std::shared_ptr<TurboModule> TurboModuleManager::getLegacyModule(
243256

244257
TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
245258

259+
// TODO(T248203434): Remove this workaround once fixed in fbjni
260+
// NOTE: We use jstring instead of std::string for the method signature to
261+
// work around a bug in fbjni's exception handling. When a Java method throws
262+
// an exception, fbjni's JMethod::operator() needs to check for pending
263+
// exceptions via FACEBOOK_JNI_THROW_PENDING_EXCEPTION(). However, if we pass
264+
// std::string, fbjni creates a temporary local_ref<JString> for the argument.
265+
// C++ destroys temporaries at the end of the full-expression, which happens
266+
// AFTER the JNI call returns but BEFORE the exception check. The destructor
267+
// calls JNI functions (GetObjectRefType) while there's a pending exception,
268+
// which violates JNI rules and causes ART's CheckJNI to abort the process.
269+
//
270+
// By pre-converting to jstring here, we control the lifetime of the
271+
// local_ref<JString> so it extends past the exception check.
246272
static auto getLegacyJavaModule =
247-
javaPart->getClass()
248-
->getMethod<jni::alias_ref<JNativeModule>(const std::string&)>(
249-
"getLegacyJavaModule");
250-
auto moduleInstance = getLegacyJavaModule(javaPart.get(), name);
273+
javaPart->getClass()->getMethod<jni::alias_ref<JNativeModule>(jstring)>(
274+
"getLegacyJavaModule");
275+
auto jname = jni::make_jstring(name);
276+
auto moduleInstance = getLegacyJavaModule(javaPart.get(), jname.get());
251277

252278
if (moduleInstance) {
253279
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);

0 commit comments

Comments
 (0)