From 6de547f3fd9ecb59a12392bd180f3a17ecb094ce Mon Sep 17 00:00:00 2001 From: Open Source Bot Date: Mon, 2 Nov 2026 14:00:22 -0800 Subject: [PATCH] Fix style keywords reading back as the wrong unit after a non-integer size (#2056) (#59745) Summary: ## Summary Fixes #2052. Replacing a non-integer size with `max-content`, `fit-content`, or `stretch` could make the getter return a different keyword. `YGNodeStyleSetHeight(node, 0.5f)`, then `YGNodeStyleSetWidth(node, 0.25f)`, then `YGNodeStyleSetWidthMaxContent(node)` made `YGNodeStyleGetWidth` return `YGUnitFitContent` instead of `YGUnitMaxContent`. A non-integer size is stored in the value pool, and the handle keeps that pool index in its value bits. `storeKeyword` changed the type to `Keyword` but left the indexed bit set, so `isKeyword` compared the pool index with the keyword enum (`MaxContent` = 0, `FitContent` = 1, `Stretch` = 2). The same mismatch affects height, min and max size, and flex-basis. A pool index of 3 or more matches no keyword and hits the debug assert in `getSize`. `auto`, `undefined`, margin, and padding read the type bits only, so those getters were already correct. `storeKeyword` still writes the keyword into the handle's existing pool slot. `getSize` now reads that slot when the indexed bit is set, so the handle keeps the slot and the getter returns the stored keyword. ## Test plan - `YogaTest.width_keyword_replaces_pooled_number` covers the #2052 sequence. - `StyleValuePool.store_keywords_after_large_int` checks max-content, fit-content, and stretch in pool slots 0 through 3. - `StyleValuePool.store_large_int_and_keyword_repeatedly` switches a pooled number and max-content 5000 times and checks that the slot is reused. - `./unit_tests Debug`: 880 passed, 0 failed. Baseline before these tests was 877 passed, 0 failed. Changelog: [General][Fixed] - Fix reading back max-content, fit-content, and stretch after a non-integer size was stored Reviewed By: javache Differential Revision: D128898045 --- .../yoga/yoga/style/StyleValueHandle.h | 4 ---- .../yoga/yoga/style/StyleValuePool.h | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/StyleValueHandle.h b/packages/react-native/ReactCommon/yoga/yoga/style/StyleValueHandle.h index 6a2b58902339..ce622dd059fa 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/StyleValueHandle.h +++ b/packages/react-native/ReactCommon/yoga/yoga/style/StyleValueHandle.h @@ -76,10 +76,6 @@ class StyleValueHandle { // Intentionally leaving out auto as a fast path enum class Keyword : uint8_t { MaxContent, FitContent, Stretch }; - constexpr bool isKeyword(Keyword keyword) const { - return type() == Type::Keyword && value() == static_cast(keyword); - } - constexpr Type type() const { return static_cast(repr_ & kHandleTypeMask); } diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h b/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h index fb31193b9789..c5846658ffb6 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h +++ b/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h @@ -89,11 +89,11 @@ class StyleValuePool { return StyleSizeLength::undefined(); } else if (handle.isAuto()) { return StyleSizeLength::ofAuto(); - } else if (handle.isKeyword(StyleValueHandle::Keyword::MaxContent)) { + } else if (isKeyword(handle, StyleValueHandle::Keyword::MaxContent)) { return StyleSizeLength::ofMaxContent(); - } else if (handle.isKeyword(StyleValueHandle::Keyword::FitContent)) { + } else if (isKeyword(handle, StyleValueHandle::Keyword::FitContent)) { return StyleSizeLength::ofFitContent(); - } else if (handle.isKeyword(StyleValueHandle::Keyword::Stretch)) { + } else if (isKeyword(handle, StyleValueHandle::Keyword::Stretch)) { return StyleSizeLength::ofStretch(); } else { assert( @@ -132,6 +132,17 @@ class StyleValuePool { } private: + bool isKeyword(StyleValueHandle handle, StyleValueHandle::Keyword keyword) + const { + if (handle.type() != StyleValueHandle::Type::Keyword) { + return false; + } + // storeKeyword() keeps an existing pool slot, so the keyword may be there + uint32_t value = handle.isValueIndexed() ? buffer_.get32(handle.value()) + : handle.value(); + return value == static_cast(keyword); + } + void storeValue( StyleValueHandle& handle, float value,