From 88ef95712d315705b2d6654f3c0d5c5ace4a6456 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Fri, 4 Dec 2020 10:54:09 -0600 Subject: [PATCH] Fork ReactFiberLane (#20371) This wasn't forked previously because Lane and associated types are opaque, and they leak into non-reconciler packages. So forking the type would also require forking all those other packages. But I really want to use the reconciler fork infra for lanes changes. So I made them no longer opaque. Another possible solution would be to add separate `new` and `old` fields to the Fiber type, like I did when migrating from expiration times. But that seems so excessive. This seems fine. But we should still treat them like they're opaque and only do lanes manipulation in the ReactFiberLane module. At least until the model stabilizes more. We'll just need to enforce this with discipline instead of with the type system. --- .../src/events/ReactDOMEventListener.js | 25 +- .../src/events/ReactDOMEventReplaying.js | 2 +- packages/react-reconciler/src/DebugTracing.js | 2 +- .../src/ReactChildFiber.new.js | 2 +- .../src/ReactChildFiber.old.js | 2 +- .../react-reconciler/src/ReactFiber.new.js | 4 +- .../react-reconciler/src/ReactFiber.old.js | 4 +- .../src/ReactFiberBeginWork.new.js | 4 +- .../src/ReactFiberBeginWork.old.js | 4 +- .../src/ReactFiberClassComponent.new.js | 4 +- .../src/ReactFiberClassComponent.old.js | 4 +- .../src/ReactFiberCommitWork.new.js | 2 +- .../src/ReactFiberCommitWork.old.js | 2 +- .../src/ReactFiberCompleteWork.new.js | 4 +- .../src/ReactFiberCompleteWork.old.js | 4 +- .../src/ReactFiberHooks.new.js | 4 +- .../src/ReactFiberHooks.old.js | 4 +- .../src/ReactFiberHotReloading.new.js | 2 +- .../src/ReactFiberHotReloading.old.js | 2 +- .../src/ReactFiberHydrationContext.new.js | 2 +- .../src/ReactFiberHydrationContext.old.js | 2 +- ...eactFiberLane.js => ReactFiberLane.new.js} | 11 +- .../src/ReactFiberLane.old.js | 845 ++++++++++++++++++ .../src/ReactFiberNewContext.new.js | 4 +- .../src/ReactFiberNewContext.old.js | 4 +- .../src/ReactFiberOffscreenComponent.js | 2 +- .../src/ReactFiberReconciler.new.js | 4 +- .../src/ReactFiberReconciler.old.js | 4 +- .../src/ReactFiberRoot.new.js | 2 +- .../src/ReactFiberRoot.old.js | 2 +- .../src/ReactFiberSuspenseComponent.new.js | 2 +- .../src/ReactFiberSuspenseComponent.old.js | 2 +- .../src/ReactFiberThrow.new.js | 4 +- .../src/ReactFiberThrow.old.js | 4 +- .../src/ReactFiberUnwindWork.new.js | 2 +- .../src/ReactFiberUnwindWork.old.js | 2 +- .../src/ReactFiberWorkLoop.new.js | 4 +- .../src/ReactFiberWorkLoop.old.js | 4 +- .../src/ReactInternalTypes.js | 2 +- .../src/ReactUpdateQueue.new.js | 9 +- .../src/ReactUpdateQueue.old.js | 9 +- .../src/SchedulerWithReactIntegration.new.js | 2 +- .../src/SchedulerWithReactIntegration.old.js | 2 +- .../src/SchedulingProfiler.js | 2 +- 44 files changed, 944 insertions(+), 69 deletions(-) rename packages/react-reconciler/src/{ReactFiberLane.js => ReactFiberLane.new.js} (99%) create mode 100644 packages/react-reconciler/src/ReactFiberLane.old.js diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index 9a41c6c1317f..dcb2cfb9fd70 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -41,6 +41,7 @@ import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree'; import { enableLegacyFBSupport, decoupleUpdatePriorityFromScheduler, + enableNewReconciler, } from 'shared/ReactFeatureFlags'; import { UserBlockingEvent, @@ -53,11 +54,27 @@ import { flushDiscreteUpdatesIfNeeded, discreteUpdates, } from './ReactDOMUpdateBatching'; + +import { + InputContinuousLanePriority as InputContinuousLanePriority_old, + getCurrentUpdateLanePriority as getCurrentUpdateLanePriority_old, + setCurrentUpdateLanePriority as setCurrentUpdateLanePriority_old, +} from 'react-reconciler/src/ReactFiberLane.old'; import { - InputContinuousLanePriority, - getCurrentUpdateLanePriority, - setCurrentUpdateLanePriority, -} from 'react-reconciler/src/ReactFiberLane'; + InputContinuousLanePriority as InputContinuousLanePriority_new, + getCurrentUpdateLanePriority as getCurrentUpdateLanePriority_new, + setCurrentUpdateLanePriority as setCurrentUpdateLanePriority_new, +} from 'react-reconciler/src/ReactFiberLane.new'; + +const InputContinuousLanePriority = enableNewReconciler + ? InputContinuousLanePriority_new + : InputContinuousLanePriority_old; +const getCurrentUpdateLanePriority = enableNewReconciler + ? getCurrentUpdateLanePriority_new + : getCurrentUpdateLanePriority_old; +const setCurrentUpdateLanePriority = enableNewReconciler + ? setCurrentUpdateLanePriority_new + : setCurrentUpdateLanePriority_old; const { unstable_UserBlockingPriority: UserBlockingPriority, diff --git a/packages/react-dom/src/events/ReactDOMEventReplaying.js b/packages/react-dom/src/events/ReactDOMEventReplaying.js index 2fbe0e1b0b61..a56487c0928d 100644 --- a/packages/react-dom/src/events/ReactDOMEventReplaying.js +++ b/packages/react-dom/src/events/ReactDOMEventReplaying.js @@ -12,7 +12,7 @@ import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig'; import type {DOMEventName} from '../events/DOMEventNames'; import type {EventSystemFlags} from './EventSystemFlags'; import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; -import type {LanePriority} from 'react-reconciler/src/ReactFiberLane'; +import type {LanePriority} from 'react-reconciler/src/ReactFiberLane.old'; import {enableSelectiveHydration} from 'shared/ReactFeatureFlags'; import { diff --git a/packages/react-reconciler/src/DebugTracing.js b/packages/react-reconciler/src/DebugTracing.js index 075a705f03bd..ac13410c3623 100644 --- a/packages/react-reconciler/src/DebugTracing.js +++ b/packages/react-reconciler/src/DebugTracing.js @@ -7,7 +7,7 @@ * @flow */ -import type {Lane, Lanes} from './ReactFiberLane'; +import type {Lane, Lanes} from './ReactFiberLane.old'; import type {Wakeable} from 'shared/ReactTypes'; import {enableDebugTracing} from 'shared/ReactFeatureFlags'; diff --git a/packages/react-reconciler/src/ReactChildFiber.new.js b/packages/react-reconciler/src/ReactChildFiber.new.js index 534d7032ba2d..52bd63272118 100644 --- a/packages/react-reconciler/src/ReactChildFiber.new.js +++ b/packages/react-reconciler/src/ReactChildFiber.new.js @@ -10,7 +10,7 @@ import type {ReactElement} from 'shared/ReactElementType'; import type {ReactPortal} from 'shared/ReactTypes'; import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import getComponentName from 'shared/getComponentName'; import {Placement, Deletion} from './ReactFiberFlags'; diff --git a/packages/react-reconciler/src/ReactChildFiber.old.js b/packages/react-reconciler/src/ReactChildFiber.old.js index df4c2e10d929..7313d78f0bf3 100644 --- a/packages/react-reconciler/src/ReactChildFiber.old.js +++ b/packages/react-reconciler/src/ReactChildFiber.old.js @@ -10,7 +10,7 @@ import type {ReactElement} from 'shared/ReactElementType'; import type {ReactPortal} from 'shared/ReactTypes'; import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import getComponentName from 'shared/getComponentName'; import {Placement, Deletion} from './ReactFiberFlags'; diff --git a/packages/react-reconciler/src/ReactFiber.new.js b/packages/react-reconciler/src/ReactFiber.new.js index 5426568b9661..edeae5caa6fe 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -18,7 +18,7 @@ import type {Fiber} from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {OffscreenProps} from './ReactFiberOffscreenComponent'; @@ -63,7 +63,7 @@ import { resolveFunctionForHotReloading, resolveForwardRefForHotReloading, } from './ReactFiberHotReloading.new'; -import {NoLanes} from './ReactFiberLane'; +import {NoLanes} from './ReactFiberLane.new'; import { NoMode, ConcurrentMode, diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index 56ef2f5a528a..8e3df44ac676 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -18,7 +18,7 @@ import type {Fiber} from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {OffscreenProps} from './ReactFiberOffscreenComponent'; @@ -63,7 +63,7 @@ import { resolveFunctionForHotReloading, resolveForwardRefForHotReloading, } from './ReactFiberHotReloading.old'; -import {NoLanes} from './ReactFiberLane'; +import {NoLanes} from './ReactFiberLane.old'; import { NoMode, ConcurrentMode, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 52bbc5ac405b..99aa9193e2f2 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -11,7 +11,7 @@ import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.new'; import type {MutableSource} from 'shared/ReactTypes'; import type { SuspenseState, @@ -113,7 +113,7 @@ import { removeLanes, mergeLanes, getBumpedLaneForHydration, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; import { ConcurrentMode, NoMode, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index e12f7f300b47..f875db594495 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -11,7 +11,7 @@ import type {ReactProviderType, ReactContext} from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.old'; import type {MutableSource} from 'shared/ReactTypes'; import type { SuspenseState, @@ -113,7 +113,7 @@ import { removeLanes, mergeLanes, getBumpedLaneForHydration, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; import { ConcurrentMode, NoMode, diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.new.js b/packages/react-reconciler/src/ReactFiberClassComponent.new.js index 4e58dd71f3dc..e807ae8222a2 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.new.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import type {UpdateQueue} from './ReactUpdateQueue.new'; import * as React from 'react'; @@ -42,7 +42,7 @@ import { initializeUpdateQueue, cloneUpdateQueue, } from './ReactUpdateQueue.new'; -import {NoLanes} from './ReactFiberLane'; +import {NoLanes} from './ReactFiberLane.new'; import { cacheContext, getMaskedContext, diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.old.js b/packages/react-reconciler/src/ReactFiberClassComponent.old.js index c5f352741949..f055bac71ca9 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.old.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import type {UpdateQueue} from './ReactUpdateQueue.old'; import * as React from 'react'; @@ -42,7 +42,7 @@ import { initializeUpdateQueue, cloneUpdateQueue, } from './ReactUpdateQueue.old'; -import {NoLanes} from './ReactFiberLane'; +import {NoLanes} from './ReactFiberLane.old'; import { cacheContext, getMaskedContext, diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index f16fb9943551..a9c54d9c02e9 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -17,7 +17,7 @@ import type { } from './ReactFiberHostConfig'; import type {Fiber} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; import type {UpdateQueue} from './ReactUpdateQueue.new'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new'; diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index 55db13cf3988..c134ecccddbb 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -17,7 +17,7 @@ import type { } from './ReactFiberHostConfig'; import type {Fiber} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; import type {UpdateQueue} from './ReactUpdateQueue.old'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old'; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index 89d2193176c2..9d91de82073c 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import type { ReactFundamentalComponentInstance, ReactScopeInstance, @@ -130,7 +130,7 @@ import { getRenderTargetTime, } from './ReactFiberWorkLoop.new'; import {createFundamentalStateInstance} from './ReactFiberFundamental.new'; -import {OffscreenLane, SomeRetryLane} from './ReactFiberLane'; +import {OffscreenLane, SomeRetryLane} from './ReactFiberLane.new'; import {resetChildFibers} from './ReactChildFiber.new'; import {createScopeInstance} from './ReactFiberScope.new'; import {transferActualDuration} from './ReactProfilerTimer.new'; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js index 21ffea1c92dd..6f1942f7b90b 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import type { ReactFundamentalComponentInstance, ReactScopeInstance, @@ -130,7 +130,7 @@ import { getRenderTargetTime, } from './ReactFiberWorkLoop.old'; import {createFundamentalStateInstance} from './ReactFiberFundamental.old'; -import {OffscreenLane, SomeRetryLane} from './ReactFiberLane'; +import {OffscreenLane, SomeRetryLane} from './ReactFiberLane.old'; import {resetChildFibers} from './ReactChildFiber.old'; import {createScopeInstance} from './ReactFiberScope.old'; import {transferActualDuration} from './ReactProfilerTimer.old'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index f257657d2b55..4a0a9231f044 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -14,7 +14,7 @@ import type { ReactContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.new'; import type {HookFlags} from './ReactHookEffectTags'; import type {ReactPriorityLevel} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; @@ -44,7 +44,7 @@ import { setCurrentUpdateLanePriority, higherLanePriority, DefaultLanePriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; import {readContext} from './ReactFiberNewContext.new'; import { Update as UpdateEffect, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 9fa15c5c0655..0a7a99079c96 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -14,7 +14,7 @@ import type { ReactContext, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.old'; import type {HookFlags} from './ReactHookEffectTags'; import type {ReactPriorityLevel} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; @@ -44,7 +44,7 @@ import { setCurrentUpdateLanePriority, higherLanePriority, DefaultLanePriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; import {readContext} from './ReactFiberNewContext.old'; import { Update as UpdateEffect, diff --git a/packages/react-reconciler/src/ReactFiberHotReloading.new.js b/packages/react-reconciler/src/ReactFiberHotReloading.new.js index ed962e9d3266..4c9eaf010125 100644 --- a/packages/react-reconciler/src/ReactFiberHotReloading.new.js +++ b/packages/react-reconciler/src/ReactFiberHotReloading.new.js @@ -20,7 +20,7 @@ import { } from './ReactFiberWorkLoop.new'; import {updateContainer} from './ReactFiberReconciler.new'; import {emptyContextObject} from './ReactFiberContext.new'; -import {SyncLane, NoTimestamp} from './ReactFiberLane'; +import {SyncLane, NoTimestamp} from './ReactFiberLane.new'; import { ClassComponent, FunctionComponent, diff --git a/packages/react-reconciler/src/ReactFiberHotReloading.old.js b/packages/react-reconciler/src/ReactFiberHotReloading.old.js index 00db4f034e86..ee0616fae79c 100644 --- a/packages/react-reconciler/src/ReactFiberHotReloading.old.js +++ b/packages/react-reconciler/src/ReactFiberHotReloading.old.js @@ -20,7 +20,7 @@ import { } from './ReactFiberWorkLoop.old'; import {updateContainer} from './ReactFiberReconciler.old'; import {emptyContextObject} from './ReactFiberContext.old'; -import {SyncLane, NoTimestamp} from './ReactFiberLane'; +import {SyncLane, NoTimestamp} from './ReactFiberLane.old'; import { ClassComponent, FunctionComponent, diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.new.js b/packages/react-reconciler/src/ReactFiberHydrationContext.new.js index e90bc9768a9a..6ed0f6c8cbc5 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.new.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.new.js @@ -55,7 +55,7 @@ import { didNotFindHydratableSuspenseInstance, } from './ReactFiberHostConfig'; import {enableSuspenseServerRenderer} from 'shared/ReactFeatureFlags'; -import {OffscreenLane} from './ReactFiberLane'; +import {OffscreenLane} from './ReactFiberLane.new'; // The deepest Fiber on the stack involved in a hydration context. // This may have been an insertion or a hydration. diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.old.js b/packages/react-reconciler/src/ReactFiberHydrationContext.old.js index 7576bd5ac081..5d084bf97bad 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.old.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.old.js @@ -55,7 +55,7 @@ import { didNotFindHydratableSuspenseInstance, } from './ReactFiberHostConfig'; import {enableSuspenseServerRenderer} from 'shared/ReactFeatureFlags'; -import {OffscreenLane} from './ReactFiberLane'; +import {OffscreenLane} from './ReactFiberLane.old'; // The deepest Fiber on the stack involved in a hydration context. // This may have been an insertion or a hydration. diff --git a/packages/react-reconciler/src/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.new.js similarity index 99% rename from packages/react-reconciler/src/ReactFiberLane.js rename to packages/react-reconciler/src/ReactFiberLane.new.js index 027754dd76ee..b5d8846ec200 100644 --- a/packages/react-reconciler/src/ReactFiberLane.js +++ b/packages/react-reconciler/src/ReactFiberLane.new.js @@ -9,7 +9,9 @@ import type {FiberRoot, ReactPriorityLevel} from './ReactInternalTypes'; -export opaque type LanePriority = +// TODO: Ideally these types would be opaque but that doesn't work well with +// our reconciler fork infra, since these leak into non-reconciler packages. +export type LanePriority = | 0 | 1 | 2 @@ -28,9 +30,10 @@ export opaque type LanePriority = | 15 | 16 | 17; -export opaque type Lanes = number; -export opaque type Lane = number; -export opaque type LaneMap = Array; + +export type Lanes = number; +export type Lane = number; +export type LaneMap = Array; import invariant from 'shared/invariant'; diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js new file mode 100644 index 000000000000..f58ab2053174 --- /dev/null +++ b/packages/react-reconciler/src/ReactFiberLane.old.js @@ -0,0 +1,845 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type {FiberRoot, ReactPriorityLevel} from './ReactInternalTypes'; + +// TODO: Ideally these types would be opaque but that doesn't work well with +// our reconciler fork infra, since these leak into non-reconciler packages. +export type LanePriority = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17; + +export type Lanes = number; +export type Lane = number; +export type LaneMap = Array; + +import invariant from 'shared/invariant'; + +import { + ImmediatePriority as ImmediateSchedulerPriority, + UserBlockingPriority as UserBlockingSchedulerPriority, + NormalPriority as NormalSchedulerPriority, + LowPriority as LowSchedulerPriority, + IdlePriority as IdleSchedulerPriority, + NoPriority as NoSchedulerPriority, +} from './SchedulerWithReactIntegration.old'; + +export const SyncLanePriority: LanePriority = 15; +export const SyncBatchedLanePriority: LanePriority = 14; + +const InputDiscreteHydrationLanePriority: LanePriority = 13; +export const InputDiscreteLanePriority: LanePriority = 12; + +const InputContinuousHydrationLanePriority: LanePriority = 11; +export const InputContinuousLanePriority: LanePriority = 10; + +const DefaultHydrationLanePriority: LanePriority = 9; +export const DefaultLanePriority: LanePriority = 8; + +const TransitionHydrationPriority: LanePriority = 7; +export const TransitionPriority: LanePriority = 6; + +const RetryLanePriority: LanePriority = 5; + +const SelectiveHydrationLanePriority: LanePriority = 4; + +const IdleHydrationLanePriority: LanePriority = 3; +const IdleLanePriority: LanePriority = 2; + +const OffscreenLanePriority: LanePriority = 1; + +export const NoLanePriority: LanePriority = 0; + +const TotalLanes = 31; + +export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000; +export const NoLane: Lane = /* */ 0b0000000000000000000000000000000; + +export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001; +export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010; + +export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100; +const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011000; + +const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000100000; +const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011000000; + +export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000100000000; +export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111000000000; + +const TransitionHydrationLane: Lane = /* */ 0b0000000000000000001000000000000; +const TransitionLanes: Lanes = /* */ 0b0000000001111111110000000000000; + +const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000; + +export const SomeRetryLane: Lanes = /* */ 0b0000010000000000000000000000000; + +export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000; + +const NonIdleLanes = /* */ 0b0000111111111111111111111111111; + +export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000; +const IdleLanes: Lanes = /* */ 0b0110000000000000000000000000000; + +export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000; + +export const NoTimestamp = -1; + +let currentUpdateLanePriority: LanePriority = NoLanePriority; + +export function getCurrentUpdateLanePriority(): LanePriority { + return currentUpdateLanePriority; +} + +export function setCurrentUpdateLanePriority(newLanePriority: LanePriority) { + currentUpdateLanePriority = newLanePriority; +} + +// "Registers" used to "return" multiple values +// Used by getHighestPriorityLanes and getNextLanes: +let return_highestLanePriority: LanePriority = DefaultLanePriority; + +function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { + if ((SyncLane & lanes) !== NoLanes) { + return_highestLanePriority = SyncLanePriority; + return SyncLane; + } + if ((SyncBatchedLane & lanes) !== NoLanes) { + return_highestLanePriority = SyncBatchedLanePriority; + return SyncBatchedLane; + } + if ((InputDiscreteHydrationLane & lanes) !== NoLanes) { + return_highestLanePriority = InputDiscreteHydrationLanePriority; + return InputDiscreteHydrationLane; + } + const inputDiscreteLanes = InputDiscreteLanes & lanes; + if (inputDiscreteLanes !== NoLanes) { + return_highestLanePriority = InputDiscreteLanePriority; + return inputDiscreteLanes; + } + if ((lanes & InputContinuousHydrationLane) !== NoLanes) { + return_highestLanePriority = InputContinuousHydrationLanePriority; + return InputContinuousHydrationLane; + } + const inputContinuousLanes = InputContinuousLanes & lanes; + if (inputContinuousLanes !== NoLanes) { + return_highestLanePriority = InputContinuousLanePriority; + return inputContinuousLanes; + } + if ((lanes & DefaultHydrationLane) !== NoLanes) { + return_highestLanePriority = DefaultHydrationLanePriority; + return DefaultHydrationLane; + } + const defaultLanes = DefaultLanes & lanes; + if (defaultLanes !== NoLanes) { + return_highestLanePriority = DefaultLanePriority; + return defaultLanes; + } + if ((lanes & TransitionHydrationLane) !== NoLanes) { + return_highestLanePriority = TransitionHydrationPriority; + return TransitionHydrationLane; + } + const transitionLanes = TransitionLanes & lanes; + if (transitionLanes !== NoLanes) { + return_highestLanePriority = TransitionPriority; + return transitionLanes; + } + const retryLanes = RetryLanes & lanes; + if (retryLanes !== NoLanes) { + return_highestLanePriority = RetryLanePriority; + return retryLanes; + } + if (lanes & SelectiveHydrationLane) { + return_highestLanePriority = SelectiveHydrationLanePriority; + return SelectiveHydrationLane; + } + if ((lanes & IdleHydrationLane) !== NoLanes) { + return_highestLanePriority = IdleHydrationLanePriority; + return IdleHydrationLane; + } + const idleLanes = IdleLanes & lanes; + if (idleLanes !== NoLanes) { + return_highestLanePriority = IdleLanePriority; + return idleLanes; + } + if ((OffscreenLane & lanes) !== NoLanes) { + return_highestLanePriority = OffscreenLanePriority; + return OffscreenLane; + } + if (__DEV__) { + console.error('Should have found matching lanes. This is a bug in React.'); + } + // This shouldn't be reachable, but as a fallback, return the entire bitmask. + return_highestLanePriority = DefaultLanePriority; + return lanes; +} + +export function schedulerPriorityToLanePriority( + schedulerPriorityLevel: ReactPriorityLevel, +): LanePriority { + switch (schedulerPriorityLevel) { + case ImmediateSchedulerPriority: + return SyncLanePriority; + case UserBlockingSchedulerPriority: + return InputContinuousLanePriority; + case NormalSchedulerPriority: + case LowSchedulerPriority: + // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration. + return DefaultLanePriority; + case IdleSchedulerPriority: + return IdleLanePriority; + default: + return NoLanePriority; + } +} + +export function lanePriorityToSchedulerPriority( + lanePriority: LanePriority, +): ReactPriorityLevel { + switch (lanePriority) { + case SyncLanePriority: + case SyncBatchedLanePriority: + return ImmediateSchedulerPriority; + case InputDiscreteHydrationLanePriority: + case InputDiscreteLanePriority: + case InputContinuousHydrationLanePriority: + case InputContinuousLanePriority: + return UserBlockingSchedulerPriority; + case DefaultHydrationLanePriority: + case DefaultLanePriority: + case TransitionHydrationPriority: + case TransitionPriority: + case SelectiveHydrationLanePriority: + case RetryLanePriority: + return NormalSchedulerPriority; + case IdleHydrationLanePriority: + case IdleLanePriority: + case OffscreenLanePriority: + return IdleSchedulerPriority; + case NoLanePriority: + return NoSchedulerPriority; + default: + invariant( + false, + 'Invalid update priority: %s. This is a bug in React.', + lanePriority, + ); + } +} + +export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { + // Early bailout if there's no pending work left. + const pendingLanes = root.pendingLanes; + if (pendingLanes === NoLanes) { + return_highestLanePriority = NoLanePriority; + return NoLanes; + } + + let nextLanes = NoLanes; + let nextLanePriority = NoLanePriority; + + const expiredLanes = root.expiredLanes; + const suspendedLanes = root.suspendedLanes; + const pingedLanes = root.pingedLanes; + + // Check if any work has expired. + if (expiredLanes !== NoLanes) { + nextLanes = expiredLanes; + nextLanePriority = return_highestLanePriority = SyncLanePriority; + } else { + // Do not work on any idle work until all the non-idle work has finished, + // even if the work is suspended. + const nonIdlePendingLanes = pendingLanes & NonIdleLanes; + if (nonIdlePendingLanes !== NoLanes) { + const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; + if (nonIdleUnblockedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + nextLanePriority = return_highestLanePriority; + } else { + const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; + if (nonIdlePingedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); + nextLanePriority = return_highestLanePriority; + } + } + } else { + // The only remaining work is Idle. + const unblockedLanes = pendingLanes & ~suspendedLanes; + if (unblockedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(unblockedLanes); + nextLanePriority = return_highestLanePriority; + } else { + if (pingedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(pingedLanes); + nextLanePriority = return_highestLanePriority; + } + } + } + } + + if (nextLanes === NoLanes) { + // This should only be reachable if we're suspended + // TODO: Consider warning in this path if a fallback timer is not scheduled. + return NoLanes; + } + + // If there are higher priority lanes, we'll include them even if they + // are suspended. + nextLanes = pendingLanes & getEqualOrHigherPriorityLanes(nextLanes); + + // If we're already in the middle of a render, switching lanes will interrupt + // it and we'll lose our progress. We should only do this if the new lanes are + // higher priority. + if ( + wipLanes !== NoLanes && + wipLanes !== nextLanes && + // If we already suspended with a delay, then interrupting is fine. Don't + // bother waiting until the root is complete. + (wipLanes & suspendedLanes) === NoLanes + ) { + getHighestPriorityLanes(wipLanes); + const wipLanePriority = return_highestLanePriority; + if (nextLanePriority <= wipLanePriority) { + return wipLanes; + } else { + return_highestLanePriority = nextLanePriority; + } + } + + // Check for entangled lanes and add them to the batch. + // + // A lane is said to be entangled with another when it's not allowed to render + // in a batch that does not also include the other lane. Typically we do this + // when multiple updates have the same source, and we only want to respond to + // the most recent event from that source. + // + // Note that we apply entanglements *after* checking for partial work above. + // This means that if a lane is entangled during an interleaved event while + // it's already rendering, we won't interrupt it. This is intentional, since + // entanglement is usually "best effort": we'll try our best to render the + // lanes in the same batch, but it's not worth throwing out partially + // completed work in order to do it. + // + // For those exceptions where entanglement is semantically important, like + // useMutableSource, we should ensure that there is no partial work at the + // time we apply the entanglement. + const entangledLanes = root.entangledLanes; + if (entangledLanes !== NoLanes) { + const entanglements = root.entanglements; + let lanes = nextLanes & entangledLanes; + while (lanes > 0) { + const index = pickArbitraryLaneIndex(lanes); + const lane = 1 << index; + + nextLanes |= entanglements[index]; + + lanes &= ~lane; + } + } + + return nextLanes; +} + +export function getMostRecentEventTime(root: FiberRoot, lanes: Lanes): number { + const eventTimes = root.eventTimes; + + let mostRecentEventTime = NoTimestamp; + while (lanes > 0) { + const index = pickArbitraryLaneIndex(lanes); + const lane = 1 << index; + + const eventTime = eventTimes[index]; + if (eventTime > mostRecentEventTime) { + mostRecentEventTime = eventTime; + } + + lanes &= ~lane; + } + + return mostRecentEventTime; +} + +function computeExpirationTime(lane: Lane, currentTime: number) { + // TODO: Expiration heuristic is constant per lane, so could use a map. + getHighestPriorityLanes(lane); + const priority = return_highestLanePriority; + if (priority >= InputContinuousLanePriority) { + // User interactions should expire slightly more quickly. + // + // NOTE: This is set to the corresponding constant as in Scheduler.js. When + // we made it larger, a product metric in www regressed, suggesting there's + // a user interaction that's being starved by a series of synchronous + // updates. If that theory is correct, the proper solution is to fix the + // starvation. However, this scenario supports the idea that expiration + // times are an important safeguard when starvation does happen. + // + // Also note that, in the case of user input specifically, this will soon no + // longer be an issue because we plan to make user input synchronous by + // default (until you enter `startTransition`, of course.) + // + // If weren't planning to make these updates synchronous soon anyway, I + // would probably make this number a configurable parameter. + return currentTime + 250; + } else if (priority >= TransitionPriority) { + return currentTime + 5000; + } else { + // Anything idle priority or lower should never expire. + return NoTimestamp; + } +} + +export function markStarvedLanesAsExpired( + root: FiberRoot, + currentTime: number, +): void { + // TODO: This gets called every time we yield. We can optimize by storing + // the earliest expiration time on the root. Then use that to quickly bail out + // of this function. + + const pendingLanes = root.pendingLanes; + const suspendedLanes = root.suspendedLanes; + const pingedLanes = root.pingedLanes; + const expirationTimes = root.expirationTimes; + + // Iterate through the pending lanes and check if we've reached their + // expiration time. If so, we'll assume the update is being starved and mark + // it as expired to force it to finish. + let lanes = pendingLanes; + while (lanes > 0) { + const index = pickArbitraryLaneIndex(lanes); + const lane = 1 << index; + + const expirationTime = expirationTimes[index]; + if (expirationTime === NoTimestamp) { + // Found a pending lane with no expiration time. If it's not suspended, or + // if it's pinged, assume it's CPU-bound. Compute a new expiration time + // using the current time. + if ( + (lane & suspendedLanes) === NoLanes || + (lane & pingedLanes) !== NoLanes + ) { + // Assumes timestamps are monotonically increasing. + expirationTimes[index] = computeExpirationTime(lane, currentTime); + } + } else if (expirationTime <= currentTime) { + // This lane expired + root.expiredLanes |= lane; + } + + lanes &= ~lane; + } +} + +// This returns the highest priority pending lanes regardless of whether they +// are suspended. +export function getHighestPriorityPendingLanes(root: FiberRoot) { + return getHighestPriorityLanes(root.pendingLanes); +} + +export function getLanesToRetrySynchronouslyOnError(root: FiberRoot): Lanes { + const everythingButOffscreen = root.pendingLanes & ~OffscreenLane; + if (everythingButOffscreen !== NoLanes) { + return everythingButOffscreen; + } + if (everythingButOffscreen & OffscreenLane) { + return OffscreenLane; + } + return NoLanes; +} + +export function returnNextLanesPriority() { + return return_highestLanePriority; +} +export function includesNonIdleWork(lanes: Lanes) { + return (lanes & NonIdleLanes) !== NoLanes; +} +export function includesOnlyRetries(lanes: Lanes) { + return (lanes & RetryLanes) === lanes; +} +export function includesOnlyTransitions(lanes: Lanes) { + return (lanes & TransitionLanes) === lanes; +} + +// To ensure consistency across multiple updates in the same event, this should +// be a pure function, so that it always returns the same lane for given inputs. +export function findUpdateLane( + lanePriority: LanePriority, + wipLanes: Lanes, +): Lane { + switch (lanePriority) { + case NoLanePriority: + break; + case SyncLanePriority: + return SyncLane; + case SyncBatchedLanePriority: + return SyncBatchedLane; + case InputDiscreteLanePriority: { + const lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes); + if (lane === NoLane) { + // Shift to the next priority level + return findUpdateLane(InputContinuousLanePriority, wipLanes); + } + return lane; + } + case InputContinuousLanePriority: { + const lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes); + if (lane === NoLane) { + // Shift to the next priority level + return findUpdateLane(DefaultLanePriority, wipLanes); + } + return lane; + } + case DefaultLanePriority: { + let lane = pickArbitraryLane(DefaultLanes & ~wipLanes); + if (lane === NoLane) { + // If all the default lanes are already being worked on, look for a + // lane in the transition range. + lane = pickArbitraryLane(TransitionLanes & ~wipLanes); + if (lane === NoLane) { + // All the transition lanes are taken, too. This should be very + // rare, but as a last resort, pick a default lane. This will have + // the effect of interrupting the current work-in-progress render. + lane = pickArbitraryLane(DefaultLanes); + } + } + return lane; + } + case TransitionPriority: // Should be handled by findTransitionLane instead + case RetryLanePriority: // Should be handled by findRetryLane instead + break; + case IdleLanePriority: + let lane = pickArbitraryLane(IdleLanes & ~wipLanes); + if (lane === NoLane) { + lane = pickArbitraryLane(IdleLanes); + } + return lane; + default: + // The remaining priorities are not valid for updates + break; + } + invariant( + false, + 'Invalid update priority: %s. This is a bug in React.', + lanePriority, + ); +} + +// To ensure consistency across multiple updates in the same event, this should +// be pure function, so that it always returns the same lane for given inputs. +export function findTransitionLane(wipLanes: Lanes, pendingLanes: Lanes): Lane { + // First look for lanes that are completely unclaimed, i.e. have no + // pending work. + let lane = pickArbitraryLane(TransitionLanes & ~pendingLanes); + if (lane === NoLane) { + // If all lanes have pending work, look for a lane that isn't currently + // being worked on. + lane = pickArbitraryLane(TransitionLanes & ~wipLanes); + if (lane === NoLane) { + // If everything is being worked on, pick any lane. This has the + // effect of interrupting the current work-in-progress. + lane = pickArbitraryLane(TransitionLanes); + } + } + return lane; +} + +// To ensure consistency across multiple updates in the same event, this should +// be pure function, so that it always returns the same lane for given inputs. +export function findRetryLane(wipLanes: Lanes): Lane { + // This is a fork of `findUpdateLane` designed specifically for Suspense + // "retries" — a special update that attempts to flip a Suspense boundary + // from its placeholder state to its primary/resolved state. + let lane = pickArbitraryLane(RetryLanes & ~wipLanes); + if (lane === NoLane) { + lane = pickArbitraryLane(RetryLanes); + } + return lane; +} + +function getHighestPriorityLane(lanes: Lanes) { + return lanes & -lanes; +} + +function getLowestPriorityLane(lanes: Lanes): Lane { + // This finds the most significant non-zero bit. + const index = 31 - clz32(lanes); + return index < 0 ? NoLanes : 1 << index; +} + +function getEqualOrHigherPriorityLanes(lanes: Lanes | Lane): Lanes { + return (getLowestPriorityLane(lanes) << 1) - 1; +} + +export function pickArbitraryLane(lanes: Lanes): Lane { + // This wrapper function gets inlined. Only exists so to communicate that it + // doesn't matter which bit is selected; you can pick any bit without + // affecting the algorithms where its used. Here I'm using + // getHighestPriorityLane because it requires the fewest operations. + return getHighestPriorityLane(lanes); +} + +function pickArbitraryLaneIndex(lanes: Lanes) { + return 31 - clz32(lanes); +} + +function laneToIndex(lane: Lane) { + return pickArbitraryLaneIndex(lane); +} + +export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane) { + return (a & b) !== NoLanes; +} + +export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane) { + return (set & subset) === subset; +} + +export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes { + return a | b; +} + +export function removeLanes(set: Lanes, subset: Lanes | Lane): Lanes { + return set & ~subset; +} + +// Seems redundant, but it changes the type from a single lane (used for +// updates) to a group of lanes (used for flushing work). +export function laneToLanes(lane: Lane): Lanes { + return lane; +} + +export function higherPriorityLane(a: Lane, b: Lane) { + // This works because the bit ranges decrease in priority as you go left. + return a !== NoLane && a < b ? a : b; +} + +export function higherLanePriority( + a: LanePriority, + b: LanePriority, +): LanePriority { + return a !== NoLanePriority && a > b ? a : b; +} + +export function createLaneMap(initial: T): LaneMap { + // Intentionally pushing one by one. + // https://v8.dev/blog/elements-kinds#avoid-creating-holes + const laneMap = []; + for (let i = 0; i < TotalLanes; i++) { + laneMap.push(initial); + } + return laneMap; +} + +export function markRootUpdated( + root: FiberRoot, + updateLane: Lane, + eventTime: number, +) { + root.pendingLanes |= updateLane; + + // TODO: Theoretically, any update to any lane can unblock any other lane. But + // it's not practical to try every single possible combination. We need a + // heuristic to decide which lanes to attempt to render, and in which batches. + // For now, we use the same heuristic as in the old ExpirationTimes model: + // retry any lane at equal or lower priority, but don't try updates at higher + // priority without also including the lower priority updates. This works well + // when considering updates across different priority levels, but isn't + // sufficient for updates within the same priority, since we want to treat + // those updates as parallel. + + // Unsuspend any update at equal or lower priority. + const higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111 + + root.suspendedLanes &= higherPriorityLanes; + root.pingedLanes &= higherPriorityLanes; + + const eventTimes = root.eventTimes; + const index = laneToIndex(updateLane); + // We can always overwrite an existing timestamp because we prefer the most + // recent event, and we assume time is monotonically increasing. + eventTimes[index] = eventTime; +} + +export function markRootSuspended(root: FiberRoot, suspendedLanes: Lanes) { + root.suspendedLanes |= suspendedLanes; + root.pingedLanes &= ~suspendedLanes; + + // The suspended lanes are no longer CPU-bound. Clear their expiration times. + const expirationTimes = root.expirationTimes; + let lanes = suspendedLanes; + while (lanes > 0) { + const index = pickArbitraryLaneIndex(lanes); + const lane = 1 << index; + + expirationTimes[index] = NoTimestamp; + + lanes &= ~lane; + } +} + +export function markRootPinged( + root: FiberRoot, + pingedLanes: Lanes, + eventTime: number, +) { + root.pingedLanes |= root.suspendedLanes & pingedLanes; +} + +export function markRootExpired(root: FiberRoot, expiredLanes: Lanes) { + root.expiredLanes |= expiredLanes & root.pendingLanes; +} + +export function markDiscreteUpdatesExpired(root: FiberRoot) { + root.expiredLanes |= InputDiscreteLanes & root.pendingLanes; +} + +export function hasDiscreteLanes(lanes: Lanes) { + return (lanes & InputDiscreteLanes) !== NoLanes; +} + +export function markRootMutableRead(root: FiberRoot, updateLane: Lane) { + root.mutableReadLanes |= updateLane & root.pendingLanes; +} + +export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { + const noLongerPendingLanes = root.pendingLanes & ~remainingLanes; + + root.pendingLanes = remainingLanes; + + // Let's try everything again + root.suspendedLanes = 0; + root.pingedLanes = 0; + + root.expiredLanes &= remainingLanes; + root.mutableReadLanes &= remainingLanes; + + root.entangledLanes &= remainingLanes; + + const entanglements = root.entanglements; + const eventTimes = root.eventTimes; + const expirationTimes = root.expirationTimes; + + // Clear the lanes that no longer have pending work + let lanes = noLongerPendingLanes; + while (lanes > 0) { + const index = pickArbitraryLaneIndex(lanes); + const lane = 1 << index; + + entanglements[index] = NoLanes; + eventTimes[index] = NoTimestamp; + expirationTimes[index] = NoTimestamp; + + lanes &= ~lane; + } +} + +export function markRootEntangled(root: FiberRoot, entangledLanes: Lanes) { + root.entangledLanes |= entangledLanes; + + const entanglements = root.entanglements; + let lanes = entangledLanes; + while (lanes > 0) { + const index = pickArbitraryLaneIndex(lanes); + const lane = 1 << index; + + entanglements[index] |= entangledLanes; + + lanes &= ~lane; + } +} + +export function getBumpedLaneForHydration( + root: FiberRoot, + renderLanes: Lanes, +): Lane { + getHighestPriorityLanes(renderLanes); + const highestLanePriority = return_highestLanePriority; + + let lane; + switch (highestLanePriority) { + case SyncLanePriority: + case SyncBatchedLanePriority: + lane = NoLane; + break; + case InputDiscreteHydrationLanePriority: + case InputDiscreteLanePriority: + lane = InputDiscreteHydrationLane; + break; + case InputContinuousHydrationLanePriority: + case InputContinuousLanePriority: + lane = InputContinuousHydrationLane; + break; + case DefaultHydrationLanePriority: + case DefaultLanePriority: + lane = DefaultHydrationLane; + break; + case TransitionHydrationPriority: + case TransitionPriority: + lane = TransitionHydrationLane; + break; + case RetryLanePriority: + // Shouldn't be reachable under normal circumstances, so there's no + // dedicated lane for retry priority. Use the one for long transitions. + lane = TransitionHydrationLane; + break; + case SelectiveHydrationLanePriority: + lane = SelectiveHydrationLane; + break; + case IdleHydrationLanePriority: + case IdleLanePriority: + lane = IdleHydrationLane; + break; + case OffscreenLanePriority: + case NoLanePriority: + lane = NoLane; + break; + default: + invariant(false, 'Invalid lane: %s. This is a bug in React.', lane); + } + + // Check if the lane we chose is suspended. If so, that indicates that we + // already attempted and failed to hydrate at that level. Also check if we're + // already rendering that lane, which is rare but could happen. + if ((lane & (root.suspendedLanes | renderLanes)) !== NoLane) { + // Give up trying to hydrate and fall back to client render. + return NoLane; + } + + return lane; +} + +const clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; + +// Count leading zeros. Only used on lanes, so assume input is an integer. +// Based on: +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 +const log = Math.log; +const LN2 = Math.LN2; +function clz32Fallback(lanes: Lanes | Lane) { + if (lanes === 0) { + return 32; + } + return (31 - ((log(lanes) / LN2) | 0)) | 0; +} diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index fb464e62a259..8e387ee012ec 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -10,7 +10,7 @@ import type {ReactContext} from 'shared/ReactTypes'; import type {Fiber, ContextDependency} from './ReactInternalTypes'; import type {StackCursor} from './ReactFiberStack.new'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import {isPrimaryRenderer} from './ReactFiberHostConfig'; import {createCursor, push, pop} from './ReactFiberStack.new'; @@ -27,7 +27,7 @@ import { includesSomeLane, mergeLanes, pickArbitraryLane, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; import invariant from 'shared/invariant'; import is from 'shared/objectIs'; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index af2899727244..c7f03442d579 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -10,7 +10,7 @@ import type {ReactContext} from 'shared/ReactTypes'; import type {Fiber, ContextDependency} from './ReactInternalTypes'; import type {StackCursor} from './ReactFiberStack.old'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import {isPrimaryRenderer} from './ReactFiberHostConfig'; import {createCursor, push, pop} from './ReactFiberStack.old'; @@ -27,7 +27,7 @@ import { includesSomeLane, mergeLanes, pickArbitraryLane, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; import invariant from 'shared/invariant'; import is from 'shared/objectIs'; diff --git a/packages/react-reconciler/src/ReactFiberOffscreenComponent.js b/packages/react-reconciler/src/ReactFiberOffscreenComponent.js index 9f3aabcdde26..4a013a9d7eaf 100644 --- a/packages/react-reconciler/src/ReactFiberOffscreenComponent.js +++ b/packages/react-reconciler/src/ReactFiberOffscreenComponent.js @@ -8,7 +8,7 @@ */ import type {ReactNodeList} from 'shared/ReactTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; export type OffscreenProps = {| // TODO: Pick an API before exposing the Offscreen type. I've chosen an enum diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index 86749f5c2039..980b623343dc 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -19,7 +19,7 @@ import type { import type {RendererInspectionConfig} from './ReactFiberHostConfig'; import {FundamentalComponent} from './ReactWorkTags'; import type {ReactNodeList} from 'shared/ReactTypes'; -import type {Lane, LanePriority} from './ReactFiberLane'; +import type {Lane, LanePriority} from './ReactFiberLane.new'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; import { @@ -82,7 +82,7 @@ import { higherPriorityLane, getCurrentUpdateLanePriority, setCurrentUpdateLanePriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; import { scheduleRefresh, scheduleRoot, diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index 425cde8ab84f..0fc72c42a07e 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -19,7 +19,7 @@ import type { import type {RendererInspectionConfig} from './ReactFiberHostConfig'; import {FundamentalComponent} from './ReactWorkTags'; import type {ReactNodeList} from 'shared/ReactTypes'; -import type {Lane, LanePriority} from './ReactFiberLane'; +import type {Lane, LanePriority} from './ReactFiberLane.old'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; import { @@ -82,7 +82,7 @@ import { higherPriorityLane, getCurrentUpdateLanePriority, setCurrentUpdateLanePriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; import { scheduleRefresh, scheduleRoot, diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index 89b0743cf0b9..1b504a18fc32 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -17,7 +17,7 @@ import { NoLanePriority, NoTimestamp, createLaneMap, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; import { enableSchedulerTracing, enableSuspenseCallback, diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index e72cf818a46a..84509babdb51 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -17,7 +17,7 @@ import { NoLanePriority, NoTimestamp, createLaneMap, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; import { enableSchedulerTracing, enableSuspenseCallback, diff --git a/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js b/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js index 6e14aacd77de..9692c89a6591 100644 --- a/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js @@ -10,7 +10,7 @@ import type {ReactNodeList, Wakeable} from 'shared/ReactTypes'; import type {Fiber} from './ReactInternalTypes'; import type {SuspenseInstance} from './ReactFiberHostConfig'; -import type {Lane} from './ReactFiberLane'; +import type {Lane} from './ReactFiberLane.new'; import {SuspenseComponent, SuspenseListComponent} from './ReactWorkTags'; import {NoFlags, DidCapture} from './ReactFiberFlags'; import { diff --git a/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js b/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js index 6e14aacd77de..e98826932f92 100644 --- a/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js @@ -10,7 +10,7 @@ import type {ReactNodeList, Wakeable} from 'shared/ReactTypes'; import type {Fiber} from './ReactInternalTypes'; import type {SuspenseInstance} from './ReactFiberHostConfig'; -import type {Lane} from './ReactFiberLane'; +import type {Lane} from './ReactFiberLane.old'; import {SuspenseComponent, SuspenseListComponent} from './ReactWorkTags'; import {NoFlags, DidCapture} from './ReactFiberFlags'; import { diff --git a/packages/react-reconciler/src/ReactFiberThrow.new.js b/packages/react-reconciler/src/ReactFiberThrow.new.js index 01ab69cecee6..058b46be0040 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.new.js +++ b/packages/react-reconciler/src/ReactFiberThrow.new.js @@ -9,7 +9,7 @@ import type {Fiber} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {Lane, Lanes} from './ReactFiberLane'; +import type {Lane, Lanes} from './ReactFiberLane.new'; import type {CapturedValue} from './ReactCapturedValue'; import type {Update} from './ReactUpdateQueue.new'; import type {Wakeable} from 'shared/ReactTypes'; @@ -67,7 +67,7 @@ import { includesSomeLane, mergeLanes, pickArbitraryLane, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; diff --git a/packages/react-reconciler/src/ReactFiberThrow.old.js b/packages/react-reconciler/src/ReactFiberThrow.old.js index fabcffa87378..fbb9daa45262 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.old.js +++ b/packages/react-reconciler/src/ReactFiberThrow.old.js @@ -9,7 +9,7 @@ import type {Fiber} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {Lane, Lanes} from './ReactFiberLane'; +import type {Lane, Lanes} from './ReactFiberLane.old'; import type {CapturedValue} from './ReactCapturedValue'; import type {Update} from './ReactUpdateQueue.old'; import type {Wakeable} from 'shared/ReactTypes'; @@ -67,7 +67,7 @@ import { includesSomeLane, mergeLanes, pickArbitraryLane, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js index 98d1f7957f31..8d445cedd44f 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.new'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new'; diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js index b97dcf0a4a74..3590f3ea4f1b 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; +import type {Lanes} from './ReactFiberLane.old'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old'; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 140ea56e0420..dc1ff874b882 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -9,7 +9,7 @@ import type {Thenable, Wakeable} from 'shared/ReactTypes'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.new'; import type {ReactPriorityLevel} from './ReactInternalTypes'; import type {Interaction} from 'scheduler/src/Tracing'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; @@ -173,7 +173,7 @@ import { markRootFinished, schedulerPriorityToLanePriority, lanePriorityToSchedulerPriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; import {requestCurrentTransition, NoTransition} from './ReactFiberTransition'; import {beginWork as originalBeginWork} from './ReactFiberBeginWork.new'; import {completeWork} from './ReactFiberCompleteWork.new'; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 02cfdce20af4..57fce214dc47 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -9,7 +9,7 @@ import type {Thenable, Wakeable} from 'shared/ReactTypes'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.old'; import type {ReactPriorityLevel} from './ReactInternalTypes'; import type {Interaction} from 'scheduler/src/Tracing'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; @@ -173,7 +173,7 @@ import { markRootFinished, schedulerPriorityToLanePriority, lanePriorityToSchedulerPriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; import {requestCurrentTransition, NoTransition} from './ReactFiberTransition'; import {beginWork as originalBeginWork} from './ReactFiberBeginWork.old'; import {completeWork} from './ReactFiberCompleteWork.old'; diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index ce3f786a5186..c48d53fbb919 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -20,7 +20,7 @@ import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; import type {Flags} from './ReactFiberFlags'; -import type {Lane, LanePriority, Lanes, LaneMap} from './ReactFiberLane'; +import type {Lane, LanePriority, Lanes, LaneMap} from './ReactFiberLane.old'; import type {RootTag} from './ReactRootTags'; import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig'; import type {Wakeable} from 'shared/ReactTypes'; diff --git a/packages/react-reconciler/src/ReactUpdateQueue.new.js b/packages/react-reconciler/src/ReactUpdateQueue.new.js index f745445a1e38..132f83ab22c3 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.new.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.new.js @@ -85,9 +85,14 @@ // resources, but the final state is always the same. import type {Fiber} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.new'; -import {NoLane, NoLanes, isSubsetOfLanes, mergeLanes} from './ReactFiberLane'; +import { + NoLane, + NoLanes, + isSubsetOfLanes, + mergeLanes, +} from './ReactFiberLane.new'; import { enterDisallowedContextReadInDEV, exitDisallowedContextReadInDEV, diff --git a/packages/react-reconciler/src/ReactUpdateQueue.old.js b/packages/react-reconciler/src/ReactUpdateQueue.old.js index 4eccd78d1234..f1c14537a42d 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.old.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.old.js @@ -85,9 +85,14 @@ // resources, but the final state is always the same. import type {Fiber} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; +import type {Lanes, Lane} from './ReactFiberLane.old'; -import {NoLane, NoLanes, isSubsetOfLanes, mergeLanes} from './ReactFiberLane'; +import { + NoLane, + NoLanes, + isSubsetOfLanes, + mergeLanes, +} from './ReactFiberLane.old'; import { enterDisallowedContextReadInDEV, exitDisallowedContextReadInDEV, diff --git a/packages/react-reconciler/src/SchedulerWithReactIntegration.new.js b/packages/react-reconciler/src/SchedulerWithReactIntegration.new.js index 606a90252077..36fdf3938128 100644 --- a/packages/react-reconciler/src/SchedulerWithReactIntegration.new.js +++ b/packages/react-reconciler/src/SchedulerWithReactIntegration.new.js @@ -22,7 +22,7 @@ import { SyncLanePriority, getCurrentUpdateLanePriority, setCurrentUpdateLanePriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.new'; const { unstable_runWithPriority: Scheduler_runWithPriority, diff --git a/packages/react-reconciler/src/SchedulerWithReactIntegration.old.js b/packages/react-reconciler/src/SchedulerWithReactIntegration.old.js index 606a90252077..4a44229ac524 100644 --- a/packages/react-reconciler/src/SchedulerWithReactIntegration.old.js +++ b/packages/react-reconciler/src/SchedulerWithReactIntegration.old.js @@ -22,7 +22,7 @@ import { SyncLanePriority, getCurrentUpdateLanePriority, setCurrentUpdateLanePriority, -} from './ReactFiberLane'; +} from './ReactFiberLane.old'; const { unstable_runWithPriority: Scheduler_runWithPriority, diff --git a/packages/react-reconciler/src/SchedulingProfiler.js b/packages/react-reconciler/src/SchedulingProfiler.js index 1c69edebcbb7..0779b870a274 100644 --- a/packages/react-reconciler/src/SchedulingProfiler.js +++ b/packages/react-reconciler/src/SchedulingProfiler.js @@ -7,7 +7,7 @@ * @flow */ -import type {Lane, Lanes} from './ReactFiberLane'; +import type {Lane, Lanes} from './ReactFiberLane.old'; import type {Fiber} from './ReactInternalTypes'; import type {Wakeable} from 'shared/ReactTypes';