1 #ifdef RCT_NEW_ARCH_ENABLED
2 
3 #include "ShadowTreeCloner.h"
4 #include "FabricUtils.h"
5 
6 namespace reanimated {
7 
8 ShadowTreeCloner::ShadowTreeCloner(
9     std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry,
10     std::shared_ptr<UIManager> uiManager,
11     SurfaceId surfaceId)
12     : newestShadowNodesRegistry_{newestShadowNodesRegistry},
13       propsParserContext_{
14           surfaceId,
15           *getContextContainerFromUIManager(&*uiManager)} {}
16 
17 ShadowTreeCloner::~ShadowTreeCloner() {
18 #ifdef DEBUG
19   react_native_assert(
20       yogaChildrenUpdates_.empty() &&
21       "Deallocating `ShadowTreeCloner` without calling `updateYogaChildren`.");
22 #endif
23 }
24 
25 ShadowNode::Unshared ShadowTreeCloner::cloneWithNewProps(
26     const ShadowNode::Shared &oldRootNode,
27     const ShadowNodeFamily &family,
28     RawProps &&rawProps) {
29   // adapted from ShadowNode::cloneTree
30 
31   auto ancestors = family.getAncestors(*oldRootNode);
32 
33   if (ancestors.empty()) {
34     return ShadowNode::Unshared{nullptr};
35   }
36 
37   auto &parent = ancestors.back();
38   auto &oldShadowNode = parent.first.get().getChildren().at(parent.second);
39 
40   const auto newest = newestShadowNodesRegistry_->get(oldShadowNode->getTag());
41 
42   const auto &source = newest == nullptr ? oldShadowNode : newest;
43 
44   const auto props = source->getComponentDescriptor().cloneProps(
45       propsParserContext_, source->getProps(), rawProps);
46 
47   auto newChildNode = source->clone({/* .props = */ props});
48 
49   for (auto it = ancestors.rbegin(); it != ancestors.rend(); ++it) {
50     auto &parentNode = it->first.get();
51     auto childIndex = it->second;
52 
53     auto children = parentNode.getChildren();
54     const auto &oldChildNode = *children.at(childIndex);
55     react_native_assert(ShadowNode::sameFamily(oldChildNode, *newChildNode));
56 
57     newestShadowNodesRegistry_->set(newChildNode, parentNode.getTag());
58 
59     if (!parentNode.getSealed()) {
60       // Optimization: if a ShadowNode is unsealed, we can directly update its
61       // children instead of cloning the whole path to the root node.
62       auto &parentNodeNonConst = const_cast<ShadowNode &>(parentNode);
63       parentNodeNonConst.replaceChild(oldChildNode, newChildNode, childIndex);
64       yogaChildrenUpdates_.insert(&parentNodeNonConst);
65       return std::const_pointer_cast<ShadowNode>(oldRootNode);
66     }
67 
68     children[childIndex] = newChildNode;
69 
70     newChildNode = parentNode.clone({
71         ShadowNodeFragment::propsPlaceholder(),
72         std::make_shared<ShadowNode::ListOfShared>(children),
73     });
74   }
75 
76   return std::const_pointer_cast<ShadowNode>(newChildNode);
77 }
78 
79 void ShadowTreeCloner::updateYogaChildren() {
80   // Unfortunately, `replaceChild` does not update Yoga nodes, so we need to
81   // update them manually here.
82   for (ShadowNode *shadowNode : yogaChildrenUpdates_) {
83     static_cast<YogaLayoutableShadowNode *>(shadowNode)->updateYogaChildren();
84   }
85 #ifdef DEBUG
86   yogaChildrenUpdates_.clear();
87 #endif
88 }
89 
90 } // namespace reanimated
91 
92 #endif // RCT_NEW_ARCH_ENABLED
93