From 0f9f3898a584acbf1ee7dc661dd495c1ba43dd1c Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Mon, 4 Sep 2023 11:17:07 -0700 Subject: [PATCH] C++ Cleanup 9/N: YGAssert (#39201) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/39201 X-link: https://github.com/facebook/yoga/pull/1353 ## This diff This moves and renames `YGAssert`, and removes it from the public API, since external users should not need to call into internal Yoga assert functions, and the current API prevents us from making this a macro later to include the condition in the message. ## This stack The organization of the C++ internals of Yoga are in need of attention. 1. Some of the C++ internals are namespaced, but others not. 2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these) 2. Most of the files are in a flat hierarchy, except for event tracing in its own folder 3. Some files and functions begin with YG, others don’t 4. Some functions are uppercase, others are not 5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about 6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h) 7. There is no clear indication from file structure or type naming what is private vs not 8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers This stack does some much needed spring cleaning: 1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy 3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended 4. Utils files are split 5. Most C++ internals drop the YG prefix 6. Most C++ internal function names are all lower camel case 7. We start to split up Yoga.cpp 8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings 9. It is not possible to use private APIs without static casting handles to internal classes This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well. These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer. Reviewed By: rshest Differential Revision: D48769809 fbshipit-source-id: 0943a84420f8c0672c479accd9c15daa00ce669e --- .../ReactCommon/yoga/yoga/Yoga.cpp | 77 ++++--------------- .../react-native/ReactCommon/yoga/yoga/Yoga.h | 18 +---- .../yoga/yoga/algorithm/FlexDirection.h | 14 +--- .../yoga/yoga/debug/AssertFatal.cpp | 65 ++++++++++++++++ .../ReactCommon/yoga/yoga/debug/AssertFatal.h | 25 ++++++ .../ReactCommon/yoga/yoga/node/Node.cpp | 15 ++-- 6 files changed, 118 insertions(+), 96 deletions(-) create mode 100644 packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.cpp create mode 100644 packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.h diff --git a/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp b/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp index e0cc116215bbc9..02e5ac488c44de 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -19,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -190,8 +190,9 @@ int32_t gConfigInstanceCount = 0; YOGA_EXPORT WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) { auto* node = new yoga::Node{static_cast(config)}; - YGAssert(config != nullptr, "Tried to construct YGNode with null config"); - YGAssertWithConfig( + yoga::assertFatal( + config != nullptr, "Tried to construct YGNode with null config"); + yoga::assertFatalWithConfig( config, node != nullptr, "Could not allocate memory for node"); Event::publish(node, {config}); @@ -210,7 +211,7 @@ YOGA_EXPORT YGNodeRef YGNodeNew(void) { YOGA_EXPORT YGNodeRef YGNodeClone(YGNodeRef oldNodeRef) { auto oldNode = static_cast(oldNodeRef); auto node = new yoga::Node(*oldNode); - YGAssertWithConfig( + yoga::assertFatalWithConfig( oldNode->getConfig(), node != nullptr, "Could not allocate memory for node"); @@ -312,12 +313,12 @@ YOGA_EXPORT void YGNodeInsertChild( auto owner = static_cast(ownerRef); auto child = static_cast(childRef); - YGAssertWithNode( + yoga::assertFatalWithNode( owner, child->getOwner() == nullptr, "Child already has a owner, it must be removed first."); - YGAssertWithNode( + yoga::assertFatalWithNode( owner, !owner->hasMeasureFunc(), "Cannot add child: Nodes with measure functions cannot have children."); @@ -456,7 +457,7 @@ YOGA_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node) { YOGA_EXPORT void YGNodeMarkDirty(const YGNodeRef nodeRef) { auto node = static_cast(nodeRef); - YGAssertWithNode( + yoga::assertFatalWithNode( node, node->hasMeasureFunc(), "Only leaf nodes with custom measure functions " @@ -943,7 +944,7 @@ YOGA_EXPORT YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) { YOGA_EXPORT type YGNodeLayoutGet##name( \ const YGNodeRef nodeRef, const YGEdge edge) { \ auto node = static_cast(nodeRef); \ - YGAssertWithNode( \ + yoga::assertFatalWithNode( \ node, \ edge <= YGEdgeEnd, \ "Cannot get layout properties of multi-edge shorthands"); \ @@ -1053,7 +1054,7 @@ static float YGBaseline(yoga::Node* node, void* layoutContext) { Event::publish(node); - YGAssertWithNode( + yoga::assertFatalWithNode( node, !yoga::isUndefined(baseline), "Expect custom baseline function to not return NaN"); @@ -1669,7 +1670,7 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions( LayoutData& layoutMarkerData, void* const layoutContext, const LayoutPassReason reason) { - YGAssertWithNode( + yoga::assertFatalWithNode( node, node->hasMeasureFunc(), "Expected node to have custom measure function"); @@ -2731,14 +2732,14 @@ static void YGNodelayoutImpl( const uint32_t depth, const uint32_t generationCount, const LayoutPassReason reason) { - YGAssertWithNode( + yoga::assertFatalWithNode( node, yoga::isUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined : true, "availableWidth is indefinite so widthMeasureMode must be " "YGMeasureModeUndefined"); - YGAssertWithNode( + yoga::assertFatalWithNode( node, yoga::isUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined @@ -4089,7 +4090,7 @@ bool YGLayoutNodeInternal( YOGA_EXPORT void YGConfigSetPointScaleFactor( const YGConfigRef config, const float pixelsInPoint) { - YGAssertWithConfig( + yoga::assertFatalWithConfig( config, pixelsInPoint >= 0.0f, "Scale factor should not be less than zero"); @@ -4287,56 +4288,6 @@ YOGA_EXPORT void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { } } -static void fatalWithMessage(const char* message) { -#if defined(__cpp_exceptions) - throw std::logic_error(message); -#else - std::terminate(); -#endif -} - -void YGAssert(const bool condition, const char* message) { - if (!condition) { - yoga::log( - static_cast(nullptr), - YGLogLevelFatal, - nullptr, - "%s\n", - message); - fatalWithMessage(message); - } -} - -void YGAssertWithNode( - const YGNodeRef node, - const bool condition, - const char* message) { - if (!condition) { - yoga::log( - static_cast(node), - YGLogLevelFatal, - nullptr, - "%s\n", - message); - fatalWithMessage(message); - } -} - -void YGAssertWithConfig( - const YGConfigRef config, - const bool condition, - const char* message) { - if (!condition) { - yoga::log( - static_cast(config), - YGLogLevelFatal, - nullptr, - "%s\n", - message); - fatalWithMessage(message); - } -} - YOGA_EXPORT void YGConfigSetExperimentalFeatureEnabled( const YGConfigRef config, const YGExperimentalFeature feature, diff --git a/packages/react-native/ReactCommon/yoga/yoga/Yoga.h b/packages/react-native/ReactCommon/yoga/yoga/Yoga.h index 0c8986896c85b5..8f797f887b268b 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/Yoga.h +++ b/packages/react-native/ReactCommon/yoga/yoga/Yoga.h @@ -7,16 +7,9 @@ #pragma once -#include -#include #include -#include -#include -#include - -#ifndef __cplusplus #include -#endif +#include #include #include @@ -303,15 +296,6 @@ WIN_EXPORT float YGNodeLayoutGetBorder(YGNodeRef node, YGEdge edge); WIN_EXPORT float YGNodeLayoutGetPadding(YGNodeRef node, YGEdge edge); WIN_EXPORT void YGConfigSetLogger(YGConfigRef config, YGLogger logger); -WIN_EXPORT void YGAssert(bool condition, const char* message); -WIN_EXPORT void YGAssertWithNode( - YGNodeRef node, - bool condition, - const char* message); -WIN_EXPORT void YGAssertWithConfig( - YGConfigRef config, - bool condition, - const char* message); // Set this to number of pixels in 1 point to round calculation results If you // want to avoid rounding - set PointScaleFactor to 0 WIN_EXPORT void YGConfigSetPointScaleFactor( diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/FlexDirection.h b/packages/react-native/ReactCommon/yoga/yoga/algorithm/FlexDirection.h index 9cb87462aa796c..b6e01190d734d4 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/FlexDirection.h +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/FlexDirection.h @@ -9,6 +9,8 @@ #include +#include + namespace facebook::yoga { inline bool isRow(const YGFlexDirection flexDirection) { @@ -55,11 +57,7 @@ inline YGEdge leadingEdge(const YGFlexDirection flexDirection) { return YGEdgeRight; } - YGAssert(false, "Invalid YGFlexDirection"); - - // Avoid "not all control paths return a value" warning until next diff adds - // assert with [[noreturn]] - return YGEdgeTop; + fatalWithMessage("Invalid YGFlexDirection"); } inline YGEdge trailingEdge(const YGFlexDirection flexDirection) { @@ -74,11 +72,7 @@ inline YGEdge trailingEdge(const YGFlexDirection flexDirection) { return YGEdgeLeft; } - YGAssert(false, "Invalid YGFlexDirection"); - - // Avoid "not all control paths return a value" warning until next diff adds - // assert with [[noreturn]] - return YGEdgeTop; + fatalWithMessage("Invalid YGFlexDirection"); } } // namespace facebook::yoga diff --git a/packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.cpp b/packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.cpp new file mode 100644 index 00000000000000..e079777c77f910 --- /dev/null +++ b/packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.cpp @@ -0,0 +1,65 @@ +/* + * 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 + +namespace facebook::yoga { + +[[noreturn]] void fatalWithMessage(const char* message) { +#if defined(__cpp_exceptions) + throw std::logic_error(message); +#else + std::terminate(); +#endif +} + +void assertFatal(const bool condition, const char* message) { + if (!condition) { + yoga::log( + static_cast(nullptr), + YGLogLevelFatal, + nullptr, + "%s\n", + message); + fatalWithMessage(message); + } +} + +void assertFatalWithNode( + const YGNodeRef node, + const bool condition, + const char* message) { + if (!condition) { + yoga::log( + static_cast(node), + YGLogLevelFatal, + nullptr, + "%s\n", + message); + fatalWithMessage(message); + } +} + +void assertFatalWithConfig( + const YGConfigRef config, + const bool condition, + const char* message) { + if (!condition) { + yoga::log( + static_cast(config), + YGLogLevelFatal, + nullptr, + "%s\n", + message); + fatalWithMessage(message); + } +} + +} // namespace facebook::yoga diff --git a/packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.h b/packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.h new file mode 100644 index 00000000000000..461eb8ed440645 --- /dev/null +++ b/packages/react-native/ReactCommon/yoga/yoga/debug/AssertFatal.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +[[noreturn]] void fatalWithMessage(const char* message); + +void assertFatal(bool condition, const char* message); +void assertFatalWithNode(YGNodeRef node, bool condition, const char* message); +void assertFatalWithConfig( + YGConfigRef config, + bool condition, + const char* message); + +} // namespace facebook::yoga diff --git a/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp b/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp index 0eca4e115e5ede..d561d314770c94 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp @@ -10,13 +10,15 @@ #include #include +#include #include #include namespace facebook::yoga { Node::Node(yoga::Config* config) : config_{config} { - YGAssert(config != nullptr, "Attempting to construct Node with null config"); + yoga::assertFatal( + config != nullptr, "Attempting to construct Node with null config"); flags_.hasNewLayout = true; if (config->useWebDefaults()) { @@ -234,7 +236,7 @@ void Node::setMeasureFunc(decltype(Node::measure_) measureFunc) { // places in Litho setNodeType(YGNodeTypeDefault); } else { - YGAssertWithNode( + yoga::assertFatalWithNode( this, children_.size() == 0, "Cannot set measure function: Nodes with measure functions cannot have " @@ -274,8 +276,9 @@ void Node::insertChild(Node* child, uint32_t index) { } void Node::setConfig(yoga::Config* config) { - YGAssert(config != nullptr, "Attempting to set a null config on a Node"); - YGAssertWithConfig( + yoga::assertFatal( + config != nullptr, "Attempting to set a null config on a Node"); + yoga::assertFatalWithConfig( config, config->useWebDefaults() == config_->useWebDefaults(), "UseWebDefaults may not be changed after constructing a Node"); @@ -592,11 +595,11 @@ FloatOptional Node::getTrailingPaddingAndBorder( } void Node::reset() { - YGAssertWithNode( + yoga::assertFatalWithNode( this, children_.size() == 0, "Cannot reset a node which still has children attached"); - YGAssertWithNode( + yoga::assertFatalWithNode( this, owner_ == nullptr, "Cannot reset a node still attached to a owner"); *this = Node{getConfig()};