Repository navigation
Follow-up to #59414: cache each Java TurboModule method's ID with its signature, by name - #59619
Open
hryhoriiK97 wants to merge 3 commits into
Open
hryhoriiK97 wants to merge 3 commits into
hryhoriiK97 wants to merge 3 commits into
Conversation
JavaTurboModule now looks a method up on its first call and keeps its jmethodID in the per-module MethodInfo cache with the parsed signature, keyed by method name, instead of callers keeping the jmethodID themselves. Generated host functions no longer hold a static jmethodID, and JavaInteropTurboModule drops its methodIDs_ vector. invokeJavaMethod takes the method name and signature as C strings, so calls with literals don't build two std::strings each time. The overload taking a cachedMethodID is kept for code generated by earlier codegen versions and for modules that call it directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
javache
reviewed
Oct 29, 2026
| // Kept for code generated by earlier versions of codegen and for modules | ||
| // that call it directly. cachedMethodID is neither read nor written: the | ||
| // method is cached by name. | ||
| jsi::Value invokeJavaMethod( |
Contributor
Author
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp
javache
reviewed
Oct 29, 2026
Comment on lines
+638
to
+639
| const char* methodName, | ||
| const char* methodSignature, |
Contributor
There was a problem hiding this comment.
How about making these string_view instead?
javache
approved these changes
Oct 29, 2026
|
@javache has imported this pull request. If you are a Meta employee, you can view this in D128349302. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
This is a follow-up to #59414, suggested by @javache in its review: keep a method's
jmethodIDin the same per-module cache as its parsed JNI signature, and index that cache by method name, instead of having two caches.Before this change, a Java TurboModule method's ID was cached by its caller: every generated host function kept a
static jmethodID cachedMethodIdand passed it toinvokeJavaMethodby reference, andJavaInteropTurboModulekept amethodIDs_vector for the same purpose.JavaTurboModulethen used that ID as the key of itsMethodInfocache (#59414).With this change:
JavaTurboModule::MethodInfoalso holds the method'sjmethodID, andmethodInfos_is keyed by the method name (astd::string, looked up through astd::string_viewwithout allocating). On a method's first call,getMethodInfolooks the method up withGetMethodIDand parses its signature. A signature that doesn't match still turns into a JS error (redbox) before anything is called, and nothing is cached for it.invokeJavaMethodgets an overload that takes the method name and JNI signature as C strings and nocachedMethodID. Generated code passes string literals, so a call no longer builds twostd::strings; the JNI signature is usually longer than the small-string buffer, so that was a heap allocation on every call.const std::string&name and signature,jmethodID&) is kept, for code generated by earlier codegen versions and for modules that call it directly, and marked[[deprecated]]. It forwards to the new one;cachedMethodIDis no longer read or written. Libraries that ship pre-generated JNI code build it with-Wall -Werror(the codegen CMake template appliestarget_compile_reactnative_options), so their builds will fail on the deprecation warning until they regenerate that code.GenerateModuleJniCpp) no longer emitsstatic jmethodID cachedMethodId, andJavaInteropTurboModuledropsmethodIDs_.Besides one cache instead of two, every generated host function gets smaller: RNTester's
libreactnative.so(which includesFBReactNativeSpec) is 61 KB smaller and itslibappmodules.so9.4 KB smaller.Changelog:
[ANDROID] [CHANGED] - Java TurboModules cache each method's ID with its parsed signature, by name, instead of in every generated host function
[ANDROID] [DEPRECATED] -
JavaTurboModule::invokeJavaMethodwith acachedMethodIDargument; use the overload without itTest Plan:
yarn jest packages/react-native-codegenpasses (64 suites, 3,130 tests); theGenerateModuleJniCppsnapshots are updated for the template change.node scripts/clang-format.js --checkreports no changes for the touched C++ files.invokeJavaMethodoverload is added to the fourReactAndroid*Cxx.apisnapshots by hand, where the generator sorts it;scripts/cxx-apican't run on currentmainlocally (its codegen step fails withSyntaxError: Unexpected token '{'while processingFBReactNativeSpec, without this change too)../gradlew :packages:rn-tester:android:app:assembleRelease -PreactNativeArchitectures=arm64-v8asucceeds with this change; RNTester's generatedFBReactNativeSpecand app module code use the new overload.mainat f7de203 with local benchmark screens), without and with this change, installed alternately on a moto g57 power (Android 16), three installs each with two launches. A Playground screen times synchronous calls toNativeSampleTurboModule(20,000 calls each, 5,000 for void, callback and promise calls), median of the six launches:getBool((Z)Z)getNumber((D)D)getString((Ljava/lang/String;)Ljava/lang/String;)getArray, 3 itemsgetObject, 3 keysgetObject, 20 keysgetValue(number, string, object)voidFuncgetValueWithCallbackgetValueWithPromiseCalls whose JNI signature is longer than the small-string buffer no longer allocate it, and are 5–6% cheaper. The cheapest calls, whose short signatures never allocated, now hash the method name instead of a pointer, about 50 ns more. Native library sizes are from the same two APKs.
🤖 Generated with Claude Code