@samitouri / QOS-React / commits / 16fb2b6f9e

Moved resetChildLanes into complete work (#19836)

This allows us to inline a few checks that are specific to a certain tag-type.

Brian Vaughn committed Sep 15, 2020 at 17:12 UTC 16fb2b6f9e8fb17e2c468384e870cab635e4b1bb
2 files changed +198 -156
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+197 -5
@@ -8,7 +8,7 @@
8 */
9
10 import type {Fiber} from './ReactInternalTypes';
11 -import type {Lanes} from './ReactFiberLane';
11 +import type {Lanes, Lane} from './ReactFiberLane';
12 import type {
13 ReactFundamentalComponentInstance,
14 ReactScopeInstance,
@@ -58,7 +58,12 @@ import {
58 OffscreenComponent,
59 LegacyHiddenComponent,
60 } from './ReactWorkTags';
61 -import {NoMode, BlockingMode, ProfileMode} from './ReactTypeOfMode';
61 +import {
62 + NoMode,
63 + BlockingMode,
64 + ConcurrentMode,
65 + ProfileMode,
66 +} from './ReactTypeOfMode';
67 import {
68 Ref,
69 Update,
@@ -66,6 +71,7 @@ import {
71 DidCapture,
72 Snapshot,
73 MutationMask,
74 + StaticMask,
75 } from './ReactFiberFlags';
76 import invariant from 'shared/invariant';
77
@@ -137,9 +143,16 @@ import {
143 renderHasNotSuspendedYet,
144 popRenderLanes,
145 getRenderTargetTime,
146 + subtreeRenderLanes,
147 } from './ReactFiberWorkLoop.new';
148 import {createFundamentalStateInstance} from './ReactFiberFundamental.new';
142 -import {OffscreenLane, SomeRetryLane} from './ReactFiberLane';
149 +import {
150 + OffscreenLane,
151 + SomeRetryLane,
152 + NoLanes,
153 + includesSomeLane,
154 + mergeLanes,
155 +} from './ReactFiberLane';
156 import {resetChildFibers} from './ReactChildFiber.new';
157 import {createScopeInstance} from './ReactFiberScope.new';
158 import {transferActualDuration} from './ReactProfilerTimer.new';
@@ -668,6 +681,114 @@ function cutOffTailIfNeeded(
681 }
682 }
683
684 +function bubbleProperties(completedWork: Fiber) {
685 + const didBailout =
686 + completedWork.alternate !== null &&
687 + completedWork.alternate.child === completedWork.child;
688 +
689 + let newChildLanes = NoLanes;
690 + let subtreeFlags = NoFlags;
691 +
692 + if (!didBailout) {
693 + // Bubble up the earliest expiration time.
694 + if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
695 + // In profiling mode, resetChildExpirationTime is also used to reset
696 + // profiler durations.
697 + let actualDuration = completedWork.actualDuration;
698 + let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
699 +
700 + let child = completedWork.child;
701 + while (child !== null) {
702 + newChildLanes = mergeLanes(
703 + newChildLanes,
704 + mergeLanes(child.lanes, child.childLanes),
705 + );
706 +
707 + subtreeFlags |= child.subtreeFlags;
708 + subtreeFlags |= child.flags;
709 +
710 + // When a fiber is cloned, its actualDuration is reset to 0. This value will
711 + // only be updated if work is done on the fiber (i.e. it doesn't bailout).
712 + // When work is done, it should bubble to the parent's actualDuration. If
713 + // the fiber has not been cloned though, (meaning no work was done), then
714 + // this value will reflect the amount of time spent working on a previous
715 + // render. In that case it should not bubble. We determine whether it was
716 + // cloned by comparing the child pointer.
717 + actualDuration += child.actualDuration;
718 +
719 + treeBaseDuration += child.treeBaseDuration;
720 + child = child.sibling;
721 + }
722 +
723 + completedWork.actualDuration = actualDuration;
724 + completedWork.treeBaseDuration = treeBaseDuration;
725 + } else {
726 + let child = completedWork.child;
727 + while (child !== null) {
728 + newChildLanes = mergeLanes(
729 + newChildLanes,
730 + mergeLanes(child.lanes, child.childLanes),
731 + );
732 +
733 + subtreeFlags |= child.subtreeFlags;
734 + subtreeFlags |= child.flags;
735 +
736 + child = child.sibling;
737 + }
738 + }
739 +
740 + completedWork.subtreeFlags |= subtreeFlags;
741 + } else {
742 + // Bubble up the earliest expiration time.
743 + if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
744 + // In profiling mode, resetChildExpirationTime is also used to reset
745 + // profiler durations.
746 + let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
747 +
748 + let child = completedWork.child;
749 + while (child !== null) {
750 + newChildLanes = mergeLanes(
751 + newChildLanes,
752 + mergeLanes(child.lanes, child.childLanes),
753 + );
754 +
755 + // "Static" flags share the lifetime of the fiber/hook they belong to,
756 + // so we should bubble those up even during a bailout. All the other
757 + // flags have a lifetime only of a single render + commit, so we should
758 + // ignore them.
759 + subtreeFlags |= child.subtreeFlags & StaticMask;
760 + subtreeFlags |= child.flags & StaticMask;
761 +
762 + treeBaseDuration += child.treeBaseDuration;
763 + child = child.sibling;
764 + }
765 +
766 + completedWork.treeBaseDuration = treeBaseDuration;
767 + } else {
768 + let child = completedWork.child;
769 + while (child !== null) {
770 + newChildLanes = mergeLanes(
771 + newChildLanes,
772 + mergeLanes(child.lanes, child.childLanes),
773 + );
774 +
775 + // "Static" flags share the lifetime of the fiber/hook they belong to,
776 + // so we should bubble those up even during a bailout. All the other
777 + // flags have a lifetime only of a single render + commit, so we should
778 + // ignore them.
779 + subtreeFlags |= child.subtreeFlags & StaticMask;
780 + subtreeFlags |= child.flags & StaticMask;
781 +
782 + child = child.sibling;
783 + }
784 + }
785 +
786 + completedWork.subtreeFlags |= subtreeFlags;
787 + }
788 +
789 + completedWork.childLanes = newChildLanes;
790 +}
791 +
792 function completeWork(
793 current: Fiber | null,
794 workInProgress: Fiber,
@@ -686,12 +807,14 @@ function completeWork(
807 case Profiler:
808 case ContextConsumer:
809 case MemoComponent:
810 + bubbleProperties(workInProgress);
811 return null;
812 case ClassComponent: {
813 const Component = workInProgress.type;
814 if (isLegacyContextProvider(Component)) {
815 popLegacyContext(workInProgress);
816 }
817 + bubbleProperties(workInProgress);
818 return null;
819 }
820 case HostRoot: {
@@ -720,6 +843,7 @@ function completeWork(
843 }
844 }
845 updateHostContainer(current, workInProgress);
846 + bubbleProperties(workInProgress);
847 return null;
848 }
849 case HostComponent: {
@@ -746,6 +870,7 @@ function completeWork(
870 'caused by a bug in React. Please file an issue.',
871 );
872 // This can happen when we abort work.
873 + bubbleProperties(workInProgress);
874 return null;
875 }
876
@@ -803,6 +928,7 @@ function completeWork(
928 markRef(workInProgress);
929 }
930 }
931 + bubbleProperties(workInProgress);
932 return null;
933 }
934 case HostText: {
@@ -837,6 +963,7 @@ function completeWork(
963 );
964 }
965 }
966 + bubbleProperties(workInProgress);
967 return null;
968 }
969 case SuspenseComponent: {
@@ -856,6 +983,20 @@ function completeWork(
983 if (enableSchedulerTracing) {
984 markSpawnedWork(OffscreenLane);
985 }
986 + bubbleProperties(workInProgress);
987 + if (enableProfilerTimer) {
988 + if ((workInProgress.mode & ProfileMode) !== NoMode) {
989 + const isTimedOutSuspense = nextState !== null;
990 + if (isTimedOutSuspense) {
991 + // Don't count time spent in a timed out Suspense subtree as part of the base duration.
992 + const primaryChildFragment = workInProgress.child;
993 + if (primaryChildFragment !== null) {
994 + // $FlowFixMe Flow doens't support type casting in combiation with the -= operator
995 + workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
996 + }
997 + }
998 + }
999 + }
1000 return null;
1001 } else {
1002 // We should never have been in a hydration state if we didn't have a current.
@@ -872,6 +1013,20 @@ function completeWork(
1013 // If something suspended, schedule an effect to attach retry listeners.
1014 // So we might as well always mark this.
1015 workInProgress.flags |= Update;
1016 + bubbleProperties(workInProgress);
1017 + if (enableProfilerTimer) {
1018 + if ((workInProgress.mode & ProfileMode) !== NoMode) {
1019 + const isTimedOutSuspense = nextState !== null;
1020 + if (isTimedOutSuspense) {
1021 + // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1022 + const primaryChildFragment = workInProgress.child;
1023 + if (primaryChildFragment !== null) {
1024 + // $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1025 + workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1026 + }
1027 + }
1028 + }
1029 + }
1030 return null;
1031 }
1032 }
@@ -887,6 +1042,7 @@ function completeWork(
1042 ) {
1043 transferActualDuration(workInProgress);
1044 }
1045 + // Don't bubble properties in this case.
1046 return workInProgress;
1047 }
1048
@@ -964,6 +1120,19 @@ function completeWork(
1120 // Always notify the callback
1121 workInProgress.flags |= Update;
1122 }
1123 + bubbleProperties(workInProgress);
1124 + if (enableProfilerTimer) {
1125 + if ((workInProgress.mode & ProfileMode) !== NoMode) {
1126 + if (nextDidTimeout) {
1127 + // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1128 + const primaryChildFragment = workInProgress.child;
1129 + if (primaryChildFragment !== null) {
1130 + // $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1131 + workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1132 + }
1133 + }
1134 + }
1135 + }
1136 return null;
1137 }
1138 case HostPortal:
@@ -972,10 +1141,12 @@ function completeWork(
1141 if (current === null) {
1142 preparePortalMount(workInProgress.stateNode.containerInfo);
1143 }
1144 + bubbleProperties(workInProgress);
1145 return null;
1146 case ContextProvider:
1147 // Pop provider fiber
1148 popProvider(workInProgress);
1149 + bubbleProperties(workInProgress);
1150 return null;
1151 case IncompleteClassComponent: {
1152 // Same as class component case. I put it down here so that the tags are
@@ -984,6 +1155,7 @@ function completeWork(
1155 if (isLegacyContextProvider(Component)) {
1156 popLegacyContext(workInProgress);
1157 }
1158 + bubbleProperties(workInProgress);
1159 return null;
1160 }
1161 case SuspenseListComponent: {
@@ -995,6 +1167,7 @@ function completeWork(
1167 if (renderState === null) {
1168 // We're running in the default, "independent" mode.
1169 // We don't do anything in this mode.
1170 + bubbleProperties(workInProgress);
1171 return null;
1172 }
1173
@@ -1060,6 +1233,7 @@ function completeWork(
1233 ForceSuspenseFallback,
1234 ),
1235 );
1236 + // Don't bubble properties in this case.
1237 return workInProgress.child;
1238 }
1239 row = row.sibling;
@@ -1117,6 +1291,7 @@ function completeWork(
1291 !getIsHydrating() // We don't cut it if we're hydrating.
1292 ) {
1293 // We're done.
1294 + bubbleProperties(workInProgress);
1295 return null;
1296 }
1297 } else if (
@@ -1188,8 +1363,10 @@ function completeWork(
1363 }
1364 pushSuspenseContext(workInProgress, suspenseContext);
1365 // Do a pass over the next row.
1366 + // Don't bubble properties in this case.
1367 return next;
1368 }
1369 + bubbleProperties(workInProgress);
1370 return null;
1371 }
1372 case FundamentalComponent: {
@@ -1217,6 +1394,7 @@ function completeWork(
1394 ): any): Instance);
1395 fundamentalInstance.instance = instance;
1396 if (fundamentalImpl.reconcileChildren === false) {
1397 + bubbleProperties(workInProgress);
1398 return null;
1399 }
1400 appendAllChildren(instance, workInProgress, false, false);
@@ -1239,6 +1417,7 @@ function completeWork(
1417 markUpdate(workInProgress);
1418 }
1419 }
1420 + bubbleProperties(workInProgress);
1421 return null;
1422 }
1423 break;
@@ -1261,24 +1440,27 @@ function completeWork(
1440 markRef(workInProgress);
1441 }
1442 }
1443 + bubbleProperties(workInProgress);
1444 return null;
1445 }
1446 break;
1447 }
1448 case Block:
1449 if (enableBlocksAPI) {
1450 + bubbleProperties(workInProgress);
1451 return null;
1452 }
1453 break;
1454 case OffscreenComponent:
1455 case LegacyHiddenComponent: {
1456 popRenderLanes(workInProgress);
1457 + const nextState: OffscreenState | null = workInProgress.memoizedState;
1458 + const nextIsHidden = nextState !== null;
1459 +
1460 if (current !== null) {
1277 - const nextState: OffscreenState | null = workInProgress.memoizedState;
1461 const prevState: OffscreenState | null = current.memoizedState;
1462
1463 const prevIsHidden = prevState !== null;
1281 - const nextIsHidden = nextState !== null;
1464 if (
1465 prevIsHidden !== nextIsHidden &&
1466 newProps.mode !== 'unstable-defer-without-hiding'
@@ -1286,6 +1468,16 @@ function completeWork(
1468 workInProgress.flags |= Update;
1469 }
1470 }
1471 +
1472 + // Don't bubble properties for hidden children.
1473 + if (
1474 + !nextIsHidden ||
1475 + includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) ||
1476 + (workInProgress.mode & ConcurrentMode) === NoMode
1477 + ) {
1478 + bubbleProperties(workInProgress);
1479 + }
1480 +
1481 return null;
1482 }
1483 }
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+1 -151
@@ -115,8 +115,6 @@ import {
115 MemoComponent,
116 SimpleMemoComponent,
117 Block,
118 - OffscreenComponent,
119 - LegacyHiddenComponent,
118 ScopeComponent,
119 } from './ReactWorkTags';
120 import {LegacyRoot} from './ReactRootTags';
@@ -139,7 +137,6 @@ import {
137 MutationMask,
138 LayoutMask,
139 PassiveMask,
142 - StaticMask,
140 } from './ReactFiberFlags';
141 import {
142 NoLanePriority,
@@ -151,7 +148,6 @@ import {
148 NoLane,
149 SyncLane,
150 SyncBatchedLane,
154 - OffscreenLane,
151 NoTimestamp,
152 findUpdateLane,
153 findTransitionLane,
@@ -290,7 +286,7 @@ let workInProgressRootRenderLanes: Lanes = NoLanes;
286 //
287 // Most things in the work loop should deal with workInProgressRootRenderLanes.
288 // Most things in begin/complete phases should deal with subtreeRenderLanes.
293 -let subtreeRenderLanes: Lanes = NoLanes;
289 +export let subtreeRenderLanes: Lanes = NoLanes;
290 const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
291
292 // Whether to root completed, errored, suspended, etc.
@@ -1719,8 +1715,6 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
1715 workInProgress = next;
1716 return;
1717 }
1722 -
1723 - resetChildLanes(completedWork);
1718 } else {
1719 // This fiber did not complete because something threw. Pop values off
1720 // the stack without entering the complete phase. If this is a boundary,
@@ -1782,150 +1776,6 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
1776 }
1777 }
1778
1785 -function resetChildLanes(completedWork: Fiber) {
1786 - if (
1787 - // TODO: Move this check out of the hot path by moving `resetChildLanes`
1788 - // to switch statement in `completeWork`.
1789 - (completedWork.tag === LegacyHiddenComponent ||
1790 - completedWork.tag === OffscreenComponent) &&
1791 - completedWork.memoizedState !== null &&
1792 - !includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) &&
1793 - (completedWork.mode & ConcurrentMode) !== NoLanes
1794 - ) {
1795 - // The children of this component are hidden. Don't bubble their
1796 - // expiration times.
1797 - return;
1798 - }
1799 -
1800 - const didBailout =
1801 - completedWork.alternate !== null &&
1802 - completedWork.alternate.child === completedWork.child;
1803 -
1804 - let newChildLanes = NoLanes;
1805 - let subtreeFlags = NoFlags;
1806 -
1807 - if (!didBailout) {
1808 - // Bubble up the earliest expiration time.
1809 - if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
1810 - // In profiling mode, resetChildExpirationTime is also used to reset
1811 - // profiler durations.
1812 - let actualDuration = completedWork.actualDuration;
1813 - let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
1814 -
1815 - let child = completedWork.child;
1816 - while (child !== null) {
1817 - newChildLanes = mergeLanes(
1818 - newChildLanes,
1819 - mergeLanes(child.lanes, child.childLanes),
1820 - );
1821 -
1822 - subtreeFlags |= child.subtreeFlags;
1823 - subtreeFlags |= child.flags;
1824 -
1825 - // When a fiber is cloned, its actualDuration is reset to 0. This value will
1826 - // only be updated if work is done on the fiber (i.e. it doesn't bailout).
1827 - // When work is done, it should bubble to the parent's actualDuration. If
1828 - // the fiber has not been cloned though, (meaning no work was done), then
1829 - // this value will reflect the amount of time spent working on a previous
1830 - // render. In that case it should not bubble. We determine whether it was
1831 - // cloned by comparing the child pointer.
1832 - actualDuration += child.actualDuration;
1833 -
1834 - treeBaseDuration += child.treeBaseDuration;
1835 - child = child.sibling;
1836 - }
1837 -
1838 - const isTimedOutSuspense =
1839 - completedWork.tag === SuspenseComponent &&
1840 - completedWork.memoizedState !== null;
1841 - if (isTimedOutSuspense) {
1842 - // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1843 - const primaryChildFragment = completedWork.child;
1844 - if (primaryChildFragment !== null) {
1845 - treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1846 - }
1847 - }
1848 -
1849 - completedWork.actualDuration = actualDuration;
1850 - completedWork.treeBaseDuration = treeBaseDuration;
1851 - } else {
1852 - let child = completedWork.child;
1853 - while (child !== null) {
1854 - newChildLanes = mergeLanes(
1855 - newChildLanes,
1856 - mergeLanes(child.lanes, child.childLanes),
1857 - );
1858 -
1859 - subtreeFlags |= child.subtreeFlags;
1860 - subtreeFlags |= child.flags;
1861 -
1862 - child = child.sibling;
1863 - }
1864 - }
1865 -
1866 - completedWork.subtreeFlags |= subtreeFlags;
1867 - } else {
1868 - // Bubble up the earliest expiration time.
1869 - if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
1870 - // In profiling mode, resetChildExpirationTime is also used to reset
1871 - // profiler durations.
1872 - let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
1873 -
1874 - let child = completedWork.child;
1875 - while (child !== null) {
1876 - newChildLanes = mergeLanes(
1877 - newChildLanes,
1878 - mergeLanes(child.lanes, child.childLanes),
1879 - );
1880 -
1881 - // "Static" flags share the lifetime of the fiber/hook they belong to,
1882 - // so we should bubble those up even during a bailout. All the other
1883 - // flags have a lifetime only of a single render + commit, so we should
1884 - // ignore them.
1885 - subtreeFlags |= child.subtreeFlags & StaticMask;
1886 - subtreeFlags |= child.flags & StaticMask;
1887 -
1888 - treeBaseDuration += child.treeBaseDuration;
1889 - child = child.sibling;
1890 - }
1891 -
1892 - const isTimedOutSuspense =
1893 - completedWork.tag === SuspenseComponent &&
1894 - completedWork.memoizedState !== null;
1895 - if (isTimedOutSuspense) {
1896 - // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1897 - const primaryChildFragment = completedWork.child;
1898 - if (primaryChildFragment !== null) {
1899 - treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1900 - }
1901 - }
1902 -
1903 - completedWork.treeBaseDuration = treeBaseDuration;
1904 - } else {
1905 - let child = completedWork.child;
1906 - while (child !== null) {
1907 - newChildLanes = mergeLanes(
1908 - newChildLanes,
1909 - mergeLanes(child.lanes, child.childLanes),
1910 - );
1911 -
1912 - // "Static" flags share the lifetime of the fiber/hook they belong to,
1913 - // so we should bubble those up even during a bailout. All the other
1914 - // flags have a lifetime only of a single render + commit, so we should
1915 - // ignore them.
1916 - subtreeFlags |= child.subtreeFlags & StaticMask;
1917 - subtreeFlags |= child.flags & StaticMask;
1918 -
1919 - child = child.sibling;
1920 - }
1921 - }
1922 -
1923 - completedWork.subtreeFlags |= subtreeFlags;
1924 - }
1925 -
1926 - completedWork.childLanes = newChildLanes;
1927 -}
1928 -
1779 function commitRoot(root) {
1780 const renderPriorityLevel = getCurrentPriorityLevel();
1781 runWithPriority(