Skip to content

Commit

Permalink
Unify context implementations
Browse files Browse the repository at this point in the history
Implements the new context API on top of the existing ReactStack that we
already use for host context and legacy context. Now there is a single
array that we push and pop from.

This makes the interrupt path slightly slower, since when we reset the
unit of work pointer, we have to iterate over the stack (like before)
*and* switch on the type of work (not like before). On the other hand,
this unifies all of the unwinding behavior in the UnwindWork module.
  • Loading branch information
acdlite committed Mar 13, 2018
1 parent 010c4ed commit 3bb28ba
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 82 deletions.
8 changes: 0 additions & 8 deletions packages/react-reconciler/src/ReactFiberContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export type LegacyContext = {
processChildContext(fiber: Fiber, parentContext: Object): Object,
pushContextProvider(workInProgress: Fiber): boolean,
invalidateContextProvider(workInProgress: Fiber, didChange: boolean): void,
resetContext(): void,
findCurrentUnmaskedContext(fiber: Fiber): Object,
};

Expand Down Expand Up @@ -292,12 +291,6 @@ export default function(stack: Stack): LegacyContext {
}
}

function resetContext(): void {
previousContext = emptyObject;
contextStackCursor.current = emptyObject;
didPerformWorkStackCursor.current = false;
}

function findCurrentUnmaskedContext(fiber: Fiber): Object {
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
// makes sense elsewhere
Expand Down Expand Up @@ -336,7 +329,6 @@ export default function(stack: Stack): LegacyContext {
processChildContext,
pushContextProvider,
invalidateContextProvider,
resetContext,
findCurrentUnmaskedContext,
};
}
7 changes: 0 additions & 7 deletions packages/react-reconciler/src/ReactFiberHostContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export type HostContext<C, CX> = {
popHostContext(fiber: Fiber): void,
pushHostContainer(fiber: Fiber, container: C): void,
pushHostContext(fiber: Fiber): void,
resetHostContainer(): void,
};

export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
Expand Down Expand Up @@ -108,18 +107,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
pop(contextFiberStackCursor, fiber);
}

function resetHostContainer() {
contextStackCursor.current = NO_CONTEXT;
rootInstanceStackCursor.current = NO_CONTEXT;
}

return {
getHostContext,
getRootHostContainer,
popHostContainer,
popHostContext,
pushHostContainer,
pushHostContext,
resetHostContainer,
};
}
55 changes: 19 additions & 36 deletions packages/react-reconciler/src/ReactFiberNewContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@

import type {Fiber} from './ReactFiber';
import type {ReactContext} from 'shared/ReactTypes';
import type {StackCursor, Stack} from './ReactFiberStack';

import warning from 'fbjs/lib/warning';

export type NewContext = {
pushProvider(providerFiber: Fiber): void,
popProvider(providerFiber: Fiber): void,
resetProviderStack(): void,
};

export default function() {
let changedBitsStack: Array<any> = [];
let currentValueStack: Array<any> = [];
let stack: Array<Fiber> = [];
let index = -1;
export default function(stack: Stack) {
const {createCursor, push, pop} = stack;

const providerCursor: StackCursor<Fiber | null> = createCursor(null);
const valueCursor: StackCursor<mixed> = createCursor(null);
const changedBitsCursor: StackCursor<number> = createCursor(0);

let rendererSigil;
if (__DEV__) {
Expand All @@ -32,10 +33,11 @@ export default function() {

function pushProvider(providerFiber: Fiber): void {
const context: ReactContext<any> = providerFiber.type.context;
index += 1;
changedBitsStack[index] = context._changedBits;
currentValueStack[index] = context._currentValue;
stack[index] = providerFiber;

push(changedBitsCursor, context._changedBits, providerFiber);
push(valueCursor, context._currentValue, providerFiber);
push(providerCursor, providerFiber, providerFiber);

context._currentValue = providerFiber.pendingProps.value;
context._changedBits = providerFiber.stateNode;

Expand All @@ -51,39 +53,20 @@ export default function() {
}

function popProvider(providerFiber: Fiber): void {
if (__DEV__) {
warning(index > -1 && providerFiber === stack[index], 'Unexpected pop.');
}
const changedBits = changedBitsStack[index];
const currentValue = currentValueStack[index];
changedBitsStack[index] = null;
currentValueStack[index] = null;
stack[index] = null;
index -= 1;
const changedBits = changedBitsCursor.current;
const currentValue = valueCursor.current;

pop(providerCursor, providerFiber);
pop(valueCursor, providerFiber);
pop(changedBitsCursor, providerFiber);

const context: ReactContext<any> = providerFiber.type.context;
context._currentValue = currentValue;
context._changedBits = changedBits;
}

function resetProviderStack(): void {
for (let i = index; i > -1; i--) {
const providerFiber = stack[i];
const context: ReactContext<any> = providerFiber.type.context;
context._currentValue = context._defaultValue;
context._changedBits = 0;
changedBitsStack[i] = null;
currentValueStack[i] = null;
stack[i] = null;
if (__DEV__) {
context._currentRenderer = null;
}
}
index = -1;
}

return {
pushProvider,
popProvider,
resetProviderStack,
};
}
34 changes: 17 additions & 17 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,21 +157,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
config: HostConfig<T, P, I, TI, HI, PI, C, CC, CX, PL>,
) {
const stack = ReactFiberStack();
const {reset: resetStack} = stack;
const hostContext = ReactFiberHostContext(config, stack);
const legacyContext = ReactFiberLegacyContext(stack);
const newContext = ReactFiberNewContext();
const newContext = ReactFiberNewContext(stack);
const {popHostContext, popHostContainer} = hostContext;
const {
popTopLevelContextObject: popTopLevelLegacyContextObject,
popContextProvider: popLegacyContextProvider,
resetContext: resetLegacyContext,
} = legacyContext;
const {popProvider, resetProviderStack} = newContext;
const {popProvider} = newContext;
const hydrationContext: HydrationContext<C, CX> = ReactFiberHydrationContext(
config,
);
const {resetHostContainer} = hostContext;
const {beginWork} = ReactFiberBeginWork(
config,
hostContext,
Expand All @@ -188,7 +185,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
newContext,
hydrationContext,
);
const {throwException, unwindWork} = ReactFiberUnwindWork(
const {
throwException,
unwindWork,
unwindInterruptedWork,
} = ReactFiberUnwindWork(
hostContext,
legacyContext,
newContext,
Expand Down Expand Up @@ -289,15 +290,14 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
};
}

function resetContextStack() {
// Reset the stack
resetStack();
// Reset the cursors
resetLegacyContext();
resetHostContainer();

// TODO: Unify new context implementation with other stacks
resetProviderStack();
function resetStack() {
if (nextUnitOfWork !== null) {
let interruptedWork = nextUnitOfWork.return;
while (interruptedWork !== null) {
unwindInterruptedWork(interruptedWork);
interruptedWork = interruptedWork.return;
}
}

if (__DEV__) {
ReactStrictModeWarnings.discardPendingWarnings();
Expand Down Expand Up @@ -841,7 +841,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
nextUnitOfWork === null
) {
// Reset the stack and start working from the root.
resetContextStack();
resetStack();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
nextUnitOfWork = createWorkInProgress(
Expand Down Expand Up @@ -1102,7 +1102,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
) {
// This is an interruption. (Used for performance tracking.)
interruptedBy = fiber;
resetContextStack();
resetStack();
}
if (nextRoot !== root || !isWorking) {
requestWork(root, expirationTime);
Expand Down
14 changes: 0 additions & 14 deletions packages/react-reconciler/src/ReactFiberStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export type Stack = {
isEmpty(): boolean,
push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void,
pop<T>(cursor: StackCursor<T>, fiber: Fiber): void,
reset(): void,
};

export default function(): Stack {
Expand Down Expand Up @@ -81,23 +80,10 @@ export default function(): Stack {
cursor.current = value;
}

function reset(): void {
while (index > -1) {
valueStack[index] = null;

if (__DEV__) {
fiberStack[index] = null;
}

index--;
}
}

return {
createCursor,
isEmpty,
pop,
push,
reset,
};
}
28 changes: 28 additions & 0 deletions packages/react-reconciler/src/ReactFiberUnwindWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,36 @@ export default function<C, CX>(
return null;
}
}

function unwindInterruptedWork(interruptedWork: Fiber) {
switch (interruptedWork.tag) {
case ClassComponent: {
popLegacyContextProvider(interruptedWork);
break;
}
case HostRoot: {
popHostContainer(interruptedWork);
popTopLevelLegacyContextObject(interruptedWork);
break;
}
case HostComponent: {
popHostContext(interruptedWork);
break;
}
case HostPortal:
popHostContainer(interruptedWork);
break;
case ContextProvider:
popProvider(interruptedWork);
break;
default:
break;
}
}

return {
throwException,
unwindWork,
unwindInterruptedWork,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,13 @@ ${formatActions(actions)}
['c', step(2)],
['b', interrupt()],
);

simulateMultipleRoots(
['c', toggle(0)],
['c', step(1)],
['b', flush(7)],
['c', toggle(0)],
);
});

it('generative tests', () => {
Expand Down

0 comments on commit 3bb28ba

Please sign in to comment.