From 4b66926c5e597fa17cf533f2c322a90b1f28ee76 Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Fri, 30 Oct 2026 22:32:38 +0100 Subject: [PATCH 1/2] Check for a Java exception when converting TurboModule constants Co-Authored-By: Claude Opus 5.5 --- .../android/ReactCommon/JavaTurboModule.cpp | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp index 8cce1cf4e70..6dbe42083a8 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp @@ -520,20 +520,24 @@ JNIArgs convertJSIArgsToJNIArgs( return jniArgs; } -jsi::Value convertFromJMapToValue(JNIEnv* env, jsi::Runtime& rt, jobject arg) { +// Returns null with a pending Java exception if the map has a value that +// Arguments.makeNativeMap() can't convert. +jni::local_ref convertFromJMapToNativeMap( + JNIEnv* env, + jobject arg) { // We currently use Java Argument.makeNativeMap() method to do this conversion // This could also be done purely in C++, but iterative over map methods // but those may end up calling reflection methods anyway // TODO (axe) Investigate the best way to convert Java Map to Value - jclass jArguments = env->FindClass("com/facebook/react/bridge/Arguments"); + static const auto jArguments = + jni::findClassStatic("com/facebook/react/bridge/Arguments"); static jmethodID jMakeNativeMap = env->GetStaticMethodID( - jArguments, + jArguments.get(), "makeNativeMap", "(Ljava/util/Map;)Lcom/facebook/react/bridge/WritableNativeMap;"); - auto constants = env->CallStaticObjectMethod(jArguments, jMakeNativeMap, arg); - auto jResult = jni::adopt_local(constants); - auto result = jni::static_ref_cast(jResult); - return jsi::valueFromDynamic(rt, result->cthis()->consume()); + return jni::adopt_local( + static_cast( + env->CallStaticObjectMethod(jArguments.get(), jMakeNativeMap, arg))); } /** @@ -720,15 +724,20 @@ jsi::Value JavaTurboModule::invokeJavaMethod( TMPL::syncMethodCallArgConversionEnd(moduleName, methodName); TMPL::syncMethodCallExecutionStart(moduleName, methodName); - auto constantsMap = (jobject)env->CallObjectMethod(instance, methodID); + auto constantsMap = + jni::adopt_local(env->CallObjectMethod(instance, methodID)); checkJNIErrorForMethodCall(); TMPL::syncMethodCallExecutionEnd(moduleName, methodName); TMPL::syncMethodCallReturnConversionStart(moduleName, methodName); - jsi::Value returnValue = constantsMap == nullptr - ? jsi::Value::undefined() - : convertFromJMapToValue(env, runtime, constantsMap); + jsi::Value returnValue = jsi::Value::undefined(); + if (constantsMap) { + auto nativeMap = convertFromJMapToNativeMap(env, constantsMap.get()); + checkJNIErrorForMethodCall(); + returnValue = + jsi::valueFromDynamic(runtime, nativeMap->cthis()->consume()); + } TMPL::syncMethodCallReturnConversionEnd(moduleName, methodName); TMPL::syncMethodCallEnd(moduleName, methodName); From abae08ea5d072904487322b07da76456cc0093d8 Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Sat, 31 Oct 2026 09:36:15 +0100 Subject: [PATCH 2/2] Call Arguments.makeNativeMap through fbjni Co-Authored-By: Claude Opus 5.5 --- .../android/ReactCommon/JavaTurboModule.cpp | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp index 6dbe42083a8..fce166897c2 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp @@ -520,25 +520,23 @@ JNIArgs convertJSIArgsToJNIArgs( return jniArgs; } -// Returns null with a pending Java exception if the map has a value that -// Arguments.makeNativeMap() can't convert. -jni::local_ref convertFromJMapToNativeMap( - JNIEnv* env, - jobject arg) { +struct JArguments : public jni::JavaClass { + constexpr static auto kJavaDescriptor = + "Lcom/facebook/react/bridge/Arguments;"; + // We currently use Java Argument.makeNativeMap() method to do this conversion // This could also be done purely in C++, but iterative over map methods // but those may end up calling reflection methods anyway // TODO (axe) Investigate the best way to convert Java Map to Value - static const auto jArguments = - jni::findClassStatic("com/facebook/react/bridge/Arguments"); - static jmethodID jMakeNativeMap = env->GetStaticMethodID( - jArguments.get(), - "makeNativeMap", - "(Ljava/util/Map;)Lcom/facebook/react/bridge/WritableNativeMap;"); - return jni::adopt_local( - static_cast( - env->CallStaticObjectMethod(jArguments.get(), jMakeNativeMap, arg))); -} + static jni::local_ref makeNativeMap( + jni::alias_ref> map) { + static const auto method = + javaClassStatic() + ->getStaticMethod>)>("makeNativeMap"); + return method(javaClassStatic(), map); + } +}; /** * Creates JSError with current JS runtime stack and Throwable stack trace. @@ -724,8 +722,9 @@ jsi::Value JavaTurboModule::invokeJavaMethod( TMPL::syncMethodCallArgConversionEnd(moduleName, methodName); TMPL::syncMethodCallExecutionStart(moduleName, methodName); - auto constantsMap = - jni::adopt_local(env->CallObjectMethod(instance, methodID)); + auto constantsMap = jni::adopt_local( + static_cast::javaobject>( + env->CallObjectMethod(instance, methodID))); checkJNIErrorForMethodCall(); TMPL::syncMethodCallExecutionEnd(moduleName, methodName); @@ -733,8 +732,7 @@ jsi::Value JavaTurboModule::invokeJavaMethod( jsi::Value returnValue = jsi::Value::undefined(); if (constantsMap) { - auto nativeMap = convertFromJMapToNativeMap(env, constantsMap.get()); - checkJNIErrorForMethodCall(); + auto nativeMap = JArguments::makeNativeMap(constantsMap); returnValue = jsi::valueFromDynamic(runtime, nativeMap->cthis()->consume()); }