Skip to content

Follow-up to #59414: cache each Java TurboModule method's ID with its signature, by name - #59619

Merged
meta-codesync[bot] merged 3 commits into
react:mainfrom
hryhoriiK97:android-turbomodule-method-info-by-name
Oct 30, 2026
Merged

meta-codesync[bot] merged 3 commits into
react:mainfrom
hryhoriiK97:android-turbomodule-method-info-by-name

Conversation

@hryhoriiK97

@hryhoriiK97 hryhoriiK97 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

This is a follow-up to #59414, suggested by @javache in its review: keep a method's jmethodID in 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 cachedMethodId and passed it to invokeJavaMethod by reference, and JavaInteropTurboModule kept a methodIDs_ vector for the same purpose. JavaTurboModule then used that ID as the key of its MethodInfo cache (#59414).

With this change:

  • JavaTurboModule::MethodInfo also holds the method's jmethodID, and methodInfos_ is keyed by the method name (a std::string, looked up through a std::string_view without allocating). On a method's first call, getMethodInfo looks the method up with GetMethodID and 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.
  • invokeJavaMethod gets an overload that takes the method name and JNI signature as C strings and no cachedMethodID. Generated code passes string literals, so a call no longer builds two std::strings; the JNI signature is usually longer than the small-string buffer, so that was a heap allocation on every call.
  • The previous overload (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; cachedMethodID is no longer read or written. Libraries that ship pre-generated JNI code build it with -Wall -Werror (the codegen CMake template applies target_compile_reactnative_options), so their builds will fail on the deprecation warning until they regenerate that code.
  • The codegen template for Java module JNI files (GenerateModuleJniCpp) no longer emits static jmethodID cachedMethodId, and JavaInteropTurboModule drops methodIDs_.

Besides one cache instead of two, every generated host function gets smaller: RNTester's libreactnative.so (which includes FBReactNativeSpec) is 61 KB smaller and its libappmodules.so 9.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::invokeJavaMethod with a cachedMethodID argument; use the overload without it

Test Plan:

  • yarn jest packages/react-native-codegen passes (64 suites, 3,130 tests); the GenerateModuleJniCpp snapshots are updated for the template change.
  • node scripts/clang-format.js --check reports no changes for the touched C++ files.
  • C++ API snapshots: the new invokeJavaMethod overload is added to the four ReactAndroid*Cxx.api snapshots by hand, where the generator sorts it; scripts/cxx-api can't run on current main locally (its codegen step fails with SyntaxError: Unexpected token '{' while processing FBReactNativeSpec, without this change too).
  • ./gradlew :packages:rn-tester:android:app:assembleRelease -PreactNativeArchitectures=arm64-v8a succeeds with this change; RNTester's generated FBReactNativeSpec and app module code use the new overload.
  • On a device. Two RNTester release APKs built from the same local checkout (main at 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 to NativeSampleTurboModule (20,000 calls each, 5,000 for void, callback and promise calls), median of the six launches:
Call (JNI signature) before this PR change
getBool ((Z)Z) 1.10 µs 1.15 µs +4%
getNumber ((D)D) 1.46 µs 1.53 µs +5%, ranges overlap
getString ((Ljava/lang/String;)Ljava/lang/String;) 2.05 µs 1.92 µs −6%
getArray, 3 items 10.34 µs 10.06 µs −3%
getObject, 3 keys 9.90 µs 9.58 µs −3%
getObject, 20 keys 35.67 µs 35.28 µs −1%
getValue(number, string, object) 13.94 µs 14.21 µs +2%, within noise
voidFunc 4.55 µs 4.59 µs within noise
getValueWithCallback 8.54 µs 7.99 µs −6.5%
getValueWithPromise 18.26 µs 17.38 µs −5%

Calls 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

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>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 29, 2026
@facebook-github-tools facebook-github-tools Bot added p: Software Mansion Partner: Software Mansion Partner labels Oct 29, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 29, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hryhoriiK97
hryhoriiK97 requested a review from javache October 29, 2026 15:03
# Conflicts:
#	packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp
@meta-codesync

meta-codesync Bot commented Oct 29, 2026

Copy link
Copy Markdown

@javache has imported this pull request. If you are a Meta employee, you can view this in D128349302.

@meta-codesync
meta-codesync Bot merged commit f78bdd4 into react:main Oct 30, 2026
101 checks passed
@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @hryhoriiK97 in f78bdd4

When will my fix make it into a release? | How to file a pick request?

@react-native-bot react-native-bot added the Merged This PR has been merged. label Oct 30, 2026
hryhoriiK97 added a commit to software-mansion/enriched-markdown that referenced this pull request Nov 2, 2026
…overload (#1119)

* fix(android): build against RN nightly's deprecated invokeJavaMethod overload

The shipped codegen code (includesGeneratedCode) is generated with RN 0.88
and calls invokeJavaMethod with cachedMethodId. RN PR 59619 deprecates that
overload, and the library's -Wall -Werror turns the warning into a build
failure on the 0.89 nightly. Keep deprecations as warnings until 0.89 is
the minimum and generated/ is regenerated.

See react/react-native#59619

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(android): point the deprecation flag TODO at dropping pre-generated codegen

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
jakex7 added a commit to software-mansion/react-native-svg that referenced this pull request Nov 5, 2026
Fixes #3109

# Summary

React Native 0.89 marks the `invokeJavaMethod` overload with
`cachedMethodID` as `[[deprecated]]` (react/react-native#59619).
`rnsvg.cpp` calls it, and `target_compile_reactnative_options` builds it
with `-Werror`, so the Android build fails:

```
rnsvg.cpp:18:54: error: 'invokeJavaMethod' is deprecated: Use the overload without cachedMethodID [-Werror,-Wdeprecated-declarations]
```

Use the new `invokeJavaMethod` overload on RN 0.89+, preserving cached
method IDs on older versions.

## Test plan

Tested: Android builds on RN 0.89.0-rc.0 and 0.87.0.
meta-codesync Bot pushed a commit that referenced this pull request Nov 5, 2026
## Summary:

Moves two v0.89.0-rc.0 changelog entries to Breaking. Both stop existing libraries from building without a change on their side, as found while upgrading Expo to 0.89.0-rc.0 (expo/expo#52979):

- #59619 was under Deprecated. The deprecated `invokeJavaMethod` overload fails the build of libraries that ship pre-generated JNI codegen, because codegen CMake builds with `-Werror` (react-native-svg 15.16.0, react-native-maps 1.29.11).
- #59405 was under Changed. `constantsToExport` is now nonnull, so Swift overrides declared `-> [AnyHashable : Any]!` no longer compile (lottie-react-native 7.5.0).

The entries also say what to change.

## Changelog:

[INTERNAL] - Mark two v0.89.0-rc.0 changelog entries as breaking

## Test Plan:

N/A, changelog only. The diff moves the two entries and removes one blank line; the rest of `CHANGELOG.md` is unchanged.

Differential Revision: D129979450

Pull Request resolved: #59810
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. p: Software Mansion Partner: Software Mansion Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants