Repository navigation
Description
Description
Once a RawPropsParser is prepared, RawPropsParser::at looks at the key after the cursor and, when the key read is any other, steps the cursor one key at a time round every key the props struct registered, comparing each, until it reaches the one read. The parser already holds nameToIndex_, which maps every registered name to its index and which preparse searches for every raw prop, but at never uses it.
So a props struct that reads a key out of registration order pays one comparison per key between the cursor and that key, on every parse. The common case is a component's props that extend ViewProps and read one of View's keys again after the base constructor, width or backgroundColor for instance: the cursor sits on View's last key and walks round from the start to it. I met it in a component whose props read width, height and backgroundColor after ViewProps; each parse compared about a hundred extra keys.
The comment above the loop says to read props in the same order every time, but a subclass cannot read a base key without reading it out of order. Finding a key that is not at the cursor's next place through nameToIndex_.at(key) would make the read one lookup instead of a walk; I carry that change locally and it removes the walk.
Steps to reproduce
git clone https://github.com/YevheniiKotyrlo/react-native-fantom-reproducer && cd react-native-fantom-reproducer./fantom/reproduce.sh raw-props-out-of-order-read(needs Docker). It clonesreact/react-nativeatmain, builds Fantom's tester as its CI does, and buildsfantom/RawPropsOrder.cppin the tester's CMake tree against the same libraries.fantom/raw-props-parser-compared-keys.patchadds two counters toRawPropsParser.cpp: the readsatanswers once the parser is prepared, and the keys it compares their names with.- The program parses
{width: 10, height: 10, tint: 1}as a component descriptor'sclonePropsdoes (a parser prepared for the struct,RawProps::parse, then the struct's constructor) through three structs:
struct ViewOnlyProps : ViewProps { /* View's props and nothing more */ };
struct InOrderProps : ViewProps {
InOrderProps(const PropsParserContext& context, const InOrderProps& sourceProps, const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
tint(convertRawProp(context, rawProps, "tint", sourceProps.tint, 0)) {}
int tint{0};
};
struct OutOfOrderProps : ViewProps {
OutOfOrderProps(const PropsParserContext& context, const OutOfOrderProps& sourceProps, const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
widthAgain(convertRawProp(context, rawProps, "width", sourceProps.widthAgain, 0.0f)) {}
float widthAgain{0.0f};
};Expected: reading width after ViewProps costs what reading tint costs, one read and one comparison more than View's props alone.
Seen: the width read costs 55 comparisons, the walk from the start of View's keys to width; a key View registers later costs more, up to every key View registers.
React Native Version
0.88.0 (reproduced on main at 11f37bd)
Affected Platforms
Other (please specify): ReactCommon, which every platform shares; reproduced on the cxx platform Fantom runs, on Linux
Areas
Fabric - The New Renderer
Output of npx @react-native-community/cli info
System:
OS: Linux 6.18 Ubuntu 22.04.5 LTS 22.04.5 LTS (Jammy Jellyfish)
CPU: (24) x64 AMD Ryzen 9 9950X3D 16-Core Processor
Memory: 120.97 GB / 125.63 GB
Shell:
version: 5.1.16
path: /bin/bash
Binaries:
Node:
version: 22.14.0
path: /usr/local/bin/node
Yarn:
version: 1.22.22
path: /usr/local/bin/yarn
npm:
version: 10.9.2
path: /usr/local/bin/npm
Watchman: Not Found
SDKs:
Android SDK:
API Levels:
- "37"
Build Tools:
- 37.0.0
Android NDK: 28.2.13676358
IDEs:
Android Studio: Not Found
Languages:
Java:
version: 17.0.20.1
path: /usr/bin/javac
Ruby:
version: 3.0.2
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react: Not Found
react-native: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: Not found
newArchEnabled: Not found
iOS:
hermesEnabled: Not found
newArchEnabled: Not found
Stacktrace or Logs
ViewOnlyProps reads 222, keys compared 222
InOrderProps reads 223, keys compared 223
InOrderProps read tint 1
OutOfOrderProps reads 223, keys compared 277
OutOfOrderProps read width 10
MANDATORY Reproducer
https://github.com/YevheniiKotyrlo/react-native-fantom-reproducer
Screenshots and Videos
No response
Activity
dave-otf commentedon Nov 3, 2026
Since
nameToIndex_is already populated duringpreparse, a useful regression check could compare repeated reads of a baseViewPropskey such aswidthagainst an in-order custom key, then repeat with a larger registered prop set. The values should stay identical while lookup work stays bounded instead of growing with the distance from the cursor. That would protect both the subclass case and the cost the reproducer measures.