diff --git a/React/Fabric/Mounting/RCTMountingManager.h b/React/Fabric/Mounting/RCTMountingManager.h index 89a90ccb7..e3b22e5e2 100644 --- a/React/Fabric/Mounting/RCTMountingManager.h +++ b/React/Fabric/Mounting/RCTMountingManager.h @@ -7,7 +7,8 @@ #import -#import +#import +#import #import #import @@ -28,8 +29,8 @@ NS_ASSUME_NONNULL_BEGIN * The order of mutation tnstructions matters. * Can be called from any thread. */ -- (void)mutateComponentViewTreeWithMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions - rootTag:(ReactTag)rootTag; +- (void)performTransactionWithMutations:(facebook::react::ShadowViewMutationList)mutations + rootTag:(ReactTag)rootTag; /** * Suggests preliminary creation of a component view of given type. diff --git a/React/Fabric/Mounting/RCTMountingManager.mm b/React/Fabric/Mounting/RCTMountingManager.mm index 49326221f..75d4647c8 100644 --- a/React/Fabric/Mounting/RCTMountingManager.mm +++ b/React/Fabric/Mounting/RCTMountingManager.mm @@ -38,123 +38,112 @@ using namespace facebook::react; return self; } -- (void)mutateComponentViewTreeWithMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions - rootTag:(ReactTag)rootTag +- (void)performTransactionWithMutations:(facebook::react::ShadowViewMutationList)mutations + rootTag:(ReactTag)rootTag { NSMutableArray *mountItems = - [[NSMutableArray alloc] initWithCapacity:instructions.size() * 2 /* ~ the worst case */]; + [[NSMutableArray alloc] initWithCapacity:mutations.size() * 2 /* ~ the worst case */]; - for (auto instruction : instructions) { - switch (instruction.getType()) { - case TreeMutationInstruction::Creation: { - NSString *componentName = RCTNSStringFromString(instruction.getNewChildNode()->getComponentName(), NSASCIIStringEncoding); + for (const auto &mutation : mutations) { + switch (mutation.type) { + case ShadowViewMutation::Create: { + NSString *componentName = RCTNSStringFromString(mutation.newChildShadowView.componentName, NSASCIIStringEncoding); RCTCreateMountItem *mountItem = [[RCTCreateMountItem alloc] initWithComponentName:componentName - tag:instruction.getNewChildNode()->getTag()]; + tag:mutation.newChildShadowView.tag]; [mountItems addObject:mountItem]; break; } - case TreeMutationInstruction::Deletion: { - NSString *componentName = RCTNSStringFromString(instruction.getOldChildNode()->getComponentName(), NSASCIIStringEncoding); + case ShadowViewMutation::Delete: { + NSString *componentName = RCTNSStringFromString(mutation.oldChildShadowView.componentName, NSASCIIStringEncoding); RCTDeleteMountItem *mountItem = [[RCTDeleteMountItem alloc] initWithComponentName:componentName - tag:instruction.getOldChildNode()->getTag()]; + tag:mutation.oldChildShadowView.tag]; [mountItems addObject:mountItem]; break; } - case TreeMutationInstruction::Insertion: { + case ShadowViewMutation::Insert: { // Props - [mountItems addObject:[[RCTUpdatePropsMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() + [mountItems addObject:[[RCTUpdatePropsMountItem alloc] initWithTag:mutation.newChildShadowView.tag oldProps:nullptr - newProps:instruction.getNewChildNode()->getProps()]]; + newProps:mutation.newChildShadowView.props]]; // EventEmitter - [mountItems addObject:[[RCTUpdateEventEmitterMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() - eventEmitter:instruction.getNewChildNode()->getEventEmitter()]]; + [mountItems addObject:[[RCTUpdateEventEmitterMountItem alloc] initWithTag:mutation.newChildShadowView.tag + eventEmitter:mutation.newChildShadowView.eventEmitter]]; // LocalData - if (instruction.getNewChildNode()->getLocalData()) { - [mountItems addObject:[[RCTUpdateLocalDataMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() + if (mutation.newChildShadowView.localData) { + [mountItems addObject:[[RCTUpdateLocalDataMountItem alloc] initWithTag:mutation.newChildShadowView.tag oldLocalData:nullptr - newLocalData:instruction.getNewChildNode()->getLocalData()]]; + newLocalData:mutation.newChildShadowView.localData]]; } // Layout - auto layoutableNewShadowNode = - std::dynamic_pointer_cast(instruction.getNewChildNode()); - - if (layoutableNewShadowNode) { - [mountItems addObject:[[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() + if (mutation.newChildShadowView.layoutMetrics != EmptyLayoutMetrics) { + [mountItems addObject:[[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:mutation.newChildShadowView.tag oldLayoutMetrics:{} - newLayoutMetrics:layoutableNewShadowNode->getLayoutMetrics()]]; + newLayoutMetrics:mutation.newChildShadowView.layoutMetrics]]; } // Insertion RCTInsertMountItem *mountItem = - [[RCTInsertMountItem alloc] initWithChildTag:instruction.getNewChildNode()->getTag() - parentTag:instruction.getParentNode()->getTag() - index:instruction.getIndex()]; + [[RCTInsertMountItem alloc] initWithChildTag:mutation.newChildShadowView.tag + parentTag:mutation.parentShadowView.tag + index:mutation.index]; [mountItems addObject:mountItem]; break; } - case TreeMutationInstruction::Removal: { + case ShadowViewMutation::Remove: { RCTRemoveMountItem *mountItem = - [[RCTRemoveMountItem alloc] initWithChildTag:instruction.getOldChildNode()->getTag() - parentTag:instruction.getParentNode()->getTag() - index:instruction.getIndex()]; + [[RCTRemoveMountItem alloc] initWithChildTag:mutation.oldChildShadowView.tag + parentTag:mutation.parentShadowView.tag + index:mutation.index]; [mountItems addObject:mountItem]; break; } - case TreeMutationInstruction::Replacement: { - SharedShadowNode oldShadowNode = instruction.getOldChildNode(); - SharedShadowNode newShadowNode = instruction.getNewChildNode(); + case ShadowViewMutation::Update: { + auto oldChildShadowView = mutation.oldChildShadowView; + auto newChildShadowView = mutation.newChildShadowView; // Props - if (oldShadowNode->getProps() != newShadowNode->getProps()) { + if (oldChildShadowView.props != newChildShadowView.props) { RCTUpdatePropsMountItem *mountItem = - [[RCTUpdatePropsMountItem alloc] initWithTag:instruction.getOldChildNode()->getTag() - oldProps:instruction.getOldChildNode()->getProps() - newProps:instruction.getNewChildNode()->getProps()]; + [[RCTUpdatePropsMountItem alloc] initWithTag:mutation.oldChildShadowView.tag + oldProps:mutation.oldChildShadowView.props + newProps:mutation.newChildShadowView.props]; [mountItems addObject:mountItem]; } // EventEmitter - if (oldShadowNode->getEventEmitter() != newShadowNode->getEventEmitter()) { + if (oldChildShadowView.eventEmitter != newChildShadowView.eventEmitter) { RCTUpdateEventEmitterMountItem *mountItem = - [[RCTUpdateEventEmitterMountItem alloc] initWithTag:instruction.getOldChildNode()->getTag() - eventEmitter:instruction.getOldChildNode()->getEventEmitter()]; + [[RCTUpdateEventEmitterMountItem alloc] initWithTag:mutation.oldChildShadowView.tag + eventEmitter:mutation.oldChildShadowView.eventEmitter]; [mountItems addObject:mountItem]; } // LocalData - if (oldShadowNode->getLocalData() != newShadowNode->getLocalData()) { + if (oldChildShadowView.localData != newChildShadowView.localData) { RCTUpdateLocalDataMountItem *mountItem = - [[RCTUpdateLocalDataMountItem alloc] initWithTag:newShadowNode->getTag() - oldLocalData:oldShadowNode->getLocalData() - newLocalData:newShadowNode->getLocalData()]; + [[RCTUpdateLocalDataMountItem alloc] initWithTag:newChildShadowView.tag + oldLocalData:oldChildShadowView.localData + newLocalData:newChildShadowView.localData]; [mountItems addObject:mountItem]; } // Layout - auto layoutableOldShadowNode = - std::dynamic_pointer_cast(oldShadowNode); - - if (layoutableOldShadowNode) { - auto layoutableNewShadowNode = - std::dynamic_pointer_cast(newShadowNode); - - if (layoutableOldShadowNode->getLayoutMetrics() != layoutableNewShadowNode->getLayoutMetrics()) { - RCTUpdateLayoutMetricsMountItem *mountItem = - [[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:instruction.getOldChildNode()->getTag() - oldLayoutMetrics:layoutableOldShadowNode->getLayoutMetrics() - newLayoutMetrics:layoutableNewShadowNode->getLayoutMetrics()]; - [mountItems addObject:mountItem]; - } + if (oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) { + RCTUpdateLayoutMetricsMountItem *mountItem = + [[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:mutation.oldChildShadowView.tag + oldLayoutMetrics:oldChildShadowView.layoutMetrics + newLayoutMetrics:newChildShadowView.layoutMetrics]; + [mountItems addObject:mountItem]; } break; diff --git a/React/Fabric/RCTScheduler.h b/React/Fabric/RCTScheduler.h index 681404687..617f02ecb 100644 --- a/React/Fabric/RCTScheduler.h +++ b/React/Fabric/RCTScheduler.h @@ -12,7 +12,7 @@ #import #import #import -#import +#import NS_ASSUME_NONNULL_BEGIN @@ -23,7 +23,8 @@ NS_ASSUME_NONNULL_BEGIN */ @protocol RCTSchedulerDelegate -- (void)schedulerDidComputeMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions rootTag:(ReactTag)rootTag; +- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations + rootTag:(ReactTag)rootTag; - (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName; diff --git a/React/Fabric/RCTScheduler.mm b/React/Fabric/RCTScheduler.mm index 3e1d89236..e7623f576 100644 --- a/React/Fabric/RCTScheduler.mm +++ b/React/Fabric/RCTScheduler.mm @@ -30,9 +30,9 @@ class SchedulerDelegateProxy: public SchedulerDelegate { public: SchedulerDelegateProxy(void *scheduler): scheduler_(scheduler) {} - void schedulerDidComputeMutationInstructions(Tag rootTag, const TreeMutationInstructionList &instructions) override { + void schedulerDidFinishTransaction(Tag rootTag, const ShadowViewMutationList &mutations) override { RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; - [scheduler.delegate schedulerDidComputeMutationInstructions:instructions rootTag:rootTag]; + [scheduler.delegate schedulerDidFinishTransaction:mutations rootTag:rootTag]; } void schedulerDidRequestPreliminaryViewAllocation(ComponentName componentName) override { diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index aedcd7ae8..075ae747c 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -69,11 +69,11 @@ using namespace facebook::react; #pragma mark - RCTSchedulerDelegate -- (void)schedulerDidComputeMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions +- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations rootTag:(ReactTag)rootTag { - [_mountingManager mutateComponentViewTreeWithMutationInstructions:instructions - rootTag:rootTag]; + [_mountingManager performTransactionWithMutations:mutations + rootTag:rootTag]; } - (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName @@ -89,7 +89,7 @@ using namespace facebook::react; [_scheduler registerRootTag:surface.rootTag]; [self runSurface:surface]; - // FIXME: Mutation instruction MUST produce instruction for root node. + // FIXME: mutation MUST produce instruction for root node. [_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag]; } diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index 3f60b3d45..3642e7d20 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -106,7 +106,7 @@ public: /* * Equality operators. * Use this to compare `ShadowNode`s values for equality (and non-equality). - * Same values indicates that nodes must not produce mutation instructions + * Same values indicates that nodes must not produce mutations * during tree diffing process. * Child nodes are not considered as part of the value. */ diff --git a/ReactCommon/fabric/uimanager/Differentiator.cpp b/ReactCommon/fabric/uimanager/Differentiator.cpp index e5e1da99e..43b837850 100644 --- a/ReactCommon/fabric/uimanager/Differentiator.cpp +++ b/ReactCommon/fabric/uimanager/Differentiator.cpp @@ -5,202 +5,217 @@ #include "Differentiator.h" +#include "ShadowView.h" +#include + namespace facebook { namespace react { -static void calculateMutationInstructions( - TreeMutationInstructionList &instructions, - SharedShadowNode parentNode, - const SharedShadowNodeList &oldChildNodes, - const SharedShadowNodeList &newChildNodes +static ShadowViewNodePairList sliceChildShadowNodeViewPairs(const ShadowNode &shadowNode) { + ShadowViewNodePairList pairList; + + for (const auto &childShadowNode : shadowNode.getChildren()) { + pairList.push_back({ShadowView(*childShadowNode), *childShadowNode}); + } + + return pairList; +} + +static void calculateShadowViewMutations( + ShadowViewMutationList &mutations, + const ShadowView &parentShadowView, + const ShadowViewNodePairList &oldChildPairs, + const ShadowViewNodePairList &newChildPairs ) { // The current version of the algorithm is otimized for simplicity, - // not for performance of optimal result. + // not for performance or optimal result. - // TODO(shergin): Consider to use Minimal Edit Distance algorithm to produce - // optimal set of instructions and improve mounting performance. - // https://en.wikipedia.org/wiki/Edit_distance - // https://www.geeksforgeeks.org/dynamic-programming-set-5-edit-distance/ - - if (oldChildNodes == newChildNodes) { + if (oldChildPairs == newChildPairs) { return; } - if (oldChildNodes.size() == 0 && newChildNodes.size() == 0) { + if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) { return; } - std::unordered_map insertedNodes; + std::unordered_map insertedPaires; int index = 0; - TreeMutationInstructionList createInstructions = {}; - TreeMutationInstructionList deleteInstructions = {}; - TreeMutationInstructionList insertInstructions = {}; - TreeMutationInstructionList removeInstructions = {}; - TreeMutationInstructionList replaceInstructions = {}; - TreeMutationInstructionList downwardInstructions = {}; - TreeMutationInstructionList destructionDownwardInstructions = {}; + ShadowViewMutationList createMutations = {}; + ShadowViewMutationList deleteMutations = {}; + ShadowViewMutationList insertMutations = {}; + ShadowViewMutationList removeMutations = {}; + ShadowViewMutationList updateMutations = {}; + ShadowViewMutationList downwardMutations = {}; + ShadowViewMutationList destructiveDownwardMutations = {}; - // Stage 1: Collectings Updates - for (index = 0; index < oldChildNodes.size() && index < newChildNodes.size(); index++) { - const auto &oldChildNode = oldChildNodes.at(index); - const auto &newChildNode = newChildNodes.at(index); + // Stage 1: Collecting `Update` mutations + for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size(); index++) { + const auto &oldChildPair = oldChildPairs[index]; + const auto &newChildPair = newChildPairs[index]; - if (oldChildNode->getTag() != newChildNode->getTag()) { + if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) { // Totally different nodes, updating is impossible. break; } - if (*oldChildNode != *newChildNode) { - replaceInstructions.push_back( - TreeMutationInstruction::Replace( - parentNode, - oldChildNode, - newChildNode, + if (oldChildPair.shadowView != newChildPair.shadowView) { + updateMutations.push_back( + ShadowViewMutation::UpdateMutation( + parentShadowView, + oldChildPair.shadowView, + newChildPair.shadowView, index ) ); } - calculateMutationInstructions( - *(newChildNode->getChildren().size() ? &downwardInstructions : &destructionDownwardInstructions), - oldChildNode, - oldChildNode->getChildren(), - newChildNode->getChildren() + const auto oldGrandChildPairs = sliceChildShadowNodeViewPairs(oldChildPair.shadowNode); + const auto newGrandChildPairs = sliceChildShadowNodeViewPairs(newChildPair.shadowNode); + calculateShadowViewMutations( + *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), + oldChildPair.shadowView, + oldGrandChildPairs, + newGrandChildPairs ); } int lastIndexAfterFirstStage = index; - // Stage 2: Collectings Insertions - for (; index < newChildNodes.size(); index++) { - const auto &newChildNode = newChildNodes.at(index); + // Stage 2: Collecting `Insert` mutations + for (; index < newChildPairs.size(); index++) { + const auto &newChildPair = newChildPairs[index]; - insertInstructions.push_back( - TreeMutationInstruction::Insert( - parentNode, - newChildNode, + insertMutations.push_back( + ShadowViewMutation::InsertMutation( + parentShadowView, + newChildPair.shadowView, index ) ); - insertedNodes.insert({newChildNode->getTag(), newChildNode}); + insertedPaires.insert({newChildPair.shadowView.tag, newChildPair}); } - // Stage 3: Collectings Deletions and Removals - for (index = lastIndexAfterFirstStage; index < oldChildNodes.size(); index++) { - const auto &oldChildNode = oldChildNodes.at(index); + // Stage 3: Collecting `Delete` and `Remove` mutations + for (index = lastIndexAfterFirstStage; index < oldChildPairs.size(); index++) { + const auto &oldChildPair = oldChildPairs[index]; - // Even if the old node was (re)inserted, we have to generate `remove` - // instruction. - removeInstructions.push_back( - TreeMutationInstruction::Remove( - parentNode, - oldChildNode, + // Even if the old view was (re)inserted, we have to generate `remove` + // mutation. + removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + parentShadowView, + oldChildPair.shadowView, index ) ); - const auto &it = insertedNodes.find(oldChildNode->getTag()); + const auto &it = insertedPaires.find(oldChildPair.shadowView.tag); - if (it == insertedNodes.end()) { - // The old node was *not* (re)inserted. - // We have to generate `delete` instruction and apply the algorithm + if (it == insertedPaires.end()) { + // The old view was *not* (re)inserted. + // We have to generate `delete` mutation and apply the algorithm // recursively. - deleteInstructions.push_back( - TreeMutationInstruction::Delete( - oldChildNode + 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 node. - calculateMutationInstructions( - destructionDownwardInstructions, - oldChildNode, - oldChildNode->getChildren(), + // subtree starting from the removed view. + calculateShadowViewMutations( + destructiveDownwardMutations, + oldChildPair.shadowView, + sliceChildShadowNodeViewPairs(oldChildPair.shadowNode), {} ); } else { - // The old node *was* (re)inserted. - // We have to call the algorithm recursively if the inserted node + // The old view *was* (re)inserted. + // We have to call the algorithm recursively if the inserted view // is *not* the same as removed one. - const auto &newChildNode = it->second; - if (newChildNode != oldChildNode) { - calculateMutationInstructions( - *(newChildNode->getChildren().size() ? &downwardInstructions : &destructionDownwardInstructions), - newChildNode, - oldChildNode->getChildren(), - newChildNode->getChildren() + const auto &newChildPair = it->second; + if (newChildPair.shadowView != oldChildPair.shadowView) { + const auto oldGrandChildPairs = sliceChildShadowNodeViewPairs(oldChildPair.shadowNode); + const auto newGrandChildPairs = sliceChildShadowNodeViewPairs(newChildPair.shadowNode); + calculateShadowViewMutations( + *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), + newChildPair.shadowView, + oldGrandChildPairs, + newGrandChildPairs ); } - // In any case we have to remove the node from `insertedNodes` as - // indication that the node was actually removed (which means that - // the node existed before), hence we don't have to generate - // `create` instruction. - insertedNodes.erase(it); + // In any case we have to remove the view from `insertedPaires` as + // indication that the view was actually removed (which means that + // the view existed before), hence we don't have to generate + // `create` mutation. + insertedPaires.erase(it); } } - // Stage 4: Collectings Creations - for (index = lastIndexAfterFirstStage; index < newChildNodes.size(); index++) { - const auto &newChildNode = newChildNodes.at(index); + // Stage 4: Collecting `Create` mutations + for (index = lastIndexAfterFirstStage; index < newChildPairs.size(); index++) { + const auto &newChildPair = newChildPairs[index]; - if (insertedNodes.find(newChildNode->getTag()) == insertedNodes.end()) { - // The new node was (re)inserted, so there is no need to create it. + if (insertedPaires.find(newChildPair.shadowView.tag) == insertedPaires.end()) { + // The new view was (re)inserted, so there is no need to create it. continue; } - createInstructions.push_back( - TreeMutationInstruction::Create( - newChildNode + createMutations.push_back( + ShadowViewMutation::CreateMutation( + newChildPair.shadowView ) ); - calculateMutationInstructions( - downwardInstructions, - newChildNode, + calculateShadowViewMutations( + downwardMutations, + newChildPair.shadowView, {}, - newChildNode->getChildren() + sliceChildShadowNodeViewPairs(newChildPair.shadowNode) ); } - // All instructions in an optimal order: - instructions.insert(instructions.end(), destructionDownwardInstructions.begin(), destructionDownwardInstructions.end()); - instructions.insert(instructions.end(), replaceInstructions.begin(), replaceInstructions.end()); - instructions.insert(instructions.end(), removeInstructions.rbegin(), removeInstructions.rend()); - instructions.insert(instructions.end(), createInstructions.begin(), createInstructions.end()); - instructions.insert(instructions.end(), downwardInstructions.begin(), downwardInstructions.end()); - instructions.insert(instructions.end(), insertInstructions.begin(), insertInstructions.end()); - instructions.insert(instructions.end(), deleteInstructions.begin(), deleteInstructions.end()); + // All mutations in an optimal order: + mutations.insert(mutations.end(), destructiveDownwardMutations.begin(), destructiveDownwardMutations.end()); + mutations.insert(mutations.end(), updateMutations.begin(), updateMutations.end()); + mutations.insert(mutations.end(), removeMutations.rbegin(), removeMutations.rend()); + mutations.insert(mutations.end(), deleteMutations.begin(), deleteMutations.end()); + mutations.insert(mutations.end(), createMutations.begin(), createMutations.end()); + mutations.insert(mutations.end(), insertMutations.begin(), insertMutations.end()); + mutations.insert(mutations.end(), downwardMutations.begin(), downwardMutations.end()); } -void calculateMutationInstructions( - TreeMutationInstructionList &instructions, - const SharedShadowNode &oldRootShadowNode, - const SharedShadowNode &newRootShadowNode +ShadowViewMutationList calculateShadowViewMutations( + const ShadowNode &oldRootShadowNode, + const ShadowNode &newRootShadowNode ) { // Root shadow nodes must have same tag. - assert(oldRootShadowNode->getTag() == newRootShadowNode->getTag()); + assert(oldRootShadowNode.getTag() == newRootShadowNode.getTag()); - if (*oldRootShadowNode != *newRootShadowNode) { - instructions.push_back( - TreeMutationInstruction::Replace( - nullptr, - oldRootShadowNode, - newRootShadowNode, + ShadowViewMutationList mutations; + + if (oldRootShadowNode != newRootShadowNode) { + mutations.push_back( + ShadowViewMutation::UpdateMutation( + ShadowView(), + ShadowView(oldRootShadowNode), + ShadowView(newRootShadowNode), -1 ) ); } - calculateMutationInstructions( - instructions, - oldRootShadowNode, - oldRootShadowNode->getChildren(), - newRootShadowNode->getChildren() + calculateShadowViewMutations( + mutations, + ShadowView(oldRootShadowNode), + sliceChildShadowNodeViewPairs(oldRootShadowNode), + sliceChildShadowNodeViewPairs(newRootShadowNode) ); + + return mutations; } } // namespace react diff --git a/ReactCommon/fabric/uimanager/Differentiator.h b/ReactCommon/fabric/uimanager/Differentiator.h index fbfcddd62..61d2a0d2f 100644 --- a/ReactCommon/fabric/uimanager/Differentiator.h +++ b/ReactCommon/fabric/uimanager/Differentiator.h @@ -6,20 +6,19 @@ #pragma once #include -#include +#include namespace facebook { namespace react { /* - * Calculates set of mutation instuctions which describe how the old - * ShadowNode tree can be transformed to the new ShadowNode tree. - * The set of instuctions might be and might not be optimal. + * 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. */ -void calculateMutationInstructions( - TreeMutationInstructionList &instructions, - const SharedShadowNode &oldNode, - const SharedShadowNode &newNode +ShadowViewMutationList calculateShadowViewMutations( + const ShadowNode &oldRootShadowNode, + const ShadowNode &newRootShadowNode ); } // namespace react diff --git a/ReactCommon/fabric/uimanager/Scheduler.cpp b/ReactCommon/fabric/uimanager/Scheduler.cpp index d338edc9f..c2dada719 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.cpp +++ b/ReactCommon/fabric/uimanager/Scheduler.cpp @@ -82,9 +82,9 @@ SchedulerDelegate *Scheduler::getDelegate() const { #pragma mark - ShadowTreeDelegate -void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) { +void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const ShadowViewMutationList &mutations) { if (delegate_) { - delegate_->schedulerDidComputeMutationInstructions(shadowTree->getRootTag(), instructions); + delegate_->schedulerDidFinishTransaction(shadowTree->getRootTag(), mutations); } } diff --git a/ReactCommon/fabric/uimanager/Scheduler.h b/ReactCommon/fabric/uimanager/Scheduler.h index 22f15349a..16c164867 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.h +++ b/ReactCommon/fabric/uimanager/Scheduler.h @@ -57,7 +57,7 @@ public: #pragma mark - ShadowTreeDelegate - void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) override; + void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const ShadowViewMutationList &mutations) override; #pragma mark - Deprecated @@ -67,7 +67,6 @@ public: std::shared_ptr getUIManager_DO_NOT_USE(); private: - SchedulerDelegate *delegate_; std::shared_ptr uiManager_; std::unordered_map shadowTreeRegistry_; diff --git a/ReactCommon/fabric/uimanager/SchedulerDelegate.h b/ReactCommon/fabric/uimanager/SchedulerDelegate.h index a6e98dae6..54e7646c1 100644 --- a/ReactCommon/fabric/uimanager/SchedulerDelegate.h +++ b/ReactCommon/fabric/uimanager/SchedulerDelegate.h @@ -9,7 +9,7 @@ #include #include -#include +#include namespace facebook { namespace react { @@ -24,10 +24,10 @@ public: /* * Called right after Scheduler computed (and laid out) a new updated version - * of the tree and calculated a set of mutation instructions which are - * suffisient to construct a new one. + * of the tree and calculated a set of mutations which are suffisient + * to construct a new one. */ - virtual void schedulerDidComputeMutationInstructions(Tag rootTag, const TreeMutationInstructionList &instructions) = 0; + virtual void schedulerDidFinishTransaction(Tag rootTag, const ShadowViewMutationList &mutations) = 0; /* * Called right after a new ShadowNode was created. diff --git a/ReactCommon/fabric/uimanager/ShadowTree.cpp b/ReactCommon/fabric/uimanager/ShadowTree.cpp index 702d8d4f7..7edb570ae 100644 --- a/ReactCommon/fabric/uimanager/ShadowTree.cpp +++ b/ReactCommon/fabric/uimanager/ShadowTree.cpp @@ -10,7 +10,7 @@ #include "ShadowTreeDelegate.h" #include "Differentiator.h" -#include "TreeMutationInstruction.h" +#include "ShadowViewMutation.h" namespace facebook { namespace react { @@ -78,19 +78,16 @@ void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) { newRootShadowNode->sealRecursive(); - TreeMutationInstructionList instructions = TreeMutationInstructionList(); - - calculateMutationInstructions( - instructions, - oldRootShadowNode, - newRootShadowNode + auto mutations = calculateShadowViewMutations( + *oldRootShadowNode, + *newRootShadowNode ); - if (commit(oldRootShadowNode, newRootShadowNode, instructions)) { - emitLayoutEvents(instructions); + if (commit(oldRootShadowNode, newRootShadowNode, mutations)) { + emitLayoutEvents(mutations); if (delegate_) { - delegate_->shadowTreeDidCommit(shared_from_this(), instructions); + delegate_->shadowTreeDidCommit(shared_from_this(), mutations); } } } @@ -98,7 +95,7 @@ void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) { bool ShadowTree::commit( const SharedRootShadowNode &oldRootShadowNode, const SharedRootShadowNode &newRootShadowNode, - const TreeMutationInstructionList &mutationInstructions + const ShadowViewMutationList &mutations ) { std::lock_guard lock(commitMutex_); @@ -108,69 +105,57 @@ bool ShadowTree::commit( rootShadowNode_ = newRootShadowNode; - toggleEventEmitters(mutationInstructions); + toggleEventEmitters(mutations); return true; } -void ShadowTree::emitLayoutEvents(const TreeMutationInstructionList &instructions) { - for (const auto &instruction : instructions) { - const auto &type = instruction.getType(); - - // Only `Insertion` and `Replacement` instructions can affect layout metrics. +void ShadowTree::emitLayoutEvents(const ShadowViewMutationList &mutations) { + for (const auto &mutation : mutations) { + // Only `Insert` and `Update` mutations can affect layout metrics. if ( - type == TreeMutationInstruction::Insertion || - type == TreeMutationInstruction::Replacement + mutation.type != ShadowViewMutation::Insert && + mutation.type != ShadowViewMutation::Update ) { - const auto &newShadowNode = instruction.getNewChildNode(); - const auto &eventEmitter = newShadowNode->getEventEmitter(); - const auto &viewEventEmitter = std::dynamic_pointer_cast(eventEmitter); - - // Checking if particular shadow node supports `onLayout` event (part of `ViewEventEmitter`). - if (viewEventEmitter) { - // Now we know that both (old and new) shadow nodes must be `LayoutableShadowNode` subclasses. - assert(std::dynamic_pointer_cast(newShadowNode)); - - // Checking if the `onLayout` event was requested for the particular Shadow Node. - const auto &viewProps = std::dynamic_pointer_cast(newShadowNode->getProps()); - if (viewProps && !viewProps->onLayout) { - continue; - } - - // TODO(T29661055): Consider using `std::reinterpret_pointer_cast`. - const auto &newLayoutableShadowNode = - std::dynamic_pointer_cast(newShadowNode); - - // In case if we have `oldShadowNode`, we have to check that layout metrics have changed. - if (type == TreeMutationInstruction::Replacement) { - const auto &oldShadowNode = instruction.getOldChildNode(); - assert(std::dynamic_pointer_cast(oldShadowNode)); - // TODO(T29661055): Consider using `std::reinterpret_pointer_cast`. - const auto &oldLayoutableShadowNode = - std::dynamic_pointer_cast(oldShadowNode); - - if (oldLayoutableShadowNode->getLayoutMetrics() == newLayoutableShadowNode->getLayoutMetrics()) { - continue; - } - } - - viewEventEmitter->onLayout(newLayoutableShadowNode->getLayoutMetrics()); - } + continue; } + + const auto viewEventEmitter = std::dynamic_pointer_cast(mutation.newChildShadowView.eventEmitter); + + // Checking if particular shadow node supports `onLayout` event (part of `ViewEventEmitter`). + if (!viewEventEmitter) { + continue; + } + + // Checking if the `onLayout` event was requested for the particular Shadow Node. + const auto viewProps = std::dynamic_pointer_cast(mutation.newChildShadowView.props); + if (viewProps && !viewProps->onLayout) { + continue; + } + + // In case if we have `oldChildShadowView`, checking that layout metrics have changed. + if ( + mutation.type != ShadowViewMutation::Update && + mutation.oldChildShadowView.layoutMetrics == mutation.newChildShadowView.layoutMetrics + ) { + continue; + } + + viewEventEmitter->onLayout(mutation.newChildShadowView.layoutMetrics); } } -void ShadowTree::toggleEventEmitters(const TreeMutationInstructionList &instructions) { +void ShadowTree::toggleEventEmitters(const ShadowViewMutationList &mutations) { std::lock_guard lock(EventEmitter::DispatchMutex()); - for (const auto &instruction : instructions) { - if (instruction.getType() == TreeMutationInstruction::Deletion) { - instruction.getOldChildNode()->getEventEmitter()->setEnabled(false); + for (const auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Delete) { + mutation.oldChildShadowView.eventEmitter->setEnabled(false); } } - for (const auto &instruction : instructions) { - if (instruction.getType() == TreeMutationInstruction::Creation) { - instruction.getNewChildNode()->getEventEmitter()->setEnabled(true); + for (const auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Create) { + mutation.newChildShadowView.eventEmitter->setEnabled(true); } } } diff --git a/ReactCommon/fabric/uimanager/ShadowTree.h b/ReactCommon/fabric/uimanager/ShadowTree.h index 66cb22eea..0c6f08942 100644 --- a/ReactCommon/fabric/uimanager/ShadowTree.h +++ b/ReactCommon/fabric/uimanager/ShadowTree.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -80,10 +81,10 @@ private: bool commit( const SharedRootShadowNode &oldRootShadowNode, const SharedRootShadowNode &newRootShadowNode, - const TreeMutationInstructionList &mutationInstructions + const ShadowViewMutationList &mutations ); - void toggleEventEmitters(const TreeMutationInstructionList &instructions); - void emitLayoutEvents(const TreeMutationInstructionList &instructions); + void toggleEventEmitters(const ShadowViewMutationList &mutations); + void emitLayoutEvents(const ShadowViewMutationList &mutations); const Tag rootTag_; SharedRootShadowNode rootShadowNode_; diff --git a/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h b/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h index f2da57031..580a8a9fb 100644 --- a/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h +++ b/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h @@ -5,7 +5,7 @@ #pragma once -#include +#include namespace facebook { namespace react { @@ -21,7 +21,7 @@ public: /* * Called right after Shadow Tree commit a new state of the the tree. */ - virtual void shadowTreeDidCommit(const std::shared_ptr &shadowTree, const TreeMutationInstructionList &instructions) = 0; + virtual void shadowTreeDidCommit(const std::shared_ptr &shadowTree, const ShadowViewMutationList &mutations) = 0; }; } // namespace react diff --git a/ReactCommon/fabric/uimanager/ShadowViewMutation.h b/ReactCommon/fabric/uimanager/ShadowViewMutation.h index 2ba76d3f7..3c2895e1a 100644 --- a/ReactCommon/fabric/uimanager/ShadowViewMutation.h +++ b/ReactCommon/fabric/uimanager/ShadowViewMutation.h @@ -15,7 +15,7 @@ namespace facebook { namespace react { /* - * Describes a single native view tree mutation instruction which may contain + * Describes a single native view tree mutation which may contain * pointers to an old shadow view, a new shadow view, a parent shadow view and * final index of inserted or updated view. * Use static methods to instantiate mutations of different types. @@ -25,21 +25,21 @@ struct ShadowViewMutation final { #pragma mark - Designated Initializers /* - * Creates and returns an `Create` mutation instruction. + * Creates and returns an `Create` mutation. */ static ShadowViewMutation CreateMutation( ShadowView shadowView ); /* - * Creates and returns an `Delete` mutation instruction. + * Creates and returns an `Delete` mutation. */ static ShadowViewMutation DeleteMutation( ShadowView shadowView ); /* - * Creates and returns an `Insert` mutation instruction. + * Creates and returns an `Insert` mutation. */ static ShadowViewMutation InsertMutation( ShadowView parentShadowView, @@ -48,16 +48,16 @@ struct ShadowViewMutation final { ); /* - * Creates and returns a `Remove` mutation instruction. + * Creates and returns a `Remove` mutation. */ static ShadowViewMutation RemoveMutation( ShadowView parentShadowView, ShadowView childShadowView, int index ); - + /* - * Creates and returns an `Update` mutation instruction. + * Creates and returns an `Update` mutation. */ static ShadowViewMutation UpdateMutation( ShadowView parentShadowView, diff --git a/ReactCommon/fabric/uimanager/TreeMutationInstruction.cpp b/ReactCommon/fabric/uimanager/TreeMutationInstruction.cpp deleted file mode 100644 index 630e13202..000000000 --- a/ReactCommon/fabric/uimanager/TreeMutationInstruction.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "TreeMutationInstruction.h" - -#include - -namespace facebook { -namespace react { - -const TreeMutationInstruction TreeMutationInstruction::Create( - SharedShadowNode node -) { - assert(node); - - return TreeMutationInstruction( - Creation, - nullptr, - nullptr, - node, - -1 - ); -} - -const TreeMutationInstruction TreeMutationInstruction::Delete( - SharedShadowNode node -) { - assert(node); - - return TreeMutationInstruction( - Deletion, - nullptr, - node, - nullptr, - -1 - ); -} - -const TreeMutationInstruction TreeMutationInstruction::Insert( - SharedShadowNode parentNode, - SharedShadowNode childNode, - int index -) { - assert(parentNode); - assert(childNode); - assert(index != -1); - - return TreeMutationInstruction( - Insertion, - parentNode, - nullptr, - childNode, - index - ); -} - -const TreeMutationInstruction TreeMutationInstruction::Remove( - SharedShadowNode parentNode, - SharedShadowNode childNode, - int index -) { - assert(parentNode); - assert(childNode); - assert(index != -1); - - return TreeMutationInstruction( - Removal, - parentNode, - childNode, - nullptr, - index - ); -} - -const TreeMutationInstruction TreeMutationInstruction::Replace( - SharedShadowNode parentNode, - SharedShadowNode oldChildNode, - SharedShadowNode newChildNode, - int index -) { - assert(oldChildNode); - assert(newChildNode); - - return TreeMutationInstruction( - Replacement, - parentNode, - oldChildNode, - newChildNode, - index - ); -} - -TreeMutationInstruction::TreeMutationInstruction( - Type type, - SharedShadowNode parentNode, - SharedShadowNode oldChildNode, - SharedShadowNode newChildNode, - int index -): - type_(type), - parentNode_(parentNode), - oldChildNode_(oldChildNode), - newChildNode_(newChildNode), - index_(index) {}; - -#pragma mark - Getters - -TreeMutationInstruction::Type TreeMutationInstruction::getType() const { - return type_; -} - -SharedShadowNode TreeMutationInstruction::getParentNode() const { - assert(parentNode_); - return parentNode_; -} - -SharedShadowNode TreeMutationInstruction::getOldChildNode() const { - assert(oldChildNode_); - return oldChildNode_; -} - -SharedShadowNode TreeMutationInstruction::getNewChildNode() const { - assert(newChildNode_); - return newChildNode_; -} - -int TreeMutationInstruction::getIndex() const { - assert(index_ != -1); - return index_; -} - -#pragma mark - DebugStringConvertible - -std::string TreeMutationInstruction::getDebugName() const { - switch (type_) { - case Creation: - return "Create"; - case Deletion: - return "Delete"; - case Insertion: - return "Insert"; - case Removal: - return "Remove"; - case Replacement: - return "Replace"; - } -}; - -std::string TreeMutationInstruction::getDebugValue() const { - switch (type_) { - case Creation: - return "[*" + folly::to(newChildNode_->getTag()) + "]"; - case Deletion: - return "[~" + folly::to(oldChildNode_->getTag()) + "]"; - case Insertion: - return "[" + folly::to(newChildNode_->getTag()) + "->" + folly::to(parentNode_->getTag()) + "]"; - case Removal: - return "[" + folly::to(oldChildNode_->getTag()) + "<~" + folly::to(parentNode_->getTag()) + "]"; - case Replacement: - return "[=" + folly::to(oldChildNode_->getTag()) + "]"; - } -}; - -SharedDebugStringConvertibleList TreeMutationInstruction::getDebugProps() const { - DebugStringConvertibleOptions options = {.maximumDepth = 1, .format = false}; - - switch (type_) { - case Creation: - return SharedDebugStringConvertibleList { - std::make_shared("node", newChildNode_->getDebugDescription(options)), - }; - case Deletion: - return SharedDebugStringConvertibleList { - std::make_shared("node", oldChildNode_->getDebugDescription(options)), - }; - case Insertion: - return SharedDebugStringConvertibleList { - std::make_shared("parentNode", parentNode_->getDebugDescription(options)), - std::make_shared("childNode", newChildNode_->getDebugDescription(options)), - std::make_shared("index", folly::to(index_)) - }; - case Removal: - return SharedDebugStringConvertibleList { - std::make_shared("parentNode", parentNode_->getDebugDescription(options)), - std::make_shared("childNode", oldChildNode_->getDebugDescription(options)), - std::make_shared("index", folly::to(index_)) - }; - case Replacement: - return SharedDebugStringConvertibleList { - std::make_shared("parentNode", parentNode_ ? parentNode_->getDebugDescription(options) : "nullptr"), - std::make_shared("oldChildNode", oldChildNode_->getDebugDescription(options)), - std::make_shared("newChildNode", newChildNode_->getDebugDescription(options)), - std::make_shared("index", folly::to(index_)) - }; - } -} - -} // namespace react -} // namespace facebook diff --git a/ReactCommon/fabric/uimanager/TreeMutationInstruction.h b/ReactCommon/fabric/uimanager/TreeMutationInstruction.h deleted file mode 100644 index c27e6f71f..000000000 --- a/ReactCommon/fabric/uimanager/TreeMutationInstruction.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include - -namespace facebook { -namespace react { - -class TreeMutationInstruction; - -using TreeMutationInstructionList = std::vector; - -/* - * Describes single native views tree mutation instruction which may contain - * pointers to an old shadow node, a new shadow node, a parent shadow node and - * final index of inserted or updated node. - * The relationship between native view instances and shadow node instances is - * defined by `tag` value. - * Use static methods to instantiate mutation instructions of different types. - */ -class TreeMutationInstruction: - public DebugStringConvertible { -public: - -#pragma mark - Designated Initializers - - /* - * Creates and returns an *Creation* instruction. - */ - static const TreeMutationInstruction Create( - SharedShadowNode node - ); - - /* - * Creates and returns an *Deletion* instruction. - */ - static const TreeMutationInstruction Delete( - SharedShadowNode node - ); - - /* - * Creates and returns an *Insertion* instruction. - */ - static const TreeMutationInstruction Insert( - SharedShadowNode parentNode, - SharedShadowNode childNode, - int index - ); - - /* - * Creates and returns a *Removal* instruction. - */ - static const TreeMutationInstruction Remove( - SharedShadowNode parentNode, - SharedShadowNode childNode, - int index - ); - - /* - * Creates and returns an *Replacement* instruction. - */ - static const TreeMutationInstruction Replace( - SharedShadowNode parentNode, - SharedShadowNode oldChildNode, - SharedShadowNode newChildNode, - int index - ); - -#pragma mark - Type - - enum Type { - Creation, - Deletion, - Insertion, - Removal, - Replacement - }; - -#pragma mark - Getters - - Type getType() const; - SharedShadowNode getParentNode() const; - SharedShadowNode getOldChildNode() const; - SharedShadowNode getNewChildNode() const; - int getIndex() const; - -#pragma mark - DebugStringConvertible - - std::string getDebugName() const override; - std::string getDebugValue() const override; - SharedDebugStringConvertibleList getDebugProps() const override; - -private: - TreeMutationInstruction( - Type type, - SharedShadowNode parentNode, - SharedShadowNode oldChildNode, - SharedShadowNode newChildNode, - int index - ); - - Type type_ {Creation}; - SharedShadowNode parentNode_ {nullptr}; - SharedShadowNode oldChildNode_ {nullptr}; - SharedShadowNode newChildNode_ {nullptr}; - int index_ {-1}; -}; - -} // namespace react -} // namespace facebook