Skip to content

Commit fb7be6f

Browse files
javachefacebook-github-bot
authored andcommitted
Fix differentiator emitting updates with incorrect parentTag
Summary: Address the test-case identified in D66557919, where Differentiator could emit updates for views referencing an incorrect parentTag. The longer-term fix here is to avoid emitting any updates for nodes which are being reparented, but that requires bigger changes, including to the LayoutAnimation system. As a short-term patch, we're passing through an explicit `parentShadowViewForUpdate` which will be used as the current parent for update purposes. {F1971278019} Changelog: [Android][Fixed] Fix Fabric mutations sometimes triggering a `getViewState` crash when referencing an invalid parentTag. Differential Revision: D66654293
1 parent fa8a25e commit fb7be6f

File tree

4 files changed

+35
-5
lines changed

4 files changed

+35
-5
lines changed

packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ LayoutAnimationKeyFrameManager::pullTransaction(
392392
if (keyframe.type == AnimationConfigurationType::Update &&
393393
mutation.newChildShadowView.tag > 0) {
394394
keyframe.viewPrev = mutation.newChildShadowView;
395+
keyframe.parentView = mutation.parentShadowView;
396+
react_native_assert(
397+
keyframe.finalMutationsForKeyFrame.size() == 1);
398+
keyframe.finalMutationsForKeyFrame[0].parentShadowView =
399+
mutation.parentShadowView;
395400
}
396401
}
397402
}

packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ static void calculateShadowViewMutationsFlattener(
403403
const ShadowView& parentShadowView,
404404
TinyMap<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
405405
const ShadowViewNodePair& node,
406+
const ShadowView& parentShadowViewForUpdate,
406407
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes = nullptr,
407408
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes = nullptr);
408409

@@ -450,7 +451,8 @@ static void updateMatchedPairSubtrees(
450451
mutationContainer,
451452
parentShadowView,
452453
newRemainingPairs,
453-
oldPair);
454+
oldPair,
455+
oldPair.shadowView);
454456
}
455457
// Unflattening
456458
else {
@@ -480,7 +482,8 @@ static void updateMatchedPairSubtrees(
480482
mutationContainer,
481483
parentShadowView,
482484
unvisitedOldChildPairs,
483-
newPair);
485+
newPair,
486+
parentShadowView);
484487

485488
// If old nodes were not visited, we know that we can delete
486489
// them now. They will be removed from the hierarchy by the
@@ -621,6 +624,10 @@ static void updateMatchedPair(
621624
* performed in the subtree. If it *is* in the map, it means the node is not
622625
* in the Tree, and should be Deleted/Created **after this function is
623626
* called**, by the caller.
627+
*
628+
* @param parentShadowView shadowView under which nodes should be mounted
629+
* @param parentShadowViewForUpdate current parent in which node is mounted,
630+
* used for update mutations
624631
*/
625632
static void calculateShadowViewMutationsFlattener(
626633
ViewNodePairScope& scope,
@@ -629,6 +636,7 @@ static void calculateShadowViewMutationsFlattener(
629636
const ShadowView& parentShadowView,
630637
TinyMap<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
631638
const ShadowViewNodePair& node,
639+
const ShadowView& parentShadowViewForUpdate,
632640
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
633641
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes) {
634642
DEBUG_LOGS({
@@ -838,11 +846,15 @@ static void calculateShadowViewMutationsFlattener(
838846
// ShadowNode.
839847
if (newTreeNodePair.shadowView != oldTreeNodePair.shadowView &&
840848
newTreeNodePair.isConcreteView && oldTreeNodePair.isConcreteView) {
849+
// We execute updates before creates, so pass the current parent in when
850+
// unflattening.
851+
// TODO: whenever we insert, we already update the relevant properties,
852+
// so this update is redundant. We should remove this.
841853
mutationContainer.updateMutations.push_back(
842854
ShadowViewMutation::UpdateMutation(
843855
oldTreeNodePair.shadowView,
844856
newTreeNodePair.shadowView,
845-
node.shadowView));
857+
parentShadowViewForUpdate));
846858
}
847859

848860
// Update children if appropriate.
@@ -877,6 +889,9 @@ static void calculateShadowViewMutationsFlattener(
877889
: newTreeNodePair.shadowView),
878890
unvisitedOtherNodes,
879891
treeChildPair,
892+
(reparentMode == ReparentMode::Flatten
893+
? oldTreeNodePair.shadowView
894+
: parentShadowView),
880895
subVisitedNewMap,
881896
subVisitedOldMap);
882897
} else {
@@ -918,6 +933,9 @@ static void calculateShadowViewMutationsFlattener(
918933
: newTreeNodePair.shadowView),
919934
unvisitedRecursiveChildPairs,
920935
oldTreeNodePair,
936+
(reparentMode == ReparentMode::Flatten
937+
? oldTreeNodePair.shadowView
938+
: parentShadowView),
921939
subVisitedNewMap,
922940
subVisitedOldMap);
923941
}
@@ -933,6 +951,9 @@ static void calculateShadowViewMutationsFlattener(
933951
: newTreeNodePair.shadowView),
934952
unvisitedRecursiveChildPairs,
935953
newTreeNodePair,
954+
(reparentMode == ReparentMode::Flatten
955+
? oldTreeNodePair.shadowView
956+
: parentShadowView),
936957
subVisitedNewMap,
937958
subVisitedOldMap);
938959

packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubViewTree.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ void StubViewTree::mutate(const ShadowViewMutationList& mutations) {
221221
react_native_assert(hasTag(mutation.newChildShadowView.tag));
222222
auto oldStubView = registry_[mutation.newChildShadowView.tag];
223223
react_native_assert(oldStubView->tag != 0);
224+
if (mutation.parentShadowView.tag != 0) {
225+
react_native_assert(hasTag(mutation.parentShadowView.tag));
226+
react_native_assert(
227+
oldStubView->parentTag == mutation.parentShadowView.tag);
228+
}
224229
if ((ShadowView)(*oldStubView) != mutation.oldChildShadowView) {
225230
LOG(ERROR)
226231
<< "StubView: ASSERT FAILURE: UPDATE mutation assertion failure: oldChildShadowView does not match oldStubView: ["

packages/react-native/ReactCommon/react/renderer/mounting/tests/MountingTest.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,7 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) {
558558
EXPECT_EQ(mutations1[0].oldChildShadowView.tag, childG->getTag());
559559
EXPECT_EQ(mutations1[1].type, ShadowViewMutation::Update);
560560
EXPECT_EQ(mutations1[1].oldChildShadowView.tag, reparentedViewA->getTag());
561-
// This is incorrect! ChildH does not exist yet at this point
562-
EXPECT_EQ(mutations1[1].parentShadowView.tag, childH->getTag());
561+
EXPECT_EQ(mutations1[1].parentShadowView.tag, childG->getTag());
563562
EXPECT_EQ(mutations1[2].type, ShadowViewMutation::Remove);
564563
EXPECT_EQ(mutations1[2].oldChildShadowView.tag, reparentedViewA->getTag());
565564
EXPECT_EQ(mutations1[3].type, ShadowViewMutation::Create);

0 commit comments

Comments
 (0)