diff --git a/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp b/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp index 1bf4bf4ae85b..ed4b6453bc15 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp @@ -39,7 +39,7 @@ const RawValue* RawPropsParser::at( } // This is not thread-safe part; this happens only during initialization of // a `ComponentDescriptor` where it is actually safe. - keys_.push_back(key); + keys_.emplace_back(key); react_native_assert(size < std::numeric_limits::max()); nameToIndex_.insert(key, static_cast(size)); return nullptr; diff --git a/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.h b/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.h index 5613f794ef06..d3e78b1490e0 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.h +++ b/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace facebook::react { @@ -74,7 +75,7 @@ class RawPropsParser final { */ const RawValue *at(const RawProps &rawProps, std::string_view key) const noexcept; - mutable std::vector keys_{}; + mutable std::vector keys_{}; mutable RawPropsKeyMap nameToIndex_{}; mutable bool ready_{false}; }; diff --git a/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsParserTest.cpp b/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsParserTest.cpp new file mode 100644 index 000000000000..d0d8b31cbdc8 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsParserTest.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include +#include +#include + +using namespace facebook; +using namespace facebook::react; + +namespace { + +struct KeyParts { + const char* prefix; + const char* suffix; +}; + +constexpr std::array keyParts{{ + {"border", nullptr}, + {nullptr, "End"}, + {"border", "End"}, + {"accessibilityBorderEnd", nullptr}, + {nullptr, "AccessibilityProperty"}, +}}; + +class ComposedKeyProps : public Props { + public: + ComposedKeyProps() = default; + ComposedKeyProps( + const PropsParserContext&, + const ComposedKeyProps&, + const RawProps& rawProps) { + for (const auto& parts : keyParts) { + rawProps.at("Width", parts.prefix, parts.suffix); + rawProps.at("Color", parts.prefix, parts.suffix); + // Repeated accesses during preparation must refer to the same key. + rawProps.at("Width", parts.prefix, parts.suffix); + } + } +}; + +class TemporaryKeyProps : public Props { + public: + TemporaryKeyProps() = default; + TemporaryKeyProps( + const PropsParserContext&, + const TemporaryKeyProps&, + const RawProps& rawProps) { + for (int i = 0; i < 32; ++i) { + auto key = "prop" + std::to_string(i); + rawProps.at(key.c_str()); + } + } +}; + +} // namespace + +TEST(RawPropsParserTest, ownsComposedKeys) { + RawPropsParser parser; + parser.prepare(); + + folly::dynamic values = folly::dynamic::object(); + for (const auto& parts : keyParts) { + const std::string prefix = parts.prefix ? parts.prefix : ""; + const std::string suffix = parts.suffix ? parts.suffix : ""; + values[prefix + "Width" + suffix] = 17; + values[prefix + "Color" + suffix] = 42; + } + RawProps rawProps(values); + rawProps.parse(parser); + + for (const auto& parts : keyParts) { + // Reverse order and repeat a key to exercise cursor wraparound. + const auto* color = rawProps.at("Color", parts.prefix, parts.suffix); + ASSERT_NE(color, nullptr); + EXPECT_EQ(static_cast(*color), 42); + const auto* width = rawProps.at("Width", parts.prefix, parts.suffix); + ASSERT_NE(width, nullptr); + EXPECT_EQ(static_cast(*width), 17); + EXPECT_EQ(rawProps.at("Width", parts.prefix, parts.suffix), width); + } +} + +TEST(RawPropsParserTest, ownsTemporaryNamesAcrossStorageGrowth) { + RawPropsParser parser; + parser.prepare(); + + folly::dynamic values = folly::dynamic::object(); + for (int i = 0; i < 32; ++i) { + values["prop" + std::to_string(i)] = i; + } + RawProps rawProps(values); + rawProps.parse(parser); + + for (int i = 31; i >= 0; --i) { + auto key = "prop" + std::to_string(i); + const auto* value = rawProps.at(key.c_str()); + ASSERT_NE(value, nullptr); + EXPECT_EQ(static_cast(*value), i); + } +}