From 41b5c52b5a20a4234f64f22ca7963a255c55a16d Mon Sep 17 00:00:00 2001 From: Gregory Moskaliuk Date: Fri, 23 Oct 2026 18:56:30 +0200 Subject: [PATCH] Parse each Java TurboModule method's JNI signature once JavaTurboModule::invokeJavaMethod parsed the method's JNI signature into a vector of argument type strings on every call, and cut the return type out of it again for number and boolean methods. Keep both in a MethodInfo per method, keyed by its jmethodID, like ObjCTurboModule does on iOS. Co-Authored-By: Claude Opus 5.5 --- .../android/ReactCommon/JavaTurboModule.cpp | 29 ++++++++++++++----- .../android/ReactCommon/JavaTurboModule.h | 12 ++++++++ 2 files changed, 34 insertions(+), 7 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 0db4927a6338..df3b9bbfc436 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 @@ -588,6 +588,24 @@ void rejectWithException( } // namespace +const JavaTurboModule::MethodInfo& JavaTurboModule::getMethodInfo( + jmethodID methodID, + const std::string& methodSignature) { + std::lock_guard lock(methodInfosMutex_); + auto entry = methodInfos_.find(methodID); + if (entry == methodInfos_.end()) { + entry = methodInfos_ + .emplace( + methodID, + MethodInfo{ + getMethodArgTypesFromSignature(methodSignature), + methodSignature.substr( + methodSignature.find_last_of(')') + 1)}) + .first; + } + return entry->second; +} + jsi::Value JavaTurboModule::invokeJavaMethod( jsi::Runtime& runtime, TurboModuleMethodValueKind valueKind, @@ -689,14 +707,13 @@ jsi::Value JavaTurboModule::invokeJavaMethod( return returnValue; } - std::vector methodArgTypes = - getMethodArgTypesFromSignature(methodSignature); + const MethodInfo& methodInfo = getMethodInfo(methodID, methodSignature); JNIArgs jniArgs = convertJSIArgsToJNIArgs( env, runtime, methodNameStr, - methodArgTypes, + methodInfo.argTypes, args, argCount, jsInvoker_, @@ -712,8 +729,7 @@ jsi::Value JavaTurboModule::invokeJavaMethod( switch (valueKind) { case BooleanKind: { - std::string returnType = - methodSignature.substr(methodSignature.find_last_of(')') + 1); + const std::string& returnType = methodInfo.returnType; if (returnType == "Ljava/lang/Boolean;") { auto returnObject = env->CallObjectMethodA(instance, methodID, jargs.data()); @@ -749,8 +765,7 @@ jsi::Value JavaTurboModule::invokeJavaMethod( } } case NumberKind: { - std::string returnType = - methodSignature.substr(methodSignature.find_last_of(')') + 1); + const std::string& returnType = methodInfo.returnType; if (returnType == "Ljava/lang/Double;" || returnType == "Ljava/lang/Float;" || returnType == "Ljava/lang/Integer;") { diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h index f5c7fde52394..e8df215df712 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h @@ -9,7 +9,10 @@ #include +#include #include +#include +#include #include #include @@ -61,6 +64,15 @@ class JSI_EXPORT JavaTurboModule : public TurboModule { // instance_ can be of type JTurboModule, or JNativeModule jni::global_ref instance_; std::shared_ptr nativeMethodCallInvoker_; + + // A method's JNI signature, parsed on its first call since it doesn't change. + struct MethodInfo { + std::vector argTypes; + std::string returnType; + }; + std::unordered_map methodInfos_; + std::mutex methodInfosMutex_; + const MethodInfo &getMethodInfo(jmethodID methodID, const std::string &methodSignature); }; } // namespace facebook::react