Repository navigation
Convert iOS native module event arguments to JS without UTF-8 - #59449
Open
hryhoriiK97 wants to merge 1 commit into
Open
hryhoriiK97 wants to merge 1 commit into
hryhoriiK97 wants to merge 1 commit into
Conversation
Events sent with RCTEventEmitter went through folly::dynamic: every string was encoded as UTF-8 on the sending thread and decoded again on the JS thread. RCTInstance now reads the arguments on the sending thread into a small tree with the same structure, where strings keep their ASCII or UTF-16 characters, and the JS thread creates the JS values from it. ReactInstance gets a callFunctionOnModule overload that takes an ArgFactory, which creates the arguments on the JS thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Hi @javache, this one is similar to #59325, but for events. On iOS, event arguments don't go through If it's easier to review, I can move |
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:
Native modules send events to JS with
RCTEventEmitter sendEventWithName:body:, which ends inRCTInstance -callFunctionOnJSModule:method:args:. Core modules use it too: for example,RCTNetworkingsends the whole text of an XHR response indidReceiveNetworkData, andRCTWebSocketModulesends each message.Current behavior. The arguments go through
folly::dynamic. On the sending thread,convertIdToFollyDynamicencodes every string as UTF-8. On the JS thread,jsi::valueFromDynamicdecodes it again into a JS string. Text that isn't ASCII is the expensive case: a 1 MB response with accented text costs about 3.9 ms on the JS thread and 3.2 ms on the sending thread.With this change.
RCTInstancereads the arguments into a small tree with the structure thatconvertIdToFollyDynamiccreates. Strings keep their own ASCII or UTF-16 characters instead of being encoded as UTF-8. When CoreFoundation exposes the characters, the tree points at them in an immutable copy of the string; otherwise it copies them.valueFromDynamicdoes.ReactInstancegets acallFunctionOnModuleoverload that takes anArgFactory, which creates the arguments on the JS thread. It has the same shape asTurboModule::ArgFactory, whichemitDeviceEventuses. Both overloads share the module lookup, so Android and other callers of thefolly::dynamicoverload run the same code as before.JS receives the same values as before:
convertIdToFollyDynamic.null."".The order of properties in event objects now follows the
NSDictionary. Before, it followed thefolly::dynamichash map, so it was arbitrary in both cases. On JSC, a string with both a NUL and non-ASCII characters is no longer cut off at the NUL, asJSStringCreateWithUTF8CStringdid. Hermes output is the same.Changelog:
[IOS] [CHANGED] - Events sent by native modules are converted to JS values without a UTF-8 round trip
Test Plan:
clang-format --dry-run --Werroron the changed files passes.xcodebuildin Release and run on a physical iPhone 15 Pro (iOS 26.6.1).ReactInstanceTest.testCallFunctionOnModule_withArgFactoryandtestCallFunctionOnModule_withEmptyArgFactoryfor the new overload. I couldn't run the C++ tests locally. I updated the C++ API snapshots by hand, because I couldn't run Doxygen here.BOOL,char,unsigned char,short,int, negative,unsigned long long, 2^60,float,double, NaN andNSDecimalNumbernumbers;NSNull,NSDateandNSURLvalues;NSNullkeys.GETofdata:URLs with ASCII or accented text: 1000 requests at 1 KB, 30 at 100 KB and 10 at 1 MB. Also 1000 small events sent from a background queue: a keyboard-like payload, and one with short keys and values created at run time.valueFromDynamicbefore and the tree conversion after.convertIdToFollyDynamicbefore and building the tree after.didReceiveNetworkData, accented, 1 KBdidReceiveNetworkData, accented, 100 KBdidReceiveNetworkData, accented, 1 MBdidReceiveNetworkData, ASCII, 1 KBdidReceiveNetworkData, ASCII, 100 KBdidReceiveNetworkData, ASCII, 1 MBdidReceiveNetworkResponse(headers)🤖 Generated with Claude Code