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 9a41c6c1317f9..dcb2cfb9fd70d 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 2fbe0e1b0b616..a56487c0928dd 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 075a705f03bd3..ac13410c36231 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 534d7032ba2d9..52bd632721184 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 df4c2e10d9299..7313d78f0bf3b 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 5426568b96611..edeae5caa6feb 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 56ef2f5a528ab..8e3df44ac676f 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 52bbc5ac405ba..99aa9193e2f20 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 e12f7f300b472..f875db5944954 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 4e58dd71f3dc6..e807ae8222a2e 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 c5f3527419490..f055bac71ca94 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 f16fb99435519..a9c54d9c02e98 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 55db13cf3988d..c134ecccddbb9 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 89d2193176c27..9d91de82073c6 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 21ffea1c92dd7..6f1942f7b90bb 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 f257657d2b550..4a0a9231f0445 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 9fa15c5c0655e..0a7a99079c963 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 ed962e9d32669..4c9eaf010125c 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 00db4f034e863..ee0616fae79c0 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 e90bc9768a9ae..6ed0f6c8cbc55 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 7576bd5ac0811..5d084bf97badd 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 027754dd76ee0..b5d8846ec2002 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 0000000000000..f58ab20531740 --- /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 fb464e62a2592..8e387ee012ec3 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 af28997272441..c7f03442d5797 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 9f3aabcdde265..4a013a9d7eaf9 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 86749f5c2039d..980b623343dc4 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 425cde8ab84f5..0fc72c42a07e0 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 89b0743cf0b93..1b504a18fc326 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 e72cf818a46a3..84509babdb51a 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 6e14aacd77def..9692c89a65911 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 6e14aacd77def..e98826932f921 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 01ab69cecee66..058b46be0040e 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 fabcffa87378a..fbb9daa452625 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 98d1f7957f310..8d445cedd44f9 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 b97dcf0a4a74b..3590f3ea4f1b1 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 140ea56e04202..dc1ff874b8825 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 02cfdce20af4b..57fce214dc47d 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 ce3f786a5186c..c48d53fbb919b 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 f745445a1e387..132f83ab22c3a 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 4eccd78d12343..f1c14537a42d3 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 606a90252077e..36fdf3938128e 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 606a90252077e..4a44229ac5245 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 1c69edebcbb7e..0779b870a274b 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';