From fa773a144604e723fe85a245518d8af4f4f7e8fb Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Wed, 26 Feb 2020 22:03:14 -0800 Subject: [PATCH] Fabric: Introducing ShadowNode `traitCast`s Summary: `traitCast` is a special form of static_cast that checks additional requirements (similar to `dynamic_cast`) before performing the cast. We will use that in many places in the coming diffs. Restructuring the class hierarchy in the previous diff finally allows us to do static casts among ShadowNode and LayoutableShadowNode and other classes (previously it wasn't allowed because of lack of common base class). Why we don't want to use `dynamic_cast`: * It's expensive (and we need to do the cast on the hottest fragments of the framework). (See: (1) http://www.stroustrup.com/fast_dynamic_casting.pdf and (2) https://www.youtube.com/watch?v=ARYP83yNAWk Herb Sutter & proposal of `down_cast`). * It's code-size inefficient, whereas `static_cast` has zero runtime and code-size overhead. * Removing `dynamic_cast` will allow us finally to opt-out `RTTI` (additional code size and perm wins). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D20052024 fbshipit-source-id: d293c1cf80deb7817d333d5306d6b32bf3abdb27 --- .../view/yoga/YogaLayoutableShadowNode.h | 27 +++++++++++++++++++ .../fabric/core/layout/LayoutableShadowNode.h | 27 +++++++++++++++++++ .../fabric/core/shadownode/ShadowNode.h | 12 +++++++++ .../core/tests/LayoutableShadowNodeTest.cpp | 13 +++++---- .../fabric/core/tests/ShadowNodeTest.cpp | 24 ++++++++++------- 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h b/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h index 68f603840ef..bd58941c459 100644 --- a/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h +++ b/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h @@ -124,5 +124,32 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode { YGMeasureMode heightMode); }; +template <> +inline YogaLayoutableShadowNode const & +traitCast(ShadowNode const &shadowNode) { + bool castable = + shadowNode.getTraits().check(ShadowNodeTraits::Trait::YogaLayoutableKind); + assert( + castable == + (dynamic_cast(&shadowNode) != nullptr)); + assert(castable); + (void)castable; + return static_cast(shadowNode); +} + +template <> +inline YogaLayoutableShadowNode const * +traitCast(ShadowNode const *shadowNode) { + bool castable = shadowNode->getTraits().check( + ShadowNodeTraits::Trait::YogaLayoutableKind); + assert( + castable == + (dynamic_cast(shadowNode) != nullptr)); + if (!castable) { + return nullptr; + } + return static_cast(shadowNode); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/core/layout/LayoutableShadowNode.h b/ReactCommon/fabric/core/layout/LayoutableShadowNode.h index ca946be4589..ec6e8e485e6 100644 --- a/ReactCommon/fabric/core/layout/LayoutableShadowNode.h +++ b/ReactCommon/fabric/core/layout/LayoutableShadowNode.h @@ -160,5 +160,32 @@ class LayoutableShadowNode : public ShadowNode { LayoutMetrics layoutMetrics_; }; +template <> +inline LayoutableShadowNode const &traitCast( + ShadowNode const &shadowNode) { + bool castable = + shadowNode.getTraits().check(ShadowNodeTraits::Trait::LayoutableKind); + assert( + castable == + (dynamic_cast(&shadowNode) != nullptr)); + assert(castable); + (void)castable; + return static_cast(shadowNode); +} + +template <> +inline LayoutableShadowNode const *traitCast( + ShadowNode const *shadowNode) { + bool castable = + shadowNode->getTraits().check(ShadowNodeTraits::Trait::LayoutableKind); + assert( + castable == + (dynamic_cast(shadowNode) != nullptr)); + if (!castable) { + return nullptr; + } + return static_cast(shadowNode); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index 63ccdfcd866..e4482f469a2 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -194,5 +194,17 @@ class ShadowNode : public Sealable, public DebugStringConvertible { ShadowNodeTraits traits_; }; +/* + * Template declarations for future specializations in concrete classes. + * `traitCast` checks for a trait that corresponds to the provided type and + * performs `static_cast`. Practically, the behavior is identical to + * `dynamic_cast` with very little runtime overhead. + */ +template +ShadowNodeReferenceT traitCast(ShadowNode const &shadowNode); + +template +ShadowNodePointerT traitCast(ShadowNode const *shadowNode); + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp index f58e949952b..9f3388d882c 100644 --- a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp +++ b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp @@ -15,6 +15,9 @@ class LayoutableShadowNodeTest : public ::testing::Test { LayoutableShadowNodeTest() : eventDispatcher_(std::shared_ptr()), componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) { + + auto traits = TestShadowNode::BaseTraits(); + auto familyA = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 9, @@ -30,7 +33,7 @@ class LayoutableShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyA, - ShadowNodeTraits{}); + traits); auto familyAA = std::make_shared( ShadowNodeFamilyFragment{ @@ -41,8 +44,8 @@ class LayoutableShadowNodeTest : public ::testing::Test { eventDispatcher_, componentDescriptor_); - auto traits = TestShadowNode::BaseTraits(); - traits.set(ShadowNodeTraits::Trait::RootNodeKind); + auto rootTraits = traits; + rootTraits.set(ShadowNodeTraits::Trait::RootNodeKind); nodeAA_ = std::make_shared( ShadowNodeFragment{ @@ -50,7 +53,7 @@ class LayoutableShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyAA, - traits); + rootTraits); auto familyAAA = std::make_shared( ShadowNodeFamilyFragment{ @@ -67,7 +70,7 @@ class LayoutableShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyAAA, - ShadowNodeTraits{}); + traits); nodeA_->appendChild(nodeAA_); nodeAA_->appendChild(nodeAAA_); diff --git a/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp b/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp index 0a2e5525bb7..08e331a56fc 100644 --- a/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp +++ b/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp @@ -35,6 +35,8 @@ class ShadowNodeTest : public ::testing::Test { auto props = std::make_shared(); + auto traits = TestShadowNode::BaseTraits(); + auto familyAA = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 11, @@ -49,7 +51,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyAA, - ShadowNodeTraits{}); + traits); auto familyABA = std::make_shared( ShadowNodeFamilyFragment{ @@ -65,7 +67,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyABA, - ShadowNodeTraits{}); + traits); auto familyABB = std::make_shared( ShadowNodeFamilyFragment{ @@ -81,7 +83,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyABB, - ShadowNodeTraits{}); + traits); auto nodeABChildren = std::make_shared( SharedShadowNodeList{nodeABA_, nodeABB_}); @@ -100,7 +102,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ nodeABChildren, }, familyAB, - ShadowNodeTraits{}); + traits); auto familyAC = std::make_shared( ShadowNodeFamilyFragment{ @@ -116,7 +118,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyAC, - ShadowNodeTraits{}); + traits); auto nodeAChildren = std::make_shared( SharedShadowNodeList{nodeAA_, nodeAB_, nodeAC_}); @@ -135,7 +137,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ nodeAChildren, }, familyA, - ShadowNodeTraits{}); + traits); auto familyZ = std::make_shared( ShadowNodeFamilyFragment{ @@ -151,7 +153,7 @@ class ShadowNodeTest : public ::testing::Test { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyZ, - ShadowNodeTraits{}); + traits); } std::shared_ptr eventDispatcher_; @@ -239,6 +241,8 @@ TEST_F(ShadowNodeTest, handleState) { eventDispatcher_, componentDescriptor_); + auto traits = TestShadowNode::BaseTraits(); + auto props = std::make_shared(); auto fragment = ShadowNodeFragment{ /* .props = */ props, @@ -254,21 +258,21 @@ TEST_F(ShadowNodeTest, handleState) { /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ initialState}, family, - ShadowNodeTraits{}); + traits); auto secondNode = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ initialState}, family, - ShadowNodeTraits{}); + traits); auto thirdNode = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ initialState}, family, - ShadowNodeTraits{}); + traits); TestShadowNode::ConcreteState::Shared _state = std::static_pointer_cast(