diff --git a/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h b/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h index 8739d849a4..2e2ee37fd2 100644 --- a/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h +++ b/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -40,7 +41,14 @@ public: const InstanceHandle &instanceHandle, const SharedProps &props ) const override { - return std::make_shared(tag, rootTag, instanceHandle, std::static_pointer_cast(props)); + return std::make_shared( + tag, + rootTag, + instanceHandle, + std::static_pointer_cast(props), + ShadowNode::emptySharedShadowNodeSharedList(), + getCloneFunction() + ); } SharedShadowNode cloneShadowNode( @@ -67,6 +75,18 @@ public: return ShadowNodeT::Props(rawProps, props); }; +private: + mutable ShadowNodeCloneFunction cloneFunction_; + + ShadowNodeCloneFunction getCloneFunction() const { + if (!cloneFunction_) { + cloneFunction_ = [this](const SharedShadowNode &shadowNode, const SharedProps &props, const SharedShadowNodeSharedList &children) { + return this->cloneShadowNode(shadowNode, props, children); + }; + } + + return cloneFunction_; + } }; } // namespace react diff --git a/ReactCommon/fabric/core/shadownode/ConcreteShadowNode.h b/ReactCommon/fabric/core/shadownode/ConcreteShadowNode.h index 278d448708..9a18cf7e1e 100644 --- a/ReactCommon/fabric/core/shadownode/ConcreteShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ConcreteShadowNode.h @@ -51,14 +51,16 @@ public: const Tag &rootTag, const InstanceHandle &instanceHandle, const SharedConcreteProps &props = ConcreteShadowNode::defaultSharedProps(), - const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList() + const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList(), + const ShadowNodeCloneFunction &cloneFunction = nullptr ): ShadowNode( tag, rootTag, instanceHandle, (SharedProps)props, - children + children, + cloneFunction ) {}; ConcreteShadowNode( diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.cpp b/ReactCommon/fabric/core/shadownode/ShadowNode.cpp index e7c6b68f22..f1272c3269 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.cpp +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.cpp @@ -25,13 +25,15 @@ ShadowNode::ShadowNode( const Tag &rootTag, const InstanceHandle &instanceHandle, const SharedProps &props, - const SharedShadowNodeSharedList &children + const SharedShadowNodeSharedList &children, + const ShadowNodeCloneFunction &cloneFunction ): tag_(tag), rootTag_(rootTag), instanceHandle_(instanceHandle), props_(props), children_(std::make_shared(*children)), + cloneFunction_(cloneFunction), revision_(1) {} ShadowNode::ShadowNode( @@ -45,8 +47,17 @@ ShadowNode::ShadowNode( props_(props ? props : shadowNode->props_), children_(std::make_shared(*(children ? children : shadowNode->children_))), sourceNode_(shadowNode), + cloneFunction_(shadowNode->cloneFunction_), revision_(shadowNode->revision_ + 1) {} +SharedShadowNode ShadowNode::clone( + const SharedProps &props, + const SharedShadowNodeSharedList &children +) const { + assert(cloneFunction_); + return cloneFunction_(shared_from_this(), props_, children_); +} + #pragma mark - Getters SharedShadowNodeSharedList ShadowNode::getChildren() const { diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index a5b8a2ff9d..e83e622763 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -26,9 +27,12 @@ using SharedShadowNodeSharedList = std::shared_ptr; using SharedShadowNodeUnsharedList = std::shared_ptr; using WeakShadowNode = std::weak_ptr; +using ShadowNodeCloneFunction = std::function; + class ShadowNode: public virtual Sealable, - public virtual DebugStringConvertible { + public virtual DebugStringConvertible, + public std::enable_shared_from_this { public: static SharedShadowNodeSharedList emptySharedShadowNodeSharedList(); @@ -39,7 +43,8 @@ public: const Tag &rootTag, const InstanceHandle &instanceHandle, const SharedProps &props = SharedProps(), - const SharedShadowNodeSharedList &children = SharedShadowNodeSharedList() + const SharedShadowNodeSharedList &children = SharedShadowNodeSharedList(), + const ShadowNodeCloneFunction &cloneFunction = nullptr ); ShadowNode( @@ -48,6 +53,14 @@ public: const SharedShadowNodeSharedList &children = nullptr ); + /* + * Clones the shadow node using stored `cloneFunction`. + */ + SharedShadowNode clone( + const SharedProps &props = nullptr, + const SharedShadowNodeSharedList &children = nullptr + ) const; + #pragma mark - Getters virtual ComponentHandle getComponentHandle() const = 0; @@ -112,6 +125,12 @@ protected: private: + /* + * A reference to a cloning function that understands how to clone + * the specific type of ShadowNode. + */ + ShadowNodeCloneFunction cloneFunction_; + /* * A number of the generation of the ShadowNode instance; * is used and useful for debug-printing purposes *only*. diff --git a/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp b/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp index 168cf48869..8c8204684b 100644 --- a/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp +++ b/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp @@ -115,3 +115,38 @@ TEST(ShadowNodeTest, handleSourceNode) { ASSERT_EQ(nodeThirdGeneration->getSourceNode(), nodeSecondGeneration); ASSERT_EQ(nodeSecondGeneration->getSourceNode(), nodeFirstGeneration); } + +TEST(ShadowNodeTest, handleCloneFunction) { + auto firstNode = std::make_shared(9, 1, (void *)NULL); + + // The shadow node is not clonable if `cloneFunction` is not provided, + ASSERT_DEATH_IF_SUPPORTED(firstNode->clone(), "cloneFunction_"); + + auto secondNode = std::make_shared( + 9, + 1, + (void *)NULL, + std::make_shared(), + ShadowNode::emptySharedShadowNodeSharedList(), + [](const SharedShadowNode &shadowNode, const SharedProps &props, const SharedShadowNodeSharedList &children) { + return std::make_shared( + std::static_pointer_cast(shadowNode), + props, + children + ); + } + ); + + auto secondNodeClone = secondNode->clone(); + + // Those two nodes are *not* same. + ASSERT_NE(secondNode, secondNodeClone); + + // `secondNodeClone` is an instance of `TestShadowNode`. + ASSERT_NE(std::dynamic_pointer_cast(secondNodeClone), nullptr); + + // Both nodes have same content. + ASSERT_EQ(secondNode->getTag(), secondNodeClone->getTag()); + ASSERT_EQ(secondNode->getRootTag(), secondNodeClone->getRootTag()); + ASSERT_EQ(secondNode->getProps(), secondNodeClone->getProps()); +} diff --git a/ReactCommon/fabric/view/ViewShadowNode.cpp b/ReactCommon/fabric/view/ViewShadowNode.cpp index 111d220ec1..51d4be23ba 100644 --- a/ReactCommon/fabric/view/ViewShadowNode.cpp +++ b/ReactCommon/fabric/view/ViewShadowNode.cpp @@ -21,14 +21,16 @@ ViewShadowNode::ViewShadowNode( const Tag &rootTag, const InstanceHandle &instanceHandle, const SharedViewProps &props, - const SharedShadowNodeSharedList &children + const SharedShadowNodeSharedList &children, + const ShadowNodeCloneFunction &cloneFunction ): ConcreteShadowNode( tag, rootTag, instanceHandle, props, - children + children, + cloneFunction ), AccessibleShadowNode( props @@ -92,9 +94,9 @@ SharedLayoutableShadowNodeList ViewShadowNode::getLayoutableChildNodes() const { SharedLayoutableShadowNode ViewShadowNode::cloneAndReplaceChild(const SharedLayoutableShadowNode &child) { ensureUnsealed(); - auto viewShadowNodeChild = std::dynamic_pointer_cast(child); - assert(viewShadowNodeChild); - auto viewShadowNodeChildClone = std::make_shared(viewShadowNodeChild); + auto childShadowNode = std::dynamic_pointer_cast(child); + assert(childShadowNode); + auto childShadowNodeClone = childShadowNode->clone(); // This is overloading of `SharedLayoutableShadowNode::cloneAndReplaceChild`, // the method is used to clone some node as a preparation for future mutation @@ -107,10 +109,11 @@ SharedLayoutableShadowNode ViewShadowNode::cloneAndReplaceChild(const SharedLayo // In other words, if we don't compensate this change here, // the Diffing algorithm will compare wrong trees // ("new-but-not-laid-out-yet vs. new" instead of "committed vs. new"). - viewShadowNodeChildClone->shallowSourceNode(); + auto nonConstChildShadowNodeClone = std::const_pointer_cast(childShadowNodeClone); + nonConstChildShadowNodeClone->shallowSourceNode(); - ShadowNode::replaceChild(viewShadowNodeChild, viewShadowNodeChildClone); - return std::static_pointer_cast(viewShadowNodeChildClone); + ShadowNode::replaceChild(childShadowNode, childShadowNodeClone); + return std::dynamic_pointer_cast(childShadowNodeClone); } #pragma mark - Equality diff --git a/ReactCommon/fabric/view/ViewShadowNode.h b/ReactCommon/fabric/view/ViewShadowNode.h index 230aad5694..5809430df6 100644 --- a/ReactCommon/fabric/view/ViewShadowNode.h +++ b/ReactCommon/fabric/view/ViewShadowNode.h @@ -35,7 +35,8 @@ public: const Tag &rootTag, const InstanceHandle &instanceHandle, const SharedViewProps &props = ViewShadowNode::defaultSharedProps(), - const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList() + const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList(), + const ShadowNodeCloneFunction &cloneFunction = nullptr ); ViewShadowNode(