diff --git a/ReactCommon/react/renderer/mounting/Differentiator.cpp b/ReactCommon/react/renderer/mounting/Differentiator.cpp index 87001aafbc..c969522ccf 100644 --- a/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -178,22 +178,23 @@ class TinyMap final { * Sorting comparator for `reorderInPlaceIfNeeded`. */ static bool shouldFirstPairComesBeforeSecondOne( - ShadowViewNodePair const &lhs, - ShadowViewNodePair const &rhs) noexcept { - return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex(); + ShadowViewNodePair const *lhs, + ShadowViewNodePair const *rhs) noexcept { + return lhs->shadowNode->getOrderIndex() < rhs->shadowNode->getOrderIndex(); } /* * Reorders pairs in-place based on `orderIndex` using a stable sort algorithm. */ -static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { +static void reorderInPlaceIfNeeded( + ShadowViewNodePair::NonOwningList &pairs) noexcept { if (pairs.size() < 2) { return; } auto isReorderNeeded = false; for (auto const &pair : pairs) { - if (pair.shadowNode->getOrderIndex() != 0) { + if (pair->shadowNode->getOrderIndex() != 0) { isReorderNeeded = true; break; } @@ -212,7 +213,8 @@ static inline bool shadowNodeIsConcrete(ShadowNode const &shadowNode) { } static void sliceChildShadowNodeViewPairsRecursivelyV2( - ShadowViewNodePair::List &pairList, + ShadowViewNodePair::NonOwningList &pairList, + ViewNodePairScope &scope, Point layoutOffset, ShadowNode const &shadowNode) { for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { @@ -239,21 +241,22 @@ static void sliceChildShadowNodeViewPairsRecursivelyV2( bool isConcreteView = shadowNodeIsConcrete(childShadowNode); bool areChildrenFlattened = !childShadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext); - pairList.push_back( + scope.push_back( {shadowView, &childShadowNode, areChildrenFlattened, isConcreteView}); + pairList.push_back(&scope.back()); - if (!childShadowNode.getTraits().check( - ShadowNodeTraits::Trait::FormsStackingContext)) { + if (areChildrenFlattened) { sliceChildShadowNodeViewPairsRecursivelyV2( - pairList, origin, childShadowNode); + pairList, scope, origin, childShadowNode); } } } -ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( +ShadowViewNodePair::NonOwningList sliceChildShadowNodeViewPairsV2( ShadowNode const &shadowNode, + ViewNodePairScope &scope, bool allowFlattened) { - auto pairList = ShadowViewNodePair::List{}; + auto pairList = ShadowViewNodePair::NonOwningList{}; if (!shadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext) && @@ -262,23 +265,38 @@ ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( return pairList; } - sliceChildShadowNodeViewPairsRecursivelyV2(pairList, {0, 0}, shadowNode); + sliceChildShadowNodeViewPairsRecursivelyV2( + pairList, scope, {0, 0}, shadowNode); // Sorting pairs based on `orderIndex` if needed. reorderInPlaceIfNeeded(pairList); // Set list and mountIndex for each after reordering size_t mountIndex = 0; - for (auto &child : pairList) { - child.mountIndex = (child.isConcreteView ? mountIndex++ : -1); + for (auto child : pairList) { + child->mountIndex = (child->isConcreteView ? mountIndex++ : -1); } return pairList; } +/** + * Prefer calling this over `sliceChildShadowNodeViewPairsV2` directly, when + * possible. This can account for adding parent LayoutMetrics that are + * important to take into account, but tricky, in (un)flattening cases. + */ +static ShadowViewNodePair::NonOwningList +sliceChildShadowNodeViewPairsFromViewNodePair( + ShadowViewNodePair const &shadowViewNodePair, + ViewNodePairScope &scope, + bool allowFlattened = false) { + return sliceChildShadowNodeViewPairsV2( + *shadowViewNodePair.shadowNode, scope, allowFlattened); +} + /* - * Before we start to diff, let's make sure all our core data structures are in - * good shape to deliver the best performance. + * Before we start to diff, let's make sure all our core data structures are + * in good shape to deliver the best performance. */ static_assert( std::is_move_constructible::value, @@ -290,8 +308,8 @@ static_assert( std::is_move_constructible::value, "`ShadowViewNodePair` must be `move constructible`."); static_assert( - std::is_move_constructible::value, - "`ShadowViewNodePair::List` must be `move constructible`."); + std::is_move_constructible::value, + "`ShadowViewNodePair::NonOwningList` must be `move constructible`."); static_assert( std::is_move_assignable::value, @@ -303,16 +321,17 @@ static_assert( std::is_move_assignable::value, "`ShadowViewNodePair` must be `move assignable`."); static_assert( - std::is_move_assignable::value, - "`ShadowViewNodePair::List` must be `move assignable`."); + std::is_move_assignable::value, + "`ShadowViewNodePair::NonOwningList` must be `move assignable`."); // Forward declaration static void calculateShadowViewMutationsV2( BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs); + ShadowViewNodePair::NonOwningList &&oldChildPairs, + ShadowViewNodePair::NonOwningList &&newChildPairs); struct OrderedMutationInstructionContainer { ShadowViewMutation::List &createMutations; @@ -326,6 +345,7 @@ struct OrderedMutationInstructionContainer { static void calculateShadowViewMutationsFlattener( BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ReparentMode reparentMode, OrderedMutationInstructionContainer &mutationInstructionContainer, ShadowView const &parentShadowView, @@ -339,11 +359,13 @@ static void calculateShadowViewMutationsFlattener( * Here we flatten or unflatten a subtree, given an unflattened node in either * the old or new tree, and a list of flattened nodes in the other tree. * - * For example: if you are Flattening, the node will be in the old tree and the + * For example: if you are Flattening, the node will be in the old tree and + the * list will be from the new tree. If you are Unflattening, the opposite is true. - * It is currently not possible for ReactJS, and therefore React Native, to move + * It is currently not possible for ReactJS, and therefore React Native, to + move * a node *from* one parent to another without an entirely new subtree being * created. When we "reparent" in React Native here it is only because intermediate @@ -354,7 +376,8 @@ static void calculateShadowViewMutationsFlattener( * expanding vertically in that way. * Sketch of algorithm: - * 0. Create a map of nodes in the flattened list. This should be done *before* + * 0. Create a map of nodes in the flattened list. This should be done + *before* * calling this function. * 1. Traverse the Node Subtree; remove elements from the map as they are * visited in the tree. @@ -365,12 +388,14 @@ static void calculateShadowViewMutationsFlattener( * View if we're flattening. * If a node is in the list but not the map, it means it's been visited and * Update has already been - * performed in the subtree. If it *is* in the map, it means the node is not + * performed in the subtree. If it *is* in the map, it means the node is + not * * in the Tree, and should be Deleted/Created * **after this function is called**, by the caller. */ static void calculateShadowViewMutationsFlattener( BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ReparentMode reparentMode, OrderedMutationInstructionContainer &mutationInstructionContainer, ShadowView const &parentShadowView, @@ -386,8 +411,8 @@ static void calculateShadowViewMutationsFlattener( }); // Step 1: iterate through entire tree - ShadowViewNodePair::List treeChildren = - sliceChildShadowNodeViewPairsV2(*node.shadowNode); + ShadowViewNodePair::NonOwningList treeChildren = + sliceChildShadowNodeViewPairsFromViewNodePair(node, scope); DEBUG_LOGS({ LOG(ERROR) << "Differ Flattener 1.4: " @@ -397,9 +422,9 @@ static void calculateShadowViewMutationsFlattener( LOG(ERROR) << "Differ Flattener Entry: Child Pairs: "; std::string strTreeChildPairs; for (size_t k = 0; k < treeChildren.size(); k++) { - strTreeChildPairs.append(std::to_string(treeChildren[k].shadowView.tag)); - strTreeChildPairs.append(treeChildren[k].isConcreteView ? "" : "'"); - strTreeChildPairs.append(treeChildren[k].flattened ? "*" : ""); + strTreeChildPairs.append(std::to_string(treeChildren[k]->shadowView.tag)); + strTreeChildPairs.append(treeChildren[k]->isConcreteView ? "" : "'"); + strTreeChildPairs.append(treeChildren[k]->flattened ? "*" : ""); strTreeChildPairs.append(", "); } std::string strListChildPairs; @@ -416,7 +441,8 @@ static void calculateShadowViewMutationsFlattener( << strListChildPairs; }); - // Views in other tree that are visited by sub-flattening or sub-unflattening + // Views in other tree that are visited by sub-flattening or + // sub-unflattening TinyMap subVisitedOtherNewNodes{}; TinyMap subVisitedOtherOldNodes{}; auto subVisitedNewMap = @@ -433,7 +459,7 @@ static void calculateShadowViewMutationsFlattener( for (size_t index = 0; index < treeChildren.size() && index < treeChildren.size(); index++) { - auto &treeChildPair = treeChildren[index]; + auto &treeChildPair = *treeChildren[index]; // Try to find node in other tree auto unvisitedIt = unvisitedOtherNodes.find(treeChildPair.shadowView.tag); @@ -442,7 +468,7 @@ static void calculateShadowViewMutationsFlattener( ? subVisitedNewMap->find(treeChildPair.shadowView.tag) : subVisitedNewMap->end()); auto subVisitedOtherOldIt = - (unvisitedIt == unvisitedOtherNodes.end() + (unvisitedIt == unvisitedOtherNodes.end() && subVisitedNewMap->end() ? subVisitedOldMap->find(treeChildPair.shadowView.tag) : subVisitedOldMap->end()); @@ -459,8 +485,52 @@ static void calculateShadowViewMutationsFlattener( : subVisitedOtherOldIt->second)) : nullptr); - // Remove all children (non-recursively) of tree being flattened, or insert - // children into parent tree if they're being unflattened. + react_native_assert( + !existsInOtherTree || + (unvisitedIt != unvisitedOtherNodes.end() || + subVisitedOtherNewIt != subVisitedNewMap->end() || + subVisitedOtherOldIt != subVisitedOldMap->end())); + react_native_assert( + unvisitedIt == unvisitedOtherNodes.end() || + unvisitedIt->second->shadowView.tag == treeChildPair.shadowView.tag); + react_native_assert( + subVisitedOtherNewIt == subVisitedNewMap->end() || + subVisitedOtherNewIt->second->shadowView.tag == + treeChildPair.shadowView.tag); + react_native_assert( + subVisitedOtherOldIt == subVisitedOldMap->end() || + subVisitedOtherOldIt->second->shadowView.tag == + treeChildPair.shadowView.tag); + + bool alreadyUpdated = false; + + // Find in other tree and updated `otherTreePair` pointers + if (existsInOtherTree) { + react_native_assert(otherTreeNodePairPtr != nullptr); + auto newTreeNodePair = + (reparentMode == ReparentMode::Flatten ? otherTreeNodePairPtr + : &treeChildPair); + auto oldTreeNodePair = + (reparentMode == ReparentMode::Flatten ? &treeChildPair + : otherTreeNodePairPtr); + + react_native_assert(newTreeNodePair->shadowView.tag != 0); + react_native_assert(oldTreeNodePair->shadowView.tag != 0); + react_native_assert( + oldTreeNodePair->shadowView.tag == newTreeNodePair->shadowView.tag); + + alreadyUpdated = + newTreeNodePair->inOtherTree() || oldTreeNodePair->inOtherTree(); + + // We want to update these values unconditionally. Always do this + // before hitting any "continue" statements. + newTreeNodePair->otherTreePair = oldTreeNodePair; + oldTreeNodePair->otherTreePair = newTreeNodePair; + react_native_assert(treeChildPair.otherTreePair != nullptr); + } + + // Remove all children (non-recursively) of tree being flattened, or + // insert children into parent tree if they're being unflattened. // Caller will take care of the corresponding action in the other tree // (caller will handle DELETE case if we REMOVE here; caller will handle // CREATE case if we INSERT here). @@ -471,11 +541,15 @@ static void calculateShadowViewMutationsFlattener( // and will be executed before the REMOVE. Thus, we must actually // perform a REMOVE (new view) FROM (old index) in this case so that // we don't hit asserts in StubViewTree's REMOVE path. - if (otherTreeNodePairPtr != nullptr) { + // We also only do this if the "other" (newer) view is concrete. If + // it's not concrete, there will be no UPDATE mutation. + react_native_assert(existsInOtherTree == treeChildPair.inOtherTree()); + if (treeChildPair.inOtherTree() && + treeChildPair.otherTreePair->isConcreteView) { mutationInstructionContainer.removeMutations.push_back( ShadowViewMutation::RemoveMutation( node.shadowView, - (*otherTreeNodePairPtr).shadowView, + treeChildPair.otherTreePair->shadowView, static_cast(treeChildPair.mountIndex))); } else { mutationInstructionContainer.removeMutations.push_back( @@ -497,6 +571,26 @@ static void calculateShadowViewMutationsFlattener( // Find in other tree if (existsInOtherTree) { + react_native_assert(otherTreeNodePairPtr != nullptr); + auto &otherTreeNodePair = *otherTreeNodePairPtr; + + auto &newTreeNodePair = + (reparentMode == ReparentMode::Flatten ? otherTreeNodePair + : treeChildPair); + auto &oldTreeNodePair = + (reparentMode == ReparentMode::Flatten ? treeChildPair + : otherTreeNodePair); + + react_native_assert(newTreeNodePair.shadowView.tag != 0); + react_native_assert(oldTreeNodePair.shadowView.tag != 0); + react_native_assert( + oldTreeNodePair.shadowView.tag == newTreeNodePair.shadowView.tag); + + // If we've already done updates, don't repeat it. + if (alreadyUpdated) { + continue; + } + // If we've already done updates on this node, don't repeat. if (reparentMode == ReparentMode::Flatten && unvisitedIt == unvisitedOtherNodes.end() && @@ -509,46 +603,32 @@ static void calculateShadowViewMutationsFlattener( continue; } - react_native_assert(otherTreeNodePairPtr != nullptr); - auto &otherTreeNodePair = *otherTreeNodePairPtr; - - // If we've already done updates, don't repeat it. - if (treeChildPair.inOtherTree || otherTreeNodePair.inOtherTree) { - continue; - } - - auto &newTreeNodePair = - (reparentMode == ReparentMode::Flatten ? otherTreeNodePair - : treeChildPair); - auto &oldTreeNodePair = - (reparentMode == ReparentMode::Flatten ? treeChildPair - : otherTreeNodePair); - if (newTreeNodePair.shadowView != oldTreeNodePair.shadowView && newTreeNodePair.isConcreteView && oldTreeNodePair.isConcreteView) { mutationInstructionContainer.updateMutations.push_back( ShadowViewMutation::UpdateMutation( oldTreeNodePair.shadowView, newTreeNodePair.shadowView)); - - newTreeNodePair.otherTreeShadowNode = oldTreeNodePair.shadowNode; - oldTreeNodePair.otherTreeShadowNode = newTreeNodePair.shadowNode; } // Update children if appropriate. if (!oldTreeNodePair.flattened && !newTreeNodePair.flattened) { if (oldTreeNodePair.shadowNode != newTreeNodePair.shadowNode) { + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "(Un)Flattener trivial update of " + std::to_string(newTreeNodePair.shadowView.tag)), + innerScope, mutationInstructionContainer.downwardMutations, newTreeNodePair.shadowView, - sliceChildShadowNodeViewPairsV2(*oldTreeNodePair.shadowNode), - sliceChildShadowNodeViewPairsV2(*newTreeNodePair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + oldTreeNodePair, innerScope), + sliceChildShadowNodeViewPairsFromViewNodePair( + newTreeNodePair, innerScope)); } } else if (oldTreeNodePair.flattened != newTreeNodePair.flattened) { - // We need to handle one of the children being flattened or unflattened, - // in the context of a parent flattening or unflattening. + // We need to handle one of the children being flattened or + // unflattened, in the context of a parent flattening or unflattening. ReparentMode childReparentMode = (oldTreeNodePair.flattened ? ReparentMode::Unflatten : ReparentMode::Flatten); @@ -568,6 +648,7 @@ static void calculateShadowViewMutationsFlattener( ? parentShadowView.tag : newTreeNodePair.shadowView.tag) + " old:" + std::to_string(treeChildPair.shadowView.tag)), + scope, childReparentMode, mutationInstructionContainer, (reparentMode == ReparentMode::Flatten @@ -582,17 +663,18 @@ static void calculateShadowViewMutationsFlattener( if (childReparentMode == ReparentMode::Flatten) { // Construct unvisited nodes map auto unvisitedNewChildPairs = TinyMap{}; - // Memory note: these oldFlattenedNodes all disappear at the end of - // this "else" block, including any annotations we put on them. - auto newFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *newTreeNodePair.shadowNode, true); + // Memory note: these oldFlattenedNodes all disappear at the end + // of this "else" block, including any annotations we put on them. + auto newFlattenedNodes = + sliceChildShadowNodeViewPairsFromViewNodePair( + newTreeNodePair, scope, true); for (size_t i = 0; i < newFlattenedNodes.size(); i++) { - auto &newChild = newFlattenedNodes[i]; + auto &newChild = *newFlattenedNodes[i]; auto unvisitedOtherNodesIt = unvisitedOtherNodes.find(newChild.shadowView.tag); if (unvisitedOtherNodesIt != unvisitedOtherNodes.end()) { - auto &unvisitedItPair = *unvisitedOtherNodesIt->second; + auto unvisitedItPair = *unvisitedOtherNodesIt->second; unvisitedNewChildPairs.insert( {unvisitedItPair.shadowView.tag, &unvisitedItPair}); } else { @@ -612,6 +694,7 @@ static void calculateShadowViewMutationsFlattener( ? parentShadowView.tag : newTreeNodePair.shadowView.tag) + " old:" + std::to_string(oldTreeNodePair.shadowView.tag)), + scope, ReparentMode::Flatten, mutationInstructionContainer, (reparentMode == ReparentMode::Flatten @@ -622,15 +705,15 @@ static void calculateShadowViewMutationsFlattener( subVisitedNewMap, subVisitedOldMap); - for (auto &newFlattenedNode : newFlattenedNodes) { + for (auto newFlattenedNode : newFlattenedNodes) { auto unvisitedOldChildPairIt = - unvisitedNewChildPairs.find(newFlattenedNode.shadowView.tag); + unvisitedNewChildPairs.find(newFlattenedNode->shadowView.tag); if (unvisitedOldChildPairIt == unvisitedNewChildPairs.end()) { // Node was visited. auto deleteCreateIt = deletionCreationCandidatePairs.find( - newFlattenedNode.shadowView.tag); + newFlattenedNode->shadowView.tag); if (deleteCreateIt != deletionCreationCandidatePairs.end()) { deletionCreationCandidatePairs.erase(deleteCreateIt); } @@ -641,12 +724,13 @@ static void calculateShadowViewMutationsFlattener( else { // Construct unvisited nodes map auto unvisitedOldChildPairs = TinyMap{}; - // Memory note: these oldFlattenedNodes all disappear at the end of - // this "else" block, including any annotations we put on them. - auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *oldTreeNodePair.shadowNode, true); + // Memory note: these oldFlattenedNodes all disappear at the end + // of this "else" block, including any annotations we put on them. + auto oldFlattenedNodes = + sliceChildShadowNodeViewPairsFromViewNodePair( + oldTreeNodePair, scope, true); for (size_t i = 0; i < oldFlattenedNodes.size(); i++) { - auto &oldChild = oldFlattenedNodes[i]; + auto &oldChild = *oldFlattenedNodes[i]; auto unvisitedOtherNodesIt = unvisitedOtherNodes.find(oldChild.shadowView.tag); @@ -669,6 +753,7 @@ static void calculateShadowViewMutationsFlattener( ? parentShadowView.tag : newTreeNodePair.shadowView.tag) + " new:" + std::to_string(newTreeNodePair.shadowView.tag)), + scope, ReparentMode::Unflatten, mutationInstructionContainer, (reparentMode == ReparentMode::Flatten @@ -682,49 +767,51 @@ static void calculateShadowViewMutationsFlattener( // If old nodes were not visited, we know that we can delete them // now. They will be removed from the hierarchy by the outermost // loop of this function. - for (auto &oldFlattenedNode : oldFlattenedNodes) { + for (auto oldFlattenedNode : oldFlattenedNodes) { auto unvisitedOldChildPairIt = - unvisitedOldChildPairs.find(oldFlattenedNode.shadowView.tag); + unvisitedOldChildPairs.find(oldFlattenedNode->shadowView.tag); if (unvisitedOldChildPairIt != unvisitedOldChildPairs.end()) { // Node unvisited - mark the entire subtree for deletion - if (oldFlattenedNode.isConcreteView) { - auto tag = oldFlattenedNode.shadowView.tag; + if (oldFlattenedNode->isConcreteView) { + Tag tag = oldFlattenedNode->shadowView.tag; auto oldRemainingChildInListIt = std::find_if( treeChildren.begin(), treeChildren.end(), - [&tag](ShadowViewNodePair &nodePair) { - return nodePair.shadowView.tag == tag; + [&tag](ShadowViewNodePair *nodePair) { + return nodePair->shadowView.tag == tag; }); if (oldRemainingChildInListIt != treeChildren.end()) { auto deleteCreateIt = deletionCreationCandidatePairs.find( - oldFlattenedNode.shadowView.tag); + oldFlattenedNode->shadowView.tag); if (deleteCreateIt == deletionCreationCandidatePairs.end()) { deletionCreationCandidatePairs.insert( - {tag, &*oldRemainingChildInListIt}); + {tag, *oldRemainingChildInListIt}); } } else { // TODO: we might want to remove this block. It seems - // impossible to hit this logically (and empirically, after - // testing on lots of randomized and pathologically + // impossible to hit this logically (and empirically, + // after testing on lots of randomized and pathologically // constructed trees) but I'm leaving this here out of an // abundance of caution. - // In theory, this path should never be hit. If we don't see - // this in dev after a few months, let's delete this path. + // In theory, this path should never be hit. If we don't + // see this in dev after a few months, let's delete this + // path. react_native_assert(false); mutationInstructionContainer.deleteMutations.push_back( ShadowViewMutation::DeleteMutation( - oldFlattenedNode.shadowView)); + oldFlattenedNode->shadowView)); calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Destroy " + - std::to_string(oldFlattenedNode.shadowView.tag)), + std::to_string(oldFlattenedNode->shadowView.tag)), + scope, mutationInstructionContainer .destructiveDownwardMutations, - oldFlattenedNode.shadowView, - sliceChildShadowNodeViewPairsV2( - *oldFlattenedNode.shadowNode), + oldFlattenedNode->shadowView, + sliceChildShadowNodeViewPairsFromViewNodePair( + *oldFlattenedNode, scope), {}); } } @@ -732,14 +819,14 @@ static void calculateShadowViewMutationsFlattener( // Node was visited - make sure to remove it from // "newRemainingPairs" map auto newRemainingIt = - unvisitedOtherNodes.find(oldFlattenedNode.shadowView.tag); + unvisitedOtherNodes.find(oldFlattenedNode->shadowView.tag); if (newRemainingIt != unvisitedOtherNodes.end()) { unvisitedOtherNodes.erase(newRemainingIt); } // We also remove it from delete/creation candidates auto deleteCreateIt = deletionCreationCandidatePairs.find( - oldFlattenedNode.shadowView.tag); + oldFlattenedNode->shadowView.tag); if (deleteCreateIt != deletionCreationCandidatePairs.end()) { deletionCreationCandidatePairs.erase(deleteCreateIt); } @@ -753,35 +840,27 @@ static void calculateShadowViewMutationsFlattener( // concrete view. Removing the node from the unvisited list prevents the // caller from taking further action on this node, so make sure to // delete/create if the Concreteness of the node has changed. - if (newTreeNodePair.isConcreteView != oldTreeNodePair.isConcreteView && - !newTreeNodePair.inOtherTree) { + if (newTreeNodePair.isConcreteView != oldTreeNodePair.isConcreteView) { if (newTreeNodePair.isConcreteView) { mutationInstructionContainer.createMutations.push_back( ShadowViewMutation::CreateMutation(newTreeNodePair.shadowView)); } else { mutationInstructionContainer.deleteMutations.push_back( - ShadowViewMutation::DeleteMutation(newTreeNodePair.shadowView)); + ShadowViewMutation::DeleteMutation(oldTreeNodePair.shadowView)); } } - newTreeNodePair.inOtherTree = true; - oldTreeNodePair.inOtherTree = true; - - if (parentSubVisitedOtherNewNodes != nullptr) { - parentSubVisitedOtherNewNodes->insert( - {newTreeNodePair.shadowView.tag, &newTreeNodePair}); - } - if (parentSubVisitedOtherOldNodes != nullptr) { - parentSubVisitedOtherOldNodes->insert( - {oldTreeNodePair.shadowView.tag, &oldTreeNodePair}); - } + subVisitedNewMap->insert( + {newTreeNodePair.shadowView.tag, &newTreeNodePair}); + subVisitedOldMap->insert( + {oldTreeNodePair.shadowView.tag, &oldTreeNodePair}); if (unvisitedIt != unvisitedOtherNodes.end()) { unvisitedOtherNodes.erase(unvisitedIt); } } else { // Node does not in exist in other tree. - if (treeChildPair.isConcreteView && !treeChildPair.inOtherTree) { + if (treeChildPair.isConcreteView && !treeChildPair.inOtherTree()) { auto deletionCreationIt = deletionCreationCandidatePairs.find(treeChildPair.shadowView.tag); if (deletionCreationIt == deletionCreationCandidatePairs.end()) { @@ -793,8 +872,8 @@ static void calculateShadowViewMutationsFlattener( } // Final step: go through creation/deletion candidates and delete/create - // subtrees if they were never visited during the execution of the above loop - // and recursions. + // subtrees if they were never visited during the execution of the above + // loop and recursions. for (auto it = deletionCreationCandidatePairs.begin(); it != deletionCreationCandidatePairs.end(); it++) { @@ -804,7 +883,7 @@ static void calculateShadowViewMutationsFlattener( auto &treeChildPair = *it->second; // If node was visited during a flattening/unflattening recursion. - if (treeChildPair.inOtherTree) { + if (treeChildPair.inOtherTree()) { continue; } @@ -813,13 +892,16 @@ static void calculateShadowViewMutationsFlattener( ShadowViewMutation::DeleteMutation(treeChildPair.shadowView)); if (!treeChildPair.flattened) { + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Recursively delete tree child pair (flatten case): " + std::to_string(treeChildPair.shadowView.tag)), + innerScope, mutationInstructionContainer.destructiveDownwardMutations, treeChildPair.shadowView, - sliceChildShadowNodeViewPairsV2(*treeChildPair.shadowNode), + sliceChildShadowNodeViewPairsFromViewNodePair( + treeChildPair, innerScope), {}); } } else { @@ -827,14 +909,17 @@ static void calculateShadowViewMutationsFlattener( ShadowViewMutation::CreateMutation(treeChildPair.shadowView)); if (!treeChildPair.flattened) { + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Recursively delete tree child pair (unflatten case): " + std::to_string(treeChildPair.shadowView.tag)), + innerScope, mutationInstructionContainer.downwardMutations, treeChildPair.shadowView, {}, - sliceChildShadowNodeViewPairsV2(*treeChildPair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + treeChildPair, innerScope)); } } } @@ -842,10 +927,11 @@ static void calculateShadowViewMutationsFlattener( static void calculateShadowViewMutationsV2( BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs) { + ShadowViewNodePair::NonOwningList &&oldChildPairs, + ShadowViewNodePair::NonOwningList &&newChildPairs) { if (oldChildPairs.empty() && newChildPairs.empty()) { return; } @@ -875,19 +961,19 @@ static void calculateShadowViewMutationsV2( std::string strOldChildPairs; for (size_t oldIndex = 0; oldIndex < oldChildPairs.size(); oldIndex++) { strOldChildPairs.append( - std::to_string(oldChildPairs[oldIndex].shadowView.tag)); + std::to_string(oldChildPairs[oldIndex]->shadowView.tag)); strOldChildPairs.append( - oldChildPairs[oldIndex].isConcreteView ? "" : "'"); - strOldChildPairs.append(oldChildPairs[oldIndex].flattened ? "*" : ""); + oldChildPairs[oldIndex]->isConcreteView ? "" : "'"); + strOldChildPairs.append(oldChildPairs[oldIndex]->flattened ? "*" : ""); strOldChildPairs.append(", "); } std::string strNewChildPairs; for (size_t newIndex = 0; newIndex < newChildPairs.size(); newIndex++) { strNewChildPairs.append( - std::to_string(newChildPairs[newIndex].shadowView.tag)); + std::to_string(newChildPairs[newIndex]->shadowView.tag)); strNewChildPairs.append( - newChildPairs[newIndex].isConcreteView ? "" : "'"); - strNewChildPairs.append(newChildPairs[newIndex].flattened ? "*" : ""); + newChildPairs[newIndex]->isConcreteView ? "" : "'"); + strNewChildPairs.append(newChildPairs[newIndex]->flattened ? "*" : ""); strNewChildPairs.append(", "); } LOG(ERROR) << "Differ Entry: Old Child Pairs: " << strOldChildPairs; @@ -897,8 +983,8 @@ static void calculateShadowViewMutationsV2( // Stage 1: Collecting `Update` mutations for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size(); index++) { - auto &oldChildPair = oldChildPairs[index]; - auto &newChildPair = newChildPairs[index]; + auto &oldChildPair = *oldChildPairs[index]; + auto &newChildPair = *newChildPairs[index]; if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) { DEBUG_LOGS({ @@ -912,8 +998,8 @@ static void calculateShadowViewMutationsV2( break; } - // If either view was flattened, and that has changed this frame, don't try - // to update + // If either view was flattened, and that has changed this frame, don't + // try to update if (oldChildPair.flattened != newChildPair.flattened || oldChildPair.isConcreteView != newChildPair.isConcreteView) { break; @@ -939,14 +1025,16 @@ static void calculateShadowViewMutationsV2( // Recursively update tree if ShadowNode pointers are not equal if (!oldChildPair.flattened && oldChildPair.shadowNode != newChildPair.shadowNode) { - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode); + ViewNodePairScope innerScope{}; + auto oldGrandChildPairs = sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope); + auto newGrandChildPairs = sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope); calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Stage 1: Recurse on " + std::to_string(oldChildPair.shadowView.tag)), + innerScope, *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), oldChildPair.shadowView, @@ -961,7 +1049,7 @@ static void calculateShadowViewMutationsV2( // We've reached the end of the new children. We can delete+remove the // rest. for (; index < oldChildPairs.size(); index++) { - auto const &oldChildPair = oldChildPairs[index]; + auto const &oldChildPair = *oldChildPairs[index]; DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 2: Deleting Tag/Tree: [" @@ -982,19 +1070,22 @@ static void calculateShadowViewMutationsV2( // We also have to call the algorithm recursively to clean up the entire // subtree starting from the removed view. + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Trivial delete " + std::to_string(oldChildPair.shadowView.tag)), + innerScope, destructiveDownwardMutations, oldChildPair.shadowView, - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode), + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope), {}); } } else if (index == oldChildPairs.size()) { // If we don't have any more existing children we can choose a fast path // since the rest will all be create+insert. for (; index < newChildPairs.size(); index++) { - auto const &newChildPair = newChildPairs[index]; + auto const &newChildPair = *newChildPairs[index]; DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 3: Creating Tag/Tree: [" @@ -1013,13 +1104,16 @@ static void calculateShadowViewMutationsV2( createMutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Trivial create " + std::to_string(newChildPair.shadowView.tag)), + innerScope, downwardMutations, newChildPair.shadowView, {}, - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope)); } } else { // Collect map of tags in the new list @@ -1027,7 +1121,7 @@ static void calculateShadowViewMutationsV2( auto newInsertedPairs = TinyMap{}; auto deletionCandidatePairs = TinyMap{}; for (; index < newChildPairs.size(); index++) { - auto &newChildPair = newChildPairs[index]; + auto &newChildPair = *newChildPairs[index]; newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair}); } @@ -1043,8 +1137,8 @@ static void calculateShadowViewMutationsV2( // Advance both pointers if pointing to the same element if (haveNewPair && haveOldPair) { - auto const &oldChildPair = oldChildPairs[oldIndex]; - auto const &newChildPair = newChildPairs[newIndex]; + auto const &oldChildPair = *oldChildPairs[oldIndex]; + auto const &newChildPair = *newChildPairs[newIndex]; Tag newTag = newChildPair.shadowView.tag; Tag oldTag = oldChildPair.shadowView.tag; @@ -1083,7 +1177,8 @@ static void calculateShadowViewMutationsV2( } else if ( oldChildPair.isConcreteView && newChildPair.isConcreteView) { // Even if node's children are flattened, it might still be a - // concrete view. The case where they're different is handled above. + // concrete view. The case where they're different is handled + // above. if (oldChildPair.shadowView != newChildPair.shadowView) { updateMutations.push_back(ShadowViewMutation::UpdateMutation( oldChildPair.shadowView, newChildPair.shadowView)); @@ -1096,8 +1191,8 @@ static void calculateShadowViewMutationsV2( } } - // Are we flattening or unflattening either one? If node was flattened - // in both trees, there's no change, just continue. + // Are we flattening or unflattening either one? If node was + // flattened in both trees, there's no change, just continue. if (oldChildPair.flattened && newChildPair.flattened) { newIndex++; oldIndex++; @@ -1118,12 +1213,14 @@ static void calculateShadowViewMutationsV2( if (!oldChildPair.flattened) { // Flatten old tree into new list // At the end of this loop we still want to know which of these - // children are visited, so we reuse the `newRemainingPairs` map. + // children are visited, so we reuse the `newRemainingPairs` + // map. calculateShadowViewMutationsFlattener( DIFF_BREADCRUMB( "Flatten tree " + std::to_string(parentShadowView.tag) + " into list " + std::to_string(oldChildPair.shadowView.tag)), + scope, ReparentMode::Flatten, mutationInstructionContainer, parentShadowView, @@ -1135,19 +1232,20 @@ static void calculateShadowViewMutationsV2( // Construct unvisited nodes map auto unvisitedOldChildPairs = TinyMap{}; - // We don't know where all the children of oldChildPair are within - // oldChildPairs, but we know that they're in the same relative - // order. The reason for this is because of flattening + zIndex: - // the children could be listed before the parent, interwoven with - // children from other nodes, etc. - auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *oldChildPair.shadowNode, true); + // We don't know where all the children of oldChildPair are + // within oldChildPairs, but we know that they're in the same + // relative order. The reason for this is because of flattening + // + zIndex: the children could be listed before the parent, + // interwoven with children from other nodes, etc. + auto oldFlattenedNodes = + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, scope, true); for (size_t i = 0, j = 0; i < oldChildPairs.size() && j < oldFlattenedNodes.size(); i++) { - auto &oldChild = oldChildPairs[i]; + auto &oldChild = *oldChildPairs[i]; if (oldChild.shadowView.tag == - oldFlattenedNodes[j].shadowView.tag) { + oldFlattenedNodes[j]->shadowView.tag) { unvisitedOldChildPairs.insert( {oldChild.shadowView.tag, &oldChild}); j++; @@ -1160,16 +1258,18 @@ static void calculateShadowViewMutationsV2( "Unflatten old list " + std::to_string(parentShadowView.tag) + " into new tree " + std::to_string(newChildPair.shadowView.tag)), + scope, ReparentMode::Unflatten, mutationInstructionContainer, parentShadowView, unvisitedOldChildPairs, newChildPair); - // If old nodes were not visited, we know that we can delete them - // now. They will be removed from the hierarchy by the outermost - // loop of this function. - for (auto &oldFlattenedNode : oldFlattenedNodes) { + // If old nodes were not visited, we know that we can delete + // them now. They will be removed from the hierarchy by the + // outermost loop of this function. + for (auto &oldFlattenedNodePtr : oldFlattenedNodes) { + auto &oldFlattenedNode = *oldFlattenedNodePtr; auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( oldFlattenedNode.shadowView.tag); if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { @@ -1189,17 +1289,21 @@ static void calculateShadowViewMutationsV2( continue; } - // Update subtrees if View is not flattened, and if node addresses are - // not equal + // Update subtrees if View is not flattened, and if node addresses + // are not equal if (oldChildPair.shadowNode != newChildPair.shadowNode) { + ViewNodePairScope innerScope{}; auto oldGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode); + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope); auto newGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode); + sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope); calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Non-trivial update " + std::to_string(oldChildPair.shadowView.tag)), + innerScope, *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), oldChildPair.shadowView, @@ -1216,7 +1320,7 @@ static void calculateShadowViewMutationsV2( // We have an old pair, but we either don't have any remaining new pairs // or we have one but it's not matched up with the old pair if (haveOldPair) { - auto const &oldChildPair = oldChildPairs[oldIndex]; + auto const &oldChildPair = *oldChildPairs[oldIndex]; Tag oldTag = oldChildPair.shadowView.tag; @@ -1250,10 +1354,12 @@ static void calculateShadowViewMutationsV2( if (!oldChildPair.flattened) { // Flatten old tree into new list // At the end of this loop we still want to know which of these - // children are visited, so we reuse the `newRemainingPairs` map. + // children are visited, so we reuse the `newRemainingPairs` + // map. calculateShadowViewMutationsFlattener( DIFF_BREADCRUMB( "Flatten2 " + std::to_string(parentShadowView.tag)), + scope, ReparentMode::Flatten, mutationInstructionContainer, parentShadowView, @@ -1265,19 +1371,20 @@ static void calculateShadowViewMutationsV2( // Construct unvisited nodes map auto unvisitedOldChildPairs = TinyMap{}; - // We don't know where all the children of oldChildPair are within - // oldChildPairs, but we know that they're in the same relative - // order. The reason for this is because of flattening + zIndex: - // the children could be listed before the parent, interwoven with - // children from other nodes, etc. - auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *oldChildPair.shadowNode, true); + // We don't know where all the children of oldChildPair are + // within oldChildPairs, but we know that they're in the same + // relative order. The reason for this is because of flattening + // + zIndex: the children could be listed before the parent, + // interwoven with children from other nodes, etc. + auto oldFlattenedNodes = + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, scope, true); for (size_t i = 0, j = 0; i < oldChildPairs.size() && j < oldFlattenedNodes.size(); i++) { - auto &oldChild = oldChildPairs[i]; + auto &oldChild = *oldChildPairs[i]; if (oldChild.shadowView.tag == - oldFlattenedNodes[j].shadowView.tag) { + oldFlattenedNodes[j]->shadowView.tag) { unvisitedOldChildPairs.insert( {oldChild.shadowView.tag, &oldChild}); j++; @@ -1288,17 +1395,19 @@ static void calculateShadowViewMutationsV2( calculateShadowViewMutationsFlattener( DIFF_BREADCRUMB( "Unflatten2 " + std::to_string(parentShadowView.tag)), + scope, ReparentMode::Unflatten, mutationInstructionContainer, parentShadowView, unvisitedOldChildPairs, newChildPair); - // If old nodes were not visited, we know that we can delete them - // now. They will be removed from the hierarchy by the outermost - // loop of this function. TODO: delete recursively? create - // recursively? - for (auto &oldFlattenedNode : oldFlattenedNodes) { + // If old nodes were not visited, we know that we can delete + // them now. They will be removed from the hierarchy by the + // outermost loop of this function. TODO: delete recursively? + // create recursively? + for (auto &oldFlattenedNodePtr : oldFlattenedNodes) { + auto &oldFlattenedNode = *oldFlattenedNodePtr; auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( oldFlattenedNode.shadowView.tag); if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { @@ -1316,8 +1425,8 @@ static void calculateShadowViewMutationsV2( // Check concrete-ness of views // Create/Delete and Insert/Remove if necessary - // TODO: document: Insert should already be handled by outermost loop, - // but not Remove + // TODO: document: Insert should already be handled by outermost + // loop, but not Remove if (oldChildPair.isConcreteView != newChildPair.isConcreteView) { if (newChildPair.isConcreteView) { createMutations.push_back( @@ -1332,15 +1441,18 @@ static void calculateShadowViewMutationsV2( } } - // old and new child pairs are both either flattened or unflattened at - // this point. If they're not views, we don't need to update subtrees. + // old and new child pairs are both either flattened or unflattened + // at this point. If they're not views, we don't need to update + // subtrees. if (oldChildPair.isConcreteView && newChildPair.isConcreteView) { - // TODO: do we always want to remove here? There are cases where we - // might be able to remove this to prevent unnecessary + // TODO: do we always want to remove here? There are cases where + // we might be able to remove this to prevent unnecessary // removes/inserts in cases of (un)flattening + reorders? + // If removing here, we must remove the newest version of the View + // - which will always be in the "new" tree. removeMutations.push_back(ShadowViewMutation::RemoveMutation( parentShadowView, - oldChildPair.shadowView, + newChildPair.shadowView, static_cast(oldChildPair.mountIndex))); if (oldChildPair.shadowView != newChildPair.shadowView) { @@ -1352,14 +1464,18 @@ static void calculateShadowViewMutationsV2( if (!oldChildPair.flattened && !newChildPair.flattened && oldChildPair.shadowNode != newChildPair.shadowNode) { // Update subtrees + ViewNodePairScope innerScope{}; auto oldGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode); + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope); auto newGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode); + sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope); calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Non-trivial update3 " + std::to_string(oldChildPair.shadowView.tag)), + innerScope, *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), oldChildPair.shadowView, @@ -1395,31 +1511,31 @@ static void calculateShadowViewMutationsV2( << (oldChildPair.isConcreteView ? " (concrete)" : "") << " with parent: [" << parentShadowView.tag << "] " << "node is in other tree? " - << (oldChildPair.inOtherTree ? "yes" : "no"); + << (oldChildPair.inOtherTree() ? "yes" : "no"); }); - // Edge case: node is not found in `newRemainingPairs`, due to complex - // (un)flattening cases, but exists in other tree *and* is concrete. - if (oldChildPair.inOtherTree && - oldChildPair.otherTreeShadowNode != nullptr) { - if (shadowNodeIsConcrete(*oldChildPair.otherTreeShadowNode)) { - ShadowView otherTreeView = - ShadowView(*oldChildPair.otherTreeShadowNode); - // Remove, but remove using the *new* node, since we know - // an UPDATE mutation from old -> new has been generated. - // Practically this shouldn't matter for most mounting layer - // implementations, but helps adhere to the invariant that - // for all mutation instructions, "oldViewShadowNode" == "current - // node on mounting layer / stubView". - // Here we do *not" need to generate a potential DELETE mutation - // because we know the view is concrete, and still in the new - // hierarchy. - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, - otherTreeView, - static_cast(oldChildPair.mountIndex))); - continue; - } + // Edge case: node is not found in `newRemainingPairs`, due to + // complex (un)flattening cases, but exists in other tree *and* is + // concrete. + if (oldChildPair.inOtherTree() && + oldChildPair.otherTreePair->isConcreteView) { + ShadowView const &otherTreeView = + oldChildPair.otherTreePair->shadowView; + + // Remove, but remove using the *new* node, since we know + // an UPDATE mutation from old -> new has been generated. + // Practically this shouldn't matter for most mounting layer + // implementations, but helps adhere to the invariant that + // for all mutation instructions, "oldViewShadowNode" == "current + // node on mounting layer / stubView". + // Here we do *not" need to generate a potential DELETE mutation + // because we know the view is concrete, and still in the new + // hierarchy. + removeMutations.push_back(ShadowViewMutation::RemoveMutation( + parentShadowView, + otherTreeView, + static_cast(oldChildPair.mountIndex))); + continue; } removeMutations.push_back(ShadowViewMutation::RemoveMutation( @@ -1437,7 +1553,7 @@ static void calculateShadowViewMutationsV2( // At this point, oldTag is -1 or is in the new list, and hasn't been // inserted or matched yet. We're not sure yet if the new node is in the // old list - generate an insert instruction for the new node. - auto &newChildPair = newChildPairs[newIndex]; + auto &newChildPair = *newChildPairs[newIndex]; DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 10: Inserting tag/tree that was not (yet?) removed from hierarchy: " @@ -1455,11 +1571,11 @@ static void calculateShadowViewMutationsV2( } // `inOtherTree` is only set to true during flattening/unflattening of - // parent. If the parent isn't (un)flattened, this will always be `false`, - // even if the node is in the other (old) tree. In this case, we expect - // the node to be removed from `newInsertedPairs` when we later encounter - // it in this loop. - if (!newChildPair.inOtherTree) { + // parent. If the parent isn't (un)flattened, this will always be + // `false`, even if the node is in the other (old) tree. In this case, + // we expect the node to be removed from `newInsertedPairs` when we + // later encounter it in this loop. + if (!newChildPair.inOtherTree()) { newInsertedPairs.insert({newChildPair.shadowView.tag, &newChildPair}); } @@ -1468,8 +1584,8 @@ static void calculateShadowViewMutationsV2( // Penultimate step: generate Delete instructions for entirely deleted // subtrees/nodes. We do this here because we need to traverse the entire - // list to make sure that a node was not reparented into an unflattened node - // that occurs *after* it in the hierarchy, due to zIndex ordering. + // list to make sure that a node was not reparented into an unflattened + // node that occurs *after* it in the hierarchy, due to zIndex ordering. for (auto it = deletionCandidatePairs.begin(); it != deletionCandidatePairs.end(); it++) { @@ -1485,30 +1601,34 @@ static void calculateShadowViewMutationsV2( << "[" << oldChildPair.shadowView.tag << "]" << (oldChildPair.flattened ? "(flattened)" : "") << (oldChildPair.isConcreteView ? "(concrete)" : "") - << (oldChildPair.inOtherTree ? "(in other tree)" : "") - << " with parent: [" << parentShadowView.tag << "]"; + << (oldChildPair.inOtherTree() ? "(in other tree)" : "") + << " with parent: [" << parentShadowView.tag << "] ##" + << std::hash{}(oldChildPair.shadowView); }); // This can happen when the parent is unflattened - if (!oldChildPair.inOtherTree) { + if (!oldChildPair.inOtherTree() && oldChildPair.isConcreteView) { deleteMutations.push_back( ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); // We also have to call the algorithm recursively to clean up the // entire subtree starting from the removed view. + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Non-trivial delete " + std::to_string(oldChildPair.shadowView.tag)), + innerScope, destructiveDownwardMutations, oldChildPair.shadowView, - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode), + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope), {}); } } - // Final step: generate Create instructions for entirely new subtrees/nodes - // that are not the result of flattening or unflattening. + // Final step: generate Create instructions for entirely new + // subtrees/nodes that are not the result of flattening or unflattening. for (auto it = newInsertedPairs.begin(); it != newInsertedPairs.end(); it++) { // Erased elements of a TinyMap will have a Tag/key of 0 - skip those @@ -1527,28 +1647,31 @@ static void calculateShadowViewMutationsV2( << "[" << newChildPair.shadowView.tag << "]" << (newChildPair.flattened ? "(flattened)" : "") << (newChildPair.isConcreteView ? "(concrete)" : "") - << (newChildPair.inOtherTree ? "(in other tree)" : "") + << (newChildPair.inOtherTree() ? "(in other tree)" : "") << " with parent: [" << parentShadowView.tag << "]"; }); if (!newChildPair.isConcreteView) { continue; } - if (newChildPair.inOtherTree) { + if (newChildPair.inOtherTree()) { continue; } createMutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( DIFF_BREADCRUMB( "Non-trivial create " + std::to_string(newChildPair.shadowView.tag)), + innerScope, downwardMutations, newChildPair.shadowView, {}, - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope)); } } @@ -1587,7 +1710,7 @@ static void calculateShadowViewMutationsV2( * Only used by unit tests currently. */ static void sliceChildShadowNodeViewPairsRecursivelyLegacy( - ShadowViewNodePair::List &pairList, + ShadowViewNodePair::OwningList &pairList, Point layoutOffset, ShadowNode const &shadowNode) { for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { @@ -1626,9 +1749,9 @@ static void sliceChildShadowNodeViewPairsRecursivelyLegacy( /** * Only used by unit tests currently. */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsLegacy( +ShadowViewNodePair::OwningList sliceChildShadowNodeViewPairsLegacy( ShadowNode const &shadowNode) { - auto pairList = ShadowViewNodePair::List{}; + auto pairList = ShadowViewNodePair::OwningList{}; if (!shadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext) && @@ -1656,6 +1779,10 @@ ShadowViewMutation::List calculateShadowViewMutations( react_native_assert( ShadowNode::sameFamily(oldRootShadowNode, newRootShadowNode)); + // See explanation of scope in Differentiator.h. + ViewNodePairScope viewNodePairScope{}; + ViewNodePairScope innerViewNodePairScope{}; + auto mutations = ShadowViewMutation::List{}; mutations.reserve(256); @@ -1669,10 +1796,11 @@ ShadowViewMutation::List calculateShadowViewMutations( calculateShadowViewMutationsV2( CREATE_DIFF_BREADCRUMB(oldRootShadowView.tag), + innerViewNodePairScope, mutations, ShadowView(oldRootShadowNode), - sliceChildShadowNodeViewPairsV2(oldRootShadowNode), - sliceChildShadowNodeViewPairsV2(newRootShadowNode)); + sliceChildShadowNodeViewPairsV2(oldRootShadowNode, viewNodePairScope), + sliceChildShadowNodeViewPairsV2(newRootShadowNode, viewNodePairScope)); return mutations; } diff --git a/ReactCommon/react/renderer/mounting/Differentiator.h b/ReactCommon/react/renderer/mounting/Differentiator.h index 120d719aa8..cded0ab41b 100644 --- a/ReactCommon/react/renderer/mounting/Differentiator.h +++ b/ReactCommon/react/renderer/mounting/Differentiator.h @@ -10,18 +10,42 @@ #include #include #include +#include namespace facebook { namespace react { enum class ReparentMode { Flatten, Unflatten }; +/** + * During differ, we need to keep some `ShadowViewNodePair`s in memory. + * Some `ShadowViewNodePair`s are referenced from std::vectors returned + * by `sliceChildShadowNodeViewPairsV2`; some are referenced in TinyMaps + * for view (un)flattening especially; and it is not always clear which + * std::vectors will outlive which TinyMaps, and vice-versa, so it doesn't + * make sense for the std::vector or TinyMap to own any `ShadowViewNodePair`s. + * + * Thus, we introduce the concept of a scope. + * + * For the duration of some operation, we keep a ViewNodePairScope around, such + * that: (1) the ViewNodePairScope keeps each + * ShadowViewNodePair alive, (2) we have a stable pointer value that we can + * use to reference each ShadowViewNodePair (not guaranteed with std::vector, + * for example, which may have to resize and move values around). + * + * As long as we only manipulate the data-structure with push_back, std::deque + * both (1) ensures that pointers into the data-structure are never invalidated, + * and (2) tries to efficiently allocate storage such that as many objects as + * possible are close in memory, but does not guarantee adjacency. + */ +using ViewNodePairScope = std::deque; + /* * Calculates a list of view mutations which describes how the old * `ShadowTree` can be transformed to the new one. * The list of mutations might be and might not be optimal. */ -ShadowViewMutationList calculateShadowViewMutations( +ShadowViewMutation::List calculateShadowViewMutations( ShadowNode const &oldRootShadowNode, ShadowNode const &newRootShadowNode, bool useNewDiffer); @@ -31,15 +55,16 @@ ShadowViewMutationList calculateShadowViewMutations( * flattened view hierarchy. The V2 version preserves nodes even if they do * not form views and their children are flattened. */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( +ShadowViewNodePair::NonOwningList sliceChildShadowNodeViewPairsV2( ShadowNode const &shadowNode, + ViewNodePairScope &viewNodePairScope, bool allowFlattened = false); /* * Generates a list of `ShadowViewNodePair`s that represents a layer of a * flattened view hierarchy. This is *only* used by unit tests currently. */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsLegacy( +ShadowViewNodePair::OwningList sliceChildShadowNodeViewPairsLegacy( ShadowNode const &shadowNode); } // namespace react diff --git a/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.cpp b/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.cpp index f3679faea8..d81b70cfd4 100644 --- a/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.cpp +++ b/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.cpp @@ -178,15 +178,16 @@ class TinyMap final { * Sorting comparator for `reorderInPlaceIfNeeded`. */ static bool shouldFirstPairComesBeforeSecondOne( - ShadowViewNodePair const &lhs, - ShadowViewNodePair const &rhs) noexcept { + ShadowViewNodePairLegacy const &lhs, + ShadowViewNodePairLegacy const &rhs) noexcept { return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex(); } /* * Reorders pairs in-place based on `orderIndex` using a stable sort algorithm. */ -static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { +static void reorderInPlaceIfNeeded( + ShadowViewNodePairLegacy::OwningList &pairs) noexcept { if (pairs.size() < 2) { return; } @@ -208,7 +209,7 @@ static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { } static void sliceChildShadowNodeViewPairsRecursivelyV2( - ShadowViewNodePair::List &pairList, + ShadowViewNodePairLegacy::OwningList &pairList, Point layoutOffset, ShadowNode const &shadowNode) { for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { @@ -247,10 +248,10 @@ static void sliceChildShadowNodeViewPairsRecursivelyV2( } } -ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( +ShadowViewNodePairLegacy::OwningList sliceChildShadowNodeViewPairsV2( ShadowNode const &shadowNode, bool allowFlattened) { - auto pairList = ShadowViewNodePair::List{}; + auto pairList = ShadowViewNodePairLegacy::OwningList{}; if (!shadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext) && @@ -284,11 +285,11 @@ static_assert( std::is_move_constructible::value, "`ShadowView` must be `move constructible`."); static_assert( - std::is_move_constructible::value, - "`ShadowViewNodePair` must be `move constructible`."); + std::is_move_constructible::value, + "`ShadowViewNodePairLegacy` must be `move constructible`."); static_assert( - std::is_move_constructible::value, - "`ShadowViewNodePair::List` must be `move constructible`."); + std::is_move_constructible::value, + "`ShadowViewNodePairLegacy::OwningList` must be `move constructible`."); static_assert( std::is_move_assignable::value, @@ -297,19 +298,19 @@ static_assert( std::is_move_assignable::value, "`ShadowView` must be `move assignable`."); static_assert( - std::is_move_assignable::value, - "`ShadowViewNodePair` must be `move assignable`."); + std::is_move_assignable::value, + "`ShadowViewNodePairLegacy` must be `move assignable`."); static_assert( - std::is_move_assignable::value, - "`ShadowViewNodePair::List` must be `move assignable`."); + std::is_move_assignable::value, + "`ShadowViewNodePairLegacy::OwningList` must be `move assignable`."); // Forward declaration static void calculateShadowViewMutationsV2( BREADCRUMB_TYPE breadcrumb, ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs); + ShadowViewNodePairLegacy::OwningList &&oldChildPairs, + ShadowViewNodePairLegacy::OwningList &&newChildPairs); struct OrderedMutationInstructionContainer { ShadowViewMutation::List &createMutations; @@ -326,10 +327,11 @@ static void calculateShadowViewMutationsFlattener( ReparentMode reparentMode, OrderedMutationInstructionContainer &mutationInstructionContainer, ShadowView const &parentShadowView, - TinyMap &unvisitedFlattenedNodes, - ShadowViewNodePair const &node, - TinyMap *parentSubVisitedOtherNewNodes = nullptr, - TinyMap *parentSubVisitedOtherOldNodes = + TinyMap &unvisitedFlattenedNodes, + ShadowViewNodePairLegacy const &node, + TinyMap *parentSubVisitedOtherNewNodes = + nullptr, + TinyMap *parentSubVisitedOtherOldNodes = nullptr); /** @@ -371,10 +373,10 @@ static void calculateShadowViewMutationsFlattener( ReparentMode reparentMode, OrderedMutationInstructionContainer &mutationInstructionContainer, ShadowView const &parentShadowView, - TinyMap &unvisitedOtherNodes, - ShadowViewNodePair const &node, - TinyMap *parentSubVisitedOtherNewNodes, - TinyMap *parentSubVisitedOtherOldNodes) { + TinyMap &unvisitedOtherNodes, + ShadowViewNodePairLegacy const &node, + TinyMap *parentSubVisitedOtherNewNodes, + TinyMap *parentSubVisitedOtherOldNodes) { DEBUG_LOGS({ LOG(ERROR) << "Differ Flattener 1: " << (reparentMode == ReparentMode::Unflatten ? "Unflattening" @@ -383,7 +385,7 @@ static void calculateShadowViewMutationsFlattener( }); // Step 1: iterate through entire tree - ShadowViewNodePair::List treeChildren = + ShadowViewNodePairLegacy::OwningList treeChildren = sliceChildShadowNodeViewPairsV2(*node.shadowNode); DEBUG_LOGS({ @@ -414,8 +416,8 @@ static void calculateShadowViewMutationsFlattener( }); // Views in other tree that are visited by sub-flattening or sub-unflattening - TinyMap subVisitedOtherNewNodes{}; - TinyMap subVisitedOtherOldNodes{}; + TinyMap subVisitedOtherNewNodes{}; + TinyMap subVisitedOtherOldNodes{}; auto subVisitedNewMap = (parentSubVisitedOtherNewNodes != nullptr ? parentSubVisitedOtherNewNodes : &subVisitedOtherNewNodes); @@ -425,7 +427,7 @@ static void calculateShadowViewMutationsFlattener( // Candidates for full tree creation or deletion at the end of this function auto deletionCreationCandidatePairs = - TinyMap{}; + TinyMap{}; for (size_t index = 0; index < treeChildren.size() && index < treeChildren.size(); @@ -553,7 +555,8 @@ static void calculateShadowViewMutationsFlattener( // Unflatten parent, flatten child if (childReparentMode == ReparentMode::Flatten) { // Construct unvisited nodes map - auto unvisitedNewChildPairs = TinyMap{}; + auto unvisitedNewChildPairs = + TinyMap{}; // Memory note: these oldFlattenedNodes all disappear at the end of // this "else" block, including any annotations we put on them. auto newFlattenedNodes = sliceChildShadowNodeViewPairsV2( @@ -612,7 +615,8 @@ static void calculateShadowViewMutationsFlattener( // Flatten parent, unflatten child else { // Construct unvisited nodes map - auto unvisitedOldChildPairs = TinyMap{}; + auto unvisitedOldChildPairs = + TinyMap{}; // Memory note: these oldFlattenedNodes all disappear at the end of // this "else" block, including any annotations we put on them. auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( @@ -664,7 +668,7 @@ static void calculateShadowViewMutationsFlattener( auto oldRemainingChildInListIt = std::find_if( treeChildren.begin(), treeChildren.end(), - [&tag](ShadowViewNodePair &nodePair) { + [&tag](ShadowViewNodePairLegacy &nodePair) { return nodePair.shadowView.tag == tag; }); if (oldRemainingChildInListIt != treeChildren.end()) { @@ -816,8 +820,8 @@ static void calculateShadowViewMutationsV2( BREADCRUMB_TYPE breadcrumb, ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs) { + ShadowViewNodePairLegacy::OwningList &&oldChildPairs, + ShadowViewNodePairLegacy::OwningList &&newChildPairs) { if (oldChildPairs.empty() && newChildPairs.empty()) { return; } @@ -995,9 +999,10 @@ static void calculateShadowViewMutationsV2( } } else { // Collect map of tags in the new list - auto newRemainingPairs = TinyMap{}; - auto newInsertedPairs = TinyMap{}; - auto deletionCandidatePairs = TinyMap{}; + auto newRemainingPairs = TinyMap{}; + auto newInsertedPairs = TinyMap{}; + auto deletionCandidatePairs = + TinyMap{}; for (; index < newChildPairs.size(); index++) { auto &newChildPair = newChildPairs[index]; newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair}); @@ -1106,7 +1111,7 @@ static void calculateShadowViewMutationsV2( else { // Construct unvisited nodes map auto unvisitedOldChildPairs = - TinyMap{}; + TinyMap{}; // We don't know where all the children of oldChildPair are within // oldChildPairs, but we know that they're in the same relative // order. The reason for this is because of flattening + zIndex: @@ -1236,7 +1241,7 @@ static void calculateShadowViewMutationsV2( else { // Construct unvisited nodes map auto unvisitedOldChildPairs = - TinyMap{}; + TinyMap{}; // We don't know where all the children of oldChildPair are within // oldChildPairs, but we know that they're in the same relative // order. The reason for this is because of flattening + zIndex: @@ -1524,64 +1529,6 @@ static void calculateShadowViewMutationsV2( std::back_inserter(mutations)); } -/** - * Only used by unit tests currently. - */ -static void sliceChildShadowNodeViewPairsRecursivelyLegacy( - ShadowViewNodePair::List &pairList, - Point layoutOffset, - ShadowNode const &shadowNode) { - for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { - auto &childShadowNode = *sharedChildShadowNode; - -#ifndef ANDROID - // Temporary disabled on Android because the mounting infrastructure - // is not fully ready yet. - if (childShadowNode.getTraits().check(ShadowNodeTraits::Trait::Hidden)) { - continue; - } -#endif - - auto shadowView = ShadowView(childShadowNode); - auto origin = layoutOffset; - if (shadowView.layoutMetrics != EmptyLayoutMetrics) { - origin += shadowView.layoutMetrics.frame.origin; - shadowView.layoutMetrics.frame.origin += layoutOffset; - } - - if (childShadowNode.getTraits().check( - ShadowNodeTraits::Trait::FormsStackingContext)) { - pairList.push_back({shadowView, &childShadowNode}); - } else { - if (childShadowNode.getTraits().check( - ShadowNodeTraits::Trait::FormsView)) { - pairList.push_back({shadowView, &childShadowNode}); - } - - sliceChildShadowNodeViewPairsRecursivelyLegacy( - pairList, origin, childShadowNode); - } - } -} - -/** - * Only used by unit tests currently. - */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsLegacy( - ShadowNode const &shadowNode) { - auto pairList = ShadowViewNodePair::List{}; - - if (!shadowNode.getTraits().check( - ShadowNodeTraits::Trait::FormsStackingContext) && - shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) { - return pairList; - } - - sliceChildShadowNodeViewPairsRecursivelyLegacy(pairList, {0, 0}, shadowNode); - - return pairList; -} - ShadowViewMutation::List calculateShadowViewMutations( ShadowNode const &oldRootShadowNode, ShadowNode const &newRootShadowNode) { diff --git a/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.h b/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.h index fed70547f5..e344bd211e 100644 --- a/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.h +++ b/ReactCommon/react/renderer/mounting/DifferentiatorFlatteningClassic.h @@ -36,17 +36,10 @@ ShadowViewMutationList calculateShadowViewMutations( * flattened view hierarchy. The V2 version preserves nodes even if they do * not form views and their children are flattened. */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( +ShadowViewNodePairLegacy::OwningList sliceChildShadowNodeViewPairsV2( ShadowNode const &shadowNode, bool allowFlattened = false); -/* - * Generates a list of `ShadowViewNodePair`s that represents a layer of a - * flattened view hierarchy. This is *only* used by unit tests currently. - */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsLegacy( - ShadowNode const &shadowNode); - } // namespace DifferOld } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/mounting/ShadowView.cpp b/ReactCommon/react/renderer/mounting/ShadowView.cpp index f4f966ed67..203a1fc95b 100644 --- a/ReactCommon/react/renderer/mounting/ShadowView.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowView.cpp @@ -83,5 +83,15 @@ bool ShadowViewNodePair::operator!=(const ShadowViewNodePair &rhs) const { return !(*this == rhs); } +bool ShadowViewNodePairLegacy::operator==( + const ShadowViewNodePairLegacy &rhs) const { + return this->shadowNode == rhs.shadowNode; +} + +bool ShadowViewNodePairLegacy::operator!=( + const ShadowViewNodePairLegacy &rhs) const { + return !(*this == rhs); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/mounting/ShadowView.h b/ReactCommon/react/renderer/mounting/ShadowView.h index 20da653d9d..917c675412 100644 --- a/ReactCommon/react/renderer/mounting/ShadowView.h +++ b/ReactCommon/react/renderer/mounting/ShadowView.h @@ -64,7 +64,9 @@ std::vector getDebugProps( * */ struct ShadowViewNodePair final { - using List = better:: + using NonOwningList = better:: + small_vector; + using OwningList = better:: small_vector; ShadowView shadowView; @@ -74,21 +76,49 @@ struct ShadowViewNodePair final { size_t mountIndex{0}; + /** + * This is nullptr unless `inOtherTree` is set to true. + * We rely on this only for marginal cases. TODO: could we + * rely on this more heavily to simplify the diffing algorithm + * overall? + */ + ShadowViewNodePair const *otherTreePair{nullptr}; + + /* + * The stored pointer to `ShadowNode` represents an identity of the pair. + */ + bool operator==(const ShadowViewNodePair &rhs) const; + bool operator!=(const ShadowViewNodePair &rhs) const; + + bool inOtherTree() const { + return this->otherTreePair != nullptr; + } +}; + +/* + * Describes pair of a `ShadowView` and a `ShadowNode`. + * This is not exposed to the mounting layer. + * + */ +struct ShadowViewNodePairLegacy final { + using OwningList = better::small_vector< + ShadowViewNodePairLegacy, + kShadowNodeChildrenSmallVectorSize>; + + ShadowView shadowView; + ShadowNode const *shadowNode; + bool flattened{false}; + bool isConcreteView{true}; + + size_t mountIndex{0}; + bool inOtherTree{false}; - /** - * This is nullptr unless `inOtherTree` is set to true. - * We rely on this only for marginal cases. TODO: could we - * rely on this more heavily to simplify the diffing algorithm - * overall? - */ - ShadowNode const *otherTreeShadowNode{nullptr}; - /* * The stored pointer to `ShadowNode` represents an identity of the pair. */ - bool operator==(const ShadowViewNodePair &rhs) const; - bool operator!=(const ShadowViewNodePair &rhs) const; + bool operator==(const ShadowViewNodePairLegacy &rhs) const; + bool operator!=(const ShadowViewNodePairLegacy &rhs) const; }; } // namespace react diff --git a/ReactCommon/react/renderer/mounting/stubs.cpp b/ReactCommon/react/renderer/mounting/stubs.cpp index 3c483a46ea..c9c1709b71 100644 --- a/ReactCommon/react/renderer/mounting/stubs.cpp +++ b/ReactCommon/react/renderer/mounting/stubs.cpp @@ -26,7 +26,8 @@ static bool shouldFirstPairComesBeforeSecondOne( /* * Reorders pairs in-place based on `orderIndex` using a stable sort algorithm. */ -static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { +static void reorderInPlaceIfNeeded( + ShadowViewNodePair::OwningList &pairs) noexcept { // This is a simplified version of the function intentionally copied from // `Differentiator.cpp`. std::stable_sort( @@ -42,7 +43,7 @@ static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { static void calculateShadowViewMutationsForNewTree( ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List newChildPairs) { + ShadowViewNodePair::OwningList newChildPairs) { // Sorting pairs based on `orderIndex` if needed. reorderInPlaceIfNeeded(newChildPairs);