Fabric: Introducing ShadowNode traitCasts

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
This commit is contained in:
Valentin Shergin
2020-02-26 22:08:17 -08:00
committed by Facebook Github Bot
parent 432b434b55
commit fa773a1446
5 changed files with 88 additions and 15 deletions
@@ -124,5 +124,32 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
YGMeasureMode heightMode);
};
template <>
inline YogaLayoutableShadowNode const &
traitCast<YogaLayoutableShadowNode const &>(ShadowNode const &shadowNode) {
bool castable =
shadowNode.getTraits().check(ShadowNodeTraits::Trait::YogaLayoutableKind);
assert(
castable ==
(dynamic_cast<YogaLayoutableShadowNode const *>(&shadowNode) != nullptr));
assert(castable);
(void)castable;
return static_cast<YogaLayoutableShadowNode const &>(shadowNode);
}
template <>
inline YogaLayoutableShadowNode const *
traitCast<YogaLayoutableShadowNode const *>(ShadowNode const *shadowNode) {
bool castable = shadowNode->getTraits().check(
ShadowNodeTraits::Trait::YogaLayoutableKind);
assert(
castable ==
(dynamic_cast<YogaLayoutableShadowNode const *>(shadowNode) != nullptr));
if (!castable) {
return nullptr;
}
return static_cast<YogaLayoutableShadowNode const *>(shadowNode);
}
} // namespace react
} // namespace facebook
@@ -160,5 +160,32 @@ class LayoutableShadowNode : public ShadowNode {
LayoutMetrics layoutMetrics_;
};
template <>
inline LayoutableShadowNode const &traitCast<LayoutableShadowNode const &>(
ShadowNode const &shadowNode) {
bool castable =
shadowNode.getTraits().check(ShadowNodeTraits::Trait::LayoutableKind);
assert(
castable ==
(dynamic_cast<LayoutableShadowNode const *>(&shadowNode) != nullptr));
assert(castable);
(void)castable;
return static_cast<LayoutableShadowNode const &>(shadowNode);
}
template <>
inline LayoutableShadowNode const *traitCast<LayoutableShadowNode const *>(
ShadowNode const *shadowNode) {
bool castable =
shadowNode->getTraits().check(ShadowNodeTraits::Trait::LayoutableKind);
assert(
castable ==
(dynamic_cast<LayoutableShadowNode const *>(shadowNode) != nullptr));
if (!castable) {
return nullptr;
}
return static_cast<LayoutableShadowNode const *>(shadowNode);
}
} // namespace react
} // namespace facebook
@@ -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 <typename ShadowNodeReferenceT>
ShadowNodeReferenceT traitCast(ShadowNode const &shadowNode);
template <typename ShadowNodePointerT>
ShadowNodePointerT traitCast(ShadowNode const *shadowNode);
} // namespace react
} // namespace facebook
@@ -15,6 +15,9 @@ class LayoutableShadowNodeTest : public ::testing::Test {
LayoutableShadowNodeTest()
: eventDispatcher_(std::shared_ptr<EventDispatcher const>()),
componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) {
auto traits = TestShadowNode::BaseTraits();
auto familyA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 9,
@@ -30,7 +33,7 @@ class LayoutableShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyA,
ShadowNodeTraits{});
traits);
auto familyAA = std::make_shared<ShadowNodeFamily>(
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<TestShadowNode>(
ShadowNodeFragment{
@@ -50,7 +53,7 @@ class LayoutableShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAA,
traits);
rootTraits);
auto familyAAA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
@@ -67,7 +70,7 @@ class LayoutableShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAAA,
ShadowNodeTraits{});
traits);
nodeA_->appendChild(nodeAA_);
nodeAA_->appendChild(nodeAAA_);
@@ -35,6 +35,8 @@ class ShadowNodeTest : public ::testing::Test {
auto props = std::make_shared<const TestProps>();
auto traits = TestShadowNode::BaseTraits();
auto familyAA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 11,
@@ -49,7 +51,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAA,
ShadowNodeTraits{});
traits);
auto familyABA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
@@ -65,7 +67,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyABA,
ShadowNodeTraits{});
traits);
auto familyABB = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
@@ -81,7 +83,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyABB,
ShadowNodeTraits{});
traits);
auto nodeABChildren = std::make_shared<SharedShadowNodeList>(
SharedShadowNodeList{nodeABA_, nodeABB_});
@@ -100,7 +102,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ nodeABChildren,
},
familyAB,
ShadowNodeTraits{});
traits);
auto familyAC = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
@@ -116,7 +118,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAC,
ShadowNodeTraits{});
traits);
auto nodeAChildren = std::make_shared<SharedShadowNodeList>(
SharedShadowNodeList{nodeAA_, nodeAB_, nodeAC_});
@@ -135,7 +137,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ nodeAChildren,
},
familyA,
ShadowNodeTraits{});
traits);
auto familyZ = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
@@ -151,7 +153,7 @@ class ShadowNodeTest : public ::testing::Test {
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyZ,
ShadowNodeTraits{});
traits);
}
std::shared_ptr<EventDispatcher const> eventDispatcher_;
@@ -239,6 +241,8 @@ TEST_F(ShadowNodeTest, handleState) {
eventDispatcher_,
componentDescriptor_);
auto traits = TestShadowNode::BaseTraits();
auto props = std::make_shared<const TestProps>();
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<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
/* .state = */ initialState},
family,
ShadowNodeTraits{});
traits);
auto thirdNode = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
/* .state = */ initialState},
family,
ShadowNodeTraits{});
traits);
TestShadowNode::ConcreteState::Shared _state =
std::static_pointer_cast<TestShadowNode::ConcreteState const>(