From 842b7e4ba057a345f508ff1d8b548dd04223c689 Mon Sep 17 00:00:00 2001 From: Bahadir10 Date: Sat, 7 Nov 2026 13:20:21 +0300 Subject: [PATCH] Optimize RawPropsParser::at out-of-order lookup using nameToIndex_ Summary: **Problem**: In `RawPropsParser::at`, when a prop is read out of registration order (such as a component prop struct extending `ViewProps` and re-reading common keys like `width` or `backgroundColor`), the parser stepped linearly through every registered key one at a time, performing equality comparisons on strings until it reached the target key. On components with many props, this incurred dozens of redundant string comparisons per parse. **Proposed change**: Since `RawPropsParser` already maintains `nameToIndex_` (`RawPropsKeyMap`), when the key does not match the cursor's next position, look up the key directly via `nameToIndex_.at(key)`. This replaces the linear string comparison loop with an immediate key map lookup. Fixes #59759 Changelog: [General][Changed] - Optimize out-of-order prop lookup in `RawPropsParser::at` via `nameToIndex_` --- .../react/renderer/core/RawPropsParser.cpp | 31 +++++++++---------- .../renderer/core/tests/RawPropsTest.cpp | 18 +++++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp b/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp index bef5aa2839b0..2340beaeaab6 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp @@ -73,26 +73,25 @@ const RawValue* RawPropsParser::at( // This is trivial if you have a simple Props constructor, but difficult or // impossible if you have a shared sub-prop Struct that is used by multiple // parent Props. -#ifdef REACT_NATIVE_DEBUG - bool resetLoop = false; -#endif - do { - rawProps.keyIndexCursor_++; + rawProps.keyIndexCursor_++; + + if (static_cast(rawProps.keyIndexCursor_) >= keys_.size()) + [[unlikely]] { + rawProps.keyIndexCursor_ = 0; + } - if (static_cast(rawProps.keyIndexCursor_) >= keys_.size()) - [[unlikely]] { + if (!keysEqual(key, keys_[rawProps.keyIndexCursor_])) [[unlikely]] { + auto keyIndex = nameToIndex_.at(key); + if (keyIndex == kRawPropsValueIndexEmpty) [[unlikely]] { #ifdef REACT_NATIVE_DEBUG - if (resetLoop) { - LOG(ERROR) - << "Looked up property name which was not seen when preparing: " - << key; - return nullptr; - } - resetLoop = true; + LOG(ERROR) + << "Looked up property name which was not seen when preparing: " + << key; #endif - rawProps.keyIndexCursor_ = 0; + return nullptr; } - } while (!keysEqual(key, keys_[rawProps.keyIndexCursor_])); + rawProps.keyIndexCursor_ = static_cast(keyIndex); + } auto valueIndex = rawProps.keyIndexToValueIndex_[rawProps.keyIndexCursor_]; return valueIndex == kRawPropsValueIndexEmpty ? nullptr diff --git a/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp b/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp index b23760236f21..c647cf5ad5bd 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp @@ -480,3 +480,21 @@ TEST(RawPropsTest, copyJSIRawProps) { EXPECT_NEAR( copyProps->derivedFloatValue, originalProps->derivedFloatValue, 0.00001); } + +TEST(RawPropsTest, handleOutOfOrderPropsLookup) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + auto raw = RawProps(folly::dynamic::object("intValue", 42)("floatValue", 12.5f)); + + auto parser = RawPropsParser(); + parser.prepare(); + raw.parse(parser); + + // In PropsPrimitiveTypes, keys are registered as: + // intValue, doubleValue, floatValue, stringValue, boolValue + // Lookup floatValue first, then intValue (out-of-order): + EXPECT_NEAR((float)*raw.at("floatValue"), 12.5f, 0.00001); + EXPECT_EQ((int)*raw.at("intValue"), 42); + EXPECT_NEAR((float)*raw.at("floatValue"), 12.5f, 0.00001); +}