From 3d0cf47d2621cd3690e6ede19a3db08b6ff89aca Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 20 Dec 2016 18:04:38 -0800 Subject: [PATCH 01/10] Added ReactFiberStack shared by ReactFiberContext and ReactFiberHostContext ReactFiberStack is the new underlying stack used by ReactFiberContext and ReactFiberHostContext. The goal is to simplify the 2 context managers and to add more errors/dev-warnings when we pop unexpected. This changeset currently causes a lot of tests to fail (as we are currently popping too many times and/or in the wrong order). I'm pushing this branch now to share with Sebastian as he is working on a related cleanup pass at beginWork. --- .../shared/fiber/ReactFiberBeginWork.js | 15 +- .../shared/fiber/ReactFiberCompleteWork.js | 2 +- .../shared/fiber/ReactFiberContext.js | 47 +++--- .../shared/fiber/ReactFiberHostContext.js | 134 ++++++------------ src/renderers/shared/fiber/ReactFiberStack.js | 102 +++++++++++++ 5 files changed, 181 insertions(+), 119 deletions(-) create mode 100644 src/renderers/shared/fiber/ReactFiberStack.js diff --git a/src/renderers/shared/fiber/ReactFiberBeginWork.js b/src/renderers/shared/fiber/ReactFiberBeginWork.js index 26bc381c0677..c5bb64b94fdf 100644 --- a/src/renderers/shared/fiber/ReactFiberBeginWork.js +++ b/src/renderers/shared/fiber/ReactFiberBeginWork.js @@ -270,14 +270,19 @@ module.exports = function( const root = (workInProgress.stateNode : FiberRoot); if (root.pendingContext) { pushTopLevelContextObject( + workInProgress, root.pendingContext, root.pendingContext !== root.context ); } else { - pushTopLevelContextObject(root.context, false); + pushTopLevelContextObject( + workInProgress, + root.context, + false + ); } - pushHostContainer(root.containerInfo); + pushHostContainer(workInProgress, root.containerInfo); const updateQueue = workInProgress.updateQueue; if (updateQueue) { @@ -444,7 +449,7 @@ module.exports = function( } function updatePortalComponent(current, workInProgress) { - pushHostContainer(workInProgress.stateNode.containerInfo); + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); const priorityLevel = workInProgress.pendingWorkPriority; let nextChildren = workInProgress.pendingProps; if (hasContextChanged()) { @@ -535,7 +540,7 @@ module.exports = function( } break; case HostPortal: - pushHostContainer(workInProgress.stateNode.containerInfo); + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); break; } // TODO: What if this is currently in progress? @@ -628,4 +633,4 @@ module.exports = function( beginFailedWork, }; -}; +}; \ No newline at end of file diff --git a/src/renderers/shared/fiber/ReactFiberCompleteWork.js b/src/renderers/shared/fiber/ReactFiberCompleteWork.js index 214d471d7efa..07da4c25de9f 100644 --- a/src/renderers/shared/fiber/ReactFiberCompleteWork.js +++ b/src/renderers/shared/fiber/ReactFiberCompleteWork.js @@ -176,7 +176,7 @@ module.exports = function( case ClassComponent: { // We are leaving this subtree, so pop context if any. if (isContextProvider(workInProgress)) { - popContextProvider(); + popContextProvider(workInProgress); } // Don't use the state queue to compute the memoized state. We already // merged it and assigned it to the instance. Transfer it from there. diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js index 76e8ed568f62..94b2c2f8c6e8 100644 --- a/src/renderers/shared/fiber/ReactFiberContext.js +++ b/src/renderers/shared/fiber/ReactFiberContext.js @@ -13,6 +13,7 @@ 'use strict'; import type { Fiber } from 'ReactFiber'; +import type { StackCursor } from 'ReactFiberStack'; var emptyObject = require('emptyObject'); var invariant = require('invariant'); @@ -24,20 +25,22 @@ var { ClassComponent, HostRoot, } = require('ReactTypeOfWork'); +const { + createCursor, + pop, + push, + reset, +} = require('ReactFiberStack'); if (__DEV__) { var checkReactTypeSpec = require('checkReactTypeSpec'); } -let index = -1; -const contextStack : Array = []; -const didPerformWorkStack : Array = []; +let contextStackCursor : StackCursor = createCursor(); +let didPerformWorkStackCursor : StackCursor = createCursor(); function getUnmaskedContext() { - if (index === -1) { - return emptyObject; - } - return contextStack[index]; + return contextStackCursor.current; } exports.getMaskedContext = function(workInProgress : Fiber) { @@ -62,7 +65,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) { }; exports.hasContextChanged = function() : boolean { - return index > -1 && didPerformWorkStack[index]; + return Boolean(didPerformWorkStackCursor.current); }; function isContextProvider(fiber : Fiber) : boolean { @@ -75,19 +78,17 @@ function isContextProvider(fiber : Fiber) : boolean { } exports.isContextProvider = isContextProvider; -function popContextProvider() : void { - invariant(index > -1, 'Unexpected context pop'); - contextStack[index] = emptyObject; - didPerformWorkStack[index] = false; - index--; +function popContextProvider(fiber : Fiber) : void { + pop(didPerformWorkStackCursor, fiber); + pop(contextStackCursor, fiber); } exports.popContextProvider = popContextProvider; -exports.pushTopLevelContextObject = function(context : Object, didChange : boolean) : void { - invariant(index === -1, 'Unexpected context found on stack'); - index++; - contextStack[index] = context; - didPerformWorkStack[index] = didChange; +exports.pushTopLevelContextObject = function(fiber : Fiber, context : Object, didChange : boolean) : void { + invariant(contextStackCursor.cursor == null, 'Unexpected context found on stack'); + + push(contextStackCursor, context, fiber); + push(didPerformWorkStackCursor, didChange, fiber); }; function processChildContext(fiber : Fiber, parentContext : Object, isReconciling : boolean): Object { @@ -129,13 +130,13 @@ exports.pushContextProvider = function(workInProgress : Fiber, didPerformWork : instance.__reactInternalMemoizedMergedChildContext = mergedContext; } - index++; - contextStack[index] = mergedContext; - didPerformWorkStack[index] = didPerformWork; + push(contextStackCursor, mergedContext, workInProgress); + push(didPerformWorkStackCursor, didPerformWork, workInProgress); }; exports.resetContext = function() : void { - index = -1; + reset(contextStackCursor); + reset(didPerformWorkStackCursor); }; exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object { @@ -162,7 +163,7 @@ exports.unwindContext = function(from : Fiber, to: Fiber) { let node = from; while (node && (node !== to) && (node.alternate !== to)) { if (isContextProvider(node)) { - popContextProvider(); + popContextProvider(node); } node = node.return; } diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index 574ac240af0b..9effc291d4f7 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -14,16 +14,22 @@ import type { Fiber } from 'ReactFiber'; import type { HostConfig } from 'ReactFiberReconciler'; +import type { StackCursor } from 'ReactFiberStack'; + +const { + createCursor, + pop, + push, + reset, +} = require('ReactFiberStack'); export type HostContext = { - getRootHostContainer() : C, getHostContext() : CX, - - pushHostContext(fiber : Fiber) : void, + getRootHostContainer() : C, + popHostContainer() : void, popHostContext(fiber : Fiber) : void, - pushHostContainer(container : C) : void, - popHostContainer() : void, + pushHostContext(fiber : Fiber) : void, resetHostContainer() : void, }; @@ -35,129 +41,77 @@ module.exports = function( getRootHostContext, } = config; - // Context stack is reused across the subtrees. - // We use a null sentinel on the fiber stack to separate them. - let contextFibers : Array = []; - let contextValues : Array = []; - let contextDepth : number = -1; - // Current context for fast access. - let currentContextValue : CX | null = null; - // Current root instance for fast access. - let rootInstance : C | null = null; - // A stack of outer root instances if we're in a portal. - let portalStack : Array = []; - let portalDepth : number = -1; + let contextStackCursor : StackCursor = createCursor(); + let rootInstanceStackCursor : StackCursor = createCursor(); function getRootHostContainer() : C { - if (rootInstance == null) { + if (rootInstanceStackCursor.current == null) { throw new Error('Expected root container to exist.'); } - return rootInstance; + return rootInstanceStackCursor.current; } + // TODO (bvaughn) Pass the host container fiber to push() function pushHostContainer(nextRootInstance : C) { + // Push current root instance onto the stack; + // This allows us to reset root when portals are popped. + push(rootInstanceStackCursor, nextRootInstance, null); + const nextRootContext = getRootHostContext(nextRootInstance); - if (rootInstance == null) { - // We're entering a root. - rootInstance = nextRootInstance; - } else { - // We're entering a portal. - // Save the current root to the portal stack. - portalDepth++; - portalStack[portalDepth] = rootInstance; - rootInstance = nextRootInstance; - } - // Push the next root or portal context. - contextDepth++; - contextFibers[contextDepth] = null; - contextValues[contextDepth] = nextRootContext; - currentContextValue = nextRootContext; + + push(contextStackCursor, nextRootContext, null); } + // TODO (bvaughn) Pass the host container fiber to pop() function popHostContainer() { - if (portalDepth === -1) { - // We're popping the root. - rootInstance = null; - currentContextValue = null; - contextDepth = -1; - } else { - // We're popping a portal. - // Restore the root instance. - rootInstance = portalStack[portalDepth]; - portalStack[portalDepth] = null; - portalDepth--; - // If we pushed any context while in a portal, we need to roll it back. - if (contextDepth > -1) { - contextDepth--; - if (contextDepth > -1) { - currentContextValue = contextValues[contextDepth]; - } else { - currentContextValue = null; - } - } - } + pop(contextStackCursor, null); + pop(rootInstanceStackCursor, null); } function getHostContext() : CX { - if (currentContextValue == null) { + if (contextStackCursor.current == null) { throw new Error('Expected host context to exist.'); } - return currentContextValue; + + return contextStackCursor.current; } function pushHostContext(fiber : Fiber) : void { - if (currentContextValue == null) { + if (rootInstanceStackCursor.current == null) { throw new Error('Expected root host context to exist.'); } - const nextContextValue = getChildHostContext(currentContextValue, fiber.type, rootInstance); - if (currentContextValue === nextContextValue) { + + const context = contextStackCursor.current; + const rootInstance = rootInstanceStackCursor.current; + const nextContext = getChildHostContext(context, fiber.type, rootInstance); + + if (context === nextContext) { return; } - contextDepth++; - contextFibers[contextDepth] = fiber; - contextValues[contextDepth] = nextContextValue; - currentContextValue = nextContextValue; + + push(contextStackCursor, nextContext, fiber); } function popHostContext(fiber : Fiber) : void { - if (contextDepth === -1) { - return; - } - if (contextFibers == null || contextValues == null) { - throw new Error('Expected host context stacks to exist when index is more than -1.'); - } - if (fiber !== contextFibers[contextDepth]) { + if (contextStackCursor.current == null) { return; } - contextFibers[contextDepth] = null; - contextValues[contextDepth] = null; - contextDepth--; - if (contextDepth > -1) { - currentContextValue = contextValues[contextDepth]; - } else { - currentContextValue = null; - } + pop(contextStackCursor, fiber); } function resetHostContainer() { - // Reset portal stack pointer because we're starting from the very top. - portalDepth = -1; - // Reset current container state. - rootInstance = null; - contextDepth = -1; - currentContextValue = null; + reset(contextStackCursor); + reset(rootInstanceStackCursor); } return { - getRootHostContainer, getHostContext, - - pushHostContext, + getRootHostContainer, + popHostContainer, popHostContext, - pushHostContainer, - popHostContainer, + pushHostContext, resetHostContainer, }; }; diff --git a/src/renderers/shared/fiber/ReactFiberStack.js b/src/renderers/shared/fiber/ReactFiberStack.js new file mode 100644 index 000000000000..9f7b98bee850 --- /dev/null +++ b/src/renderers/shared/fiber/ReactFiberStack.js @@ -0,0 +1,102 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFiberStack + * @flow + */ + +'use strict'; + +import type { Fiber } from 'ReactFiber'; + +export type StackCursor = {| + current: any +|}; + +const emptyObject = require('emptyObject'); +const warning = require('warning'); + +const valueStack : Array = []; + +if (__DEV__) { + var fiberStack : Array = []; +} + +let index = -1; + +exports.createCursor = function() : StackCursor { + return { + current: null, + }; +}; + +exports.isEmpty = function() : boolean { + return index === -1; +}; + +exports.pop = function( + cursor : StackCursor, + fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber +) : void { + if (index < 0) { + if (__DEV__) { + warning(false, 'Unexpected pop.'); + } + return; + } + + if (__DEV__) { + if (fiber !== fiberStack[index]) { + warning(false, 'Unexpected Fiber popped.'); + } + } + + valueStack[index] = emptyObject; + + if (__DEV__) { + fiberStack[index] = emptyObject; + } + + index--; + + cursor.current = index > -1 + ? valueStack[index] + : null; +}; + +exports.push = function( + cursor : StackCursor, + value : any, + fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber +) : void { + cursor.current = value; + + index++; + + valueStack[index] = value; + + if (__DEV__) { + fiberStack[index] = fiber; + } +}; + +exports.reset = function( + cursor : StackCursor, +) : void { + while (index > -1) { + valueStack[index] = emptyObject; + + if (__DEV__) { + fiberStack[index] = emptyObject; + } + + index--; + } + + cursor.current = null; +}; From a0902fc30675808193f23b22452c5bfc0cd704e7 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 09:06:56 -0800 Subject: [PATCH 02/10] Generic type added to StackCursor. Default emptyObject replaced with null. --- .../shared/fiber/ReactFiberContext.js | 7 ++-- .../shared/fiber/ReactFiberHostContext.js | 8 +++-- src/renderers/shared/fiber/ReactFiberStack.js | 35 +++++++++---------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js index 94b2c2f8c6e8..6caea831b38e 100644 --- a/src/renderers/shared/fiber/ReactFiberContext.js +++ b/src/renderers/shared/fiber/ReactFiberContext.js @@ -36,11 +36,11 @@ if (__DEV__) { var checkReactTypeSpec = require('checkReactTypeSpec'); } -let contextStackCursor : StackCursor = createCursor(); -let didPerformWorkStackCursor : StackCursor = createCursor(); +let contextStackCursor : StackCursor = createCursor((null: ?Object)); +let didPerformWorkStackCursor : StackCursor = createCursor((null: ?boolean)); function getUnmaskedContext() { - return contextStackCursor.current; + return contextStackCursor.current || emptyObject; } exports.getMaskedContext = function(workInProgress : Fiber) { @@ -52,6 +52,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) { const unmaskedContext = getUnmaskedContext(); const context = {}; + for (let key in contextTypes) { context[key] = unmaskedContext[key]; } diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index 9effc291d4f7..8997d33e0f14 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -16,6 +16,8 @@ import type { Fiber } from 'ReactFiber'; import type { HostConfig } from 'ReactFiberReconciler'; import type { StackCursor } from 'ReactFiberStack'; +const emptyObject = require('emptyObject'); + const { createCursor, pop, @@ -41,8 +43,8 @@ module.exports = function( getRootHostContext, } = config; - let contextStackCursor : StackCursor = createCursor(); - let rootInstanceStackCursor : StackCursor = createCursor(); + let contextStackCursor : StackCursor = createCursor((null: ?CX)); + let rootInstanceStackCursor : StackCursor = createCursor((null: ?C)); function getRootHostContainer() : C { if (rootInstanceStackCursor.current == null) { @@ -81,7 +83,7 @@ module.exports = function( throw new Error('Expected root host context to exist.'); } - const context = contextStackCursor.current; + const context = contextStackCursor.current || emptyObject; const rootInstance = rootInstanceStackCursor.current; const nextContext = getChildHostContext(context, fiber.type, rootInstance); diff --git a/src/renderers/shared/fiber/ReactFiberStack.js b/src/renderers/shared/fiber/ReactFiberStack.js index 9f7b98bee850..f4e11e0e0f47 100644 --- a/src/renderers/shared/fiber/ReactFiberStack.js +++ b/src/renderers/shared/fiber/ReactFiberStack.js @@ -14,14 +14,13 @@ import type { Fiber } from 'ReactFiber'; -export type StackCursor = {| - current: any +export type StackCursor = {| + current: T |}; -const emptyObject = require('emptyObject'); const warning = require('warning'); -const valueStack : Array = []; +const valueStack : Array = []; if (__DEV__) { var fiberStack : Array = []; @@ -29,9 +28,9 @@ if (__DEV__) { let index = -1; -exports.createCursor = function() : StackCursor { +exports.createCursor = function(defaultValue : T) : StackCursor { return { - current: null, + current: defaultValue, }; }; @@ -39,8 +38,8 @@ exports.isEmpty = function() : boolean { return index === -1; }; -exports.pop = function( - cursor : StackCursor, +exports.pop = function( + cursor : StackCursor, fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber ) : void { if (index < 0) { @@ -56,21 +55,21 @@ exports.pop = function( } } - valueStack[index] = emptyObject; + valueStack[index] = null; if (__DEV__) { - fiberStack[index] = emptyObject; + fiberStack[index] = null; } index--; cursor.current = index > -1 ? valueStack[index] - : null; + : (null : any); }; -exports.push = function( - cursor : StackCursor, +exports.push = function( + cursor : StackCursor, value : any, fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber ) : void { @@ -85,18 +84,18 @@ exports.push = function( } }; -exports.reset = function( - cursor : StackCursor, +exports.reset = function( + cursor : StackCursor, ) : void { while (index > -1) { - valueStack[index] = emptyObject; + valueStack[index] = null; if (__DEV__) { - fiberStack[index] = emptyObject; + fiberStack[index] = null; } index--; } - cursor.current = null; + cursor.current = (null : any); }; From 0c1fdfd41daaf477e05b566def55975cb82a459b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 10:04:36 -0800 Subject: [PATCH 03/10] Combined unwindContext and unwindHostContext into unwindContexts --- .../shared/fiber/ReactFiberContext.js | 10 --------- .../shared/fiber/ReactFiberScheduler.js | 22 +++++++++++++------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js index 6caea831b38e..33766583a5b7 100644 --- a/src/renderers/shared/fiber/ReactFiberContext.js +++ b/src/renderers/shared/fiber/ReactFiberContext.js @@ -159,13 +159,3 @@ exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object { } return node.stateNode.context; }; - -exports.unwindContext = function(from : Fiber, to: Fiber) { - let node = from; - while (node && (node !== to) && (node.alternate !== to)) { - if (isContextProvider(node)) { - popContextProvider(node); - } - node = node.return; - } -}; diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index defece414bdd..10ccf0098d81 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -17,6 +17,11 @@ import type { FiberRoot } from 'ReactFiberRoot'; import type { HostConfig, Deadline } from 'ReactFiberReconciler'; import type { PriorityLevel } from 'ReactPriorityLevel'; +var { + isContextProvider, + popContextProvider, +} = require('ReactFiberContext'); + var ReactFiberBeginWork = require('ReactFiberBeginWork'); var ReactFiberCompleteWork = require('ReactFiberCompleteWork'); var ReactFiberCommitWork = require('ReactFiberCommitWork'); @@ -59,7 +64,6 @@ var { var { resetContext, - unwindContext, } = require('ReactFiberContext'); if (__DEV__) { @@ -724,8 +728,7 @@ module.exports = function(config : HostConfig(config : HostConfig(config : HostConfig Date: Wed, 21 Dec 2016 10:14:59 -0800 Subject: [PATCH 04/10] Tightened up Fiber | null type in ReactFiberStack to always require Fiber --- .../shared/fiber/ReactFiberCompleteWork.js | 2 +- .../shared/fiber/ReactFiberHostContext.js | 22 ++++++++++--------- src/renderers/shared/fiber/ReactFiberStack.js | 4 ++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberCompleteWork.js b/src/renderers/shared/fiber/ReactFiberCompleteWork.js index 07da4c25de9f..f58a689d1441 100644 --- a/src/renderers/shared/fiber/ReactFiberCompleteWork.js +++ b/src/renderers/shared/fiber/ReactFiberCompleteWork.js @@ -303,7 +303,7 @@ module.exports = function( // TODO: Only mark this as an update if we have any pending callbacks. markUpdate(workInProgress); workInProgress.memoizedProps = workInProgress.pendingProps; - popHostContainer(); + popHostContainer(workInProgress); return null; // Error cases diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index 8997d33e0f14..91a67f01bdbd 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -28,9 +28,9 @@ const { export type HostContext = { getHostContext() : CX, getRootHostContainer() : C, - popHostContainer() : void, + popHostContainer(fiber : Fiber) : void, popHostContext(fiber : Fiber) : void, - pushHostContainer(container : C) : void, + pushHostContainer(fiber : Fiber, container : C) : void, pushHostContext(fiber : Fiber) : void, resetHostContainer() : void, }; @@ -53,21 +53,21 @@ module.exports = function( return rootInstanceStackCursor.current; } - // TODO (bvaughn) Pass the host container fiber to push() - function pushHostContainer(nextRootInstance : C) { + function pushHostContainer(fiber : Fiber, nextRootInstance : C) { // Push current root instance onto the stack; // This allows us to reset root when portals are popped. - push(rootInstanceStackCursor, nextRootInstance, null); + push(rootInstanceStackCursor, nextRootInstance, fiber); const nextRootContext = getRootHostContext(nextRootInstance); - push(contextStackCursor, nextRootContext, null); + // TODO (bvaughn) Push context-providing Fiber with its own cursor + push(contextStackCursor, nextRootContext, fiber); } - // TODO (bvaughn) Pass the host container fiber to pop() - function popHostContainer() { - pop(contextStackCursor, null); - pop(rootInstanceStackCursor, null); + function popHostContainer(fiber : Fiber) { + pop(contextStackCursor, fiber); + // TODO (bvaughn) Pop context-providing Fiber with its own cursor + pop(rootInstanceStackCursor, fiber); } function getHostContext() : CX { @@ -91,6 +91,7 @@ module.exports = function( return; } + // TODO (bvaughn) Push context-providing Fiber with its own cursor push(contextStackCursor, nextContext, fiber); } @@ -99,6 +100,7 @@ module.exports = function( return; } + // TODO (bvaughn) Check context-providing Fiber and only pop if it matches pop(contextStackCursor, fiber); } diff --git a/src/renderers/shared/fiber/ReactFiberStack.js b/src/renderers/shared/fiber/ReactFiberStack.js index f4e11e0e0f47..6c0dc8668f18 100644 --- a/src/renderers/shared/fiber/ReactFiberStack.js +++ b/src/renderers/shared/fiber/ReactFiberStack.js @@ -40,7 +40,7 @@ exports.isEmpty = function() : boolean { exports.pop = function( cursor : StackCursor, - fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber + fiber: Fiber, ) : void { if (index < 0) { if (__DEV__) { @@ -71,7 +71,7 @@ exports.pop = function( exports.push = function( cursor : StackCursor, value : any, - fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber + fiber: Fiber, ) : void { cursor.current = value; From 6b51e037ca773bd5afd68bb13d5f913ab8e68b3f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 13:38:35 -0800 Subject: [PATCH 05/10] Avoid popping hosts that do not provide unique contexts --- .../shared/fiber/ReactFiberBeginWork.js | 2 +- .../shared/fiber/ReactFiberHostContext.js | 18 +++++++++++++----- .../shared/fiber/ReactFiberScheduler.js | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberBeginWork.js b/src/renderers/shared/fiber/ReactFiberBeginWork.js index c5bb64b94fdf..ce503be9ec94 100644 --- a/src/renderers/shared/fiber/ReactFiberBeginWork.js +++ b/src/renderers/shared/fiber/ReactFiberBeginWork.js @@ -633,4 +633,4 @@ module.exports = function( beginFailedWork, }; -}; \ No newline at end of file +}; diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index 91a67f01bdbd..f49f41ea95cc 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -44,6 +44,7 @@ module.exports = function( } = config; let contextStackCursor : StackCursor = createCursor((null: ?CX)); + let contextFiberStackCursor : StackCursor = createCursor((null: ?Fiber)); let rootInstanceStackCursor : StackCursor = createCursor((null: ?C)); function getRootHostContainer() : C { @@ -60,13 +61,15 @@ module.exports = function( const nextRootContext = getRootHostContext(nextRootInstance); - // TODO (bvaughn) Push context-providing Fiber with its own cursor + // Track the context and the Fiber that provided it. + // This enables us to pop only Fibers that provide unique contexts. + push(contextFiberStackCursor, fiber, fiber); push(contextStackCursor, nextRootContext, fiber); } function popHostContainer(fiber : Fiber) { pop(contextStackCursor, fiber); - // TODO (bvaughn) Pop context-providing Fiber with its own cursor + pop(contextFiberStackCursor, fiber); pop(rootInstanceStackCursor, fiber); } @@ -87,21 +90,26 @@ module.exports = function( const rootInstance = rootInstanceStackCursor.current; const nextContext = getChildHostContext(context, fiber.type, rootInstance); + // Don't push this Fiber's context unless it's unique. if (context === nextContext) { return; } - // TODO (bvaughn) Push context-providing Fiber with its own cursor + // Track the context and the Fiber that provided it. + // This enables us to pop only Fibers that provide unique contexts. + push(contextFiberStackCursor, fiber, fiber); push(contextStackCursor, nextContext, fiber); } function popHostContext(fiber : Fiber) : void { - if (contextStackCursor.current == null) { + // Do not pop unless this Fiber provided the current context. + // pushHostContext() only pushes Fibers that provide unique contexts. + if (contextFiberStackCursor.current !== fiber) { return; } - // TODO (bvaughn) Check context-providing Fiber and only pop if it matches pop(contextStackCursor, fiber); + pop(contextFiberStackCursor, fiber); } function resetHostContainer() { diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index 10ccf0098d81..c59167a1c5af 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -1126,4 +1126,4 @@ module.exports = function(config : HostConfig Date: Wed, 21 Dec 2016 14:06:20 -0800 Subject: [PATCH 06/10] Avoid reading the same StackCursor current multiple times --- .../shared/fiber/ReactFiberHostContext.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index f49f41ea95cc..a10688aa7f89 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -48,10 +48,11 @@ module.exports = function( let rootInstanceStackCursor : StackCursor = createCursor((null: ?C)); function getRootHostContainer() : C { - if (rootInstanceStackCursor.current == null) { + const rootInstance = rootInstanceStackCursor.current; + if (rootInstance == null) { throw new Error('Expected root container to exist.'); } - return rootInstanceStackCursor.current; + return rootInstance; } function pushHostContainer(fiber : Fiber, nextRootInstance : C) { @@ -74,20 +75,20 @@ module.exports = function( } function getHostContext() : CX { - if (contextStackCursor.current == null) { + const context = contextStackCursor.current; + if (context == null) { throw new Error('Expected host context to exist.'); } - - return contextStackCursor.current; + return context; } function pushHostContext(fiber : Fiber) : void { - if (rootInstanceStackCursor.current == null) { + const rootInstance = rootInstanceStackCursor.current; + if (rootInstance == null) { throw new Error('Expected root host context to exist.'); } const context = contextStackCursor.current || emptyObject; - const rootInstance = rootInstanceStackCursor.current; const nextContext = getChildHostContext(context, fiber.type, rootInstance); // Don't push this Fiber's context unless it's unique. From e7a591ed57a1dddd61307f934c41caf520bb3391 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 15:05:46 -0800 Subject: [PATCH 07/10] ReactFiberStack cursor values are no longer corrupted on pop --- src/renderers/shared/fiber/ReactFiberStack.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberStack.js b/src/renderers/shared/fiber/ReactFiberStack.js index 6c0dc8668f18..4a38a2f8f74f 100644 --- a/src/renderers/shared/fiber/ReactFiberStack.js +++ b/src/renderers/shared/fiber/ReactFiberStack.js @@ -55,6 +55,10 @@ exports.pop = function( } } + cursor.current = index > 0 + ? valueStack[index] + : (null : any); + valueStack[index] = null; if (__DEV__) { @@ -62,10 +66,6 @@ exports.pop = function( } index--; - - cursor.current = index > -1 - ? valueStack[index] - : (null : any); }; exports.push = function( @@ -73,15 +73,15 @@ exports.push = function( value : any, fiber: Fiber, ) : void { - cursor.current = value; - index++; - valueStack[index] = value; + valueStack[index] = cursor.current; if (__DEV__) { fiberStack[index] = fiber; } + + cursor.current = value; }; exports.reset = function( From ea6530d13a1c16602317ff87c8b93a27129ad25e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 16:48:14 -0800 Subject: [PATCH 08/10] Removed exact object type for StackCursor Flow type This was causing the 'react:extract-errors' Gulp taks to fail. --- src/renderers/shared/fiber/ReactFiberStack.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberStack.js b/src/renderers/shared/fiber/ReactFiberStack.js index 4a38a2f8f74f..7c41c93b685c 100644 --- a/src/renderers/shared/fiber/ReactFiberStack.js +++ b/src/renderers/shared/fiber/ReactFiberStack.js @@ -14,9 +14,9 @@ import type { Fiber } from 'ReactFiber'; -export type StackCursor = {| +export type StackCursor = { current: T -|}; +}; const warning = require('warning'); From 2f2a44007c1fdc1cd2726d79b2e3aba6bbfdb677 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 17:32:53 -0800 Subject: [PATCH 09/10] Improved Flowtypes in response to code review feedback --- src/renderers/shared/fiber/ReactFiberContext.js | 10 ++++++---- src/renderers/shared/fiber/ReactFiberHostContext.js | 6 ++++-- src/renderers/shared/fiber/ReactFiberStack.js | 12 +++--------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js index 33766583a5b7..70e53de431f8 100644 --- a/src/renderers/shared/fiber/ReactFiberContext.js +++ b/src/renderers/shared/fiber/ReactFiberContext.js @@ -37,7 +37,7 @@ if (__DEV__) { } let contextStackCursor : StackCursor = createCursor((null: ?Object)); -let didPerformWorkStackCursor : StackCursor = createCursor((null: ?boolean)); +let didPerformWorkStackCursor : StackCursor = createCursor(false); function getUnmaskedContext() { return contextStackCursor.current || emptyObject; @@ -66,7 +66,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) { }; exports.hasContextChanged = function() : boolean { - return Boolean(didPerformWorkStackCursor.current); + return didPerformWorkStackCursor.current; }; function isContextProvider(fiber : Fiber) : boolean { @@ -136,8 +136,10 @@ exports.pushContextProvider = function(workInProgress : Fiber, didPerformWork : }; exports.resetContext = function() : void { - reset(contextStackCursor); - reset(didPerformWorkStackCursor); + reset(); + + contextStackCursor.current = null; + didPerformWorkStackCursor.current = false; }; exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object { diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index a10688aa7f89..94a0e636204d 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -114,8 +114,10 @@ module.exports = function( } function resetHostContainer() { - reset(contextStackCursor); - reset(rootInstanceStackCursor); + reset(); + + contextStackCursor.current = null; + rootInstanceStackCursor.current = null; } return { diff --git a/src/renderers/shared/fiber/ReactFiberStack.js b/src/renderers/shared/fiber/ReactFiberStack.js index 7c41c93b685c..3d51b4aec64c 100644 --- a/src/renderers/shared/fiber/ReactFiberStack.js +++ b/src/renderers/shared/fiber/ReactFiberStack.js @@ -55,9 +55,7 @@ exports.pop = function( } } - cursor.current = index > 0 - ? valueStack[index] - : (null : any); + cursor.current = valueStack[index]; valueStack[index] = null; @@ -70,7 +68,7 @@ exports.pop = function( exports.push = function( cursor : StackCursor, - value : any, + value : T, fiber: Fiber, ) : void { index++; @@ -84,9 +82,7 @@ exports.push = function( cursor.current = value; }; -exports.reset = function( - cursor : StackCursor, -) : void { +exports.reset = function() : void { while (index > -1) { valueStack[index] = null; @@ -96,6 +92,4 @@ exports.reset = function( index--; } - - cursor.current = (null : any); }; From d1e604004a0f820ffcef7b7d023501e9ae017a6d Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 21 Dec 2016 17:39:39 -0800 Subject: [PATCH 10/10] Removed redundant calls to ReactFiberStack.reset() --- src/renderers/shared/fiber/ReactFiberContext.js | 3 --- src/renderers/shared/fiber/ReactFiberHostContext.js | 3 --- src/renderers/shared/fiber/ReactFiberScheduler.js | 4 ++++ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js index 70e53de431f8..013a083f8cb8 100644 --- a/src/renderers/shared/fiber/ReactFiberContext.js +++ b/src/renderers/shared/fiber/ReactFiberContext.js @@ -29,7 +29,6 @@ const { createCursor, pop, push, - reset, } = require('ReactFiberStack'); if (__DEV__) { @@ -136,8 +135,6 @@ exports.pushContextProvider = function(workInProgress : Fiber, didPerformWork : }; exports.resetContext = function() : void { - reset(); - contextStackCursor.current = null; didPerformWorkStackCursor.current = false; }; diff --git a/src/renderers/shared/fiber/ReactFiberHostContext.js b/src/renderers/shared/fiber/ReactFiberHostContext.js index 94a0e636204d..529271de6ad3 100644 --- a/src/renderers/shared/fiber/ReactFiberHostContext.js +++ b/src/renderers/shared/fiber/ReactFiberHostContext.js @@ -22,7 +22,6 @@ const { createCursor, pop, push, - reset, } = require('ReactFiberStack'); export type HostContext = { @@ -114,8 +113,6 @@ module.exports = function( } function resetHostContainer() { - reset(); - contextStackCursor.current = null; rootInstanceStackCursor.current = null; } diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index c59167a1c5af..2e7635cfe764 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -21,6 +21,7 @@ var { isContextProvider, popContextProvider, } = require('ReactFiberContext'); +const { reset } = require('ReactFiberStack'); var ReactFiberBeginWork = require('ReactFiberBeginWork'); var ReactFiberCompleteWork = require('ReactFiberCompleteWork'); @@ -159,6 +160,9 @@ module.exports = function(config : HostConfig