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); +}