@samitouri / QOS-React / commits / 3d0cf47d26

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.

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.

Brian Vaughn committed Dec 20, 2016 at 18:04 UTC 3d0cf47d2621cd3690e6ede19a3db08b6ff89aca
5 files changed +181 -119
src/renderers/shared/fiber/ReactFiberBeginWork.js
+10 -5
@@ -270,14 +270,19 @@ module.exports = function<T, P, I, TI, C, CX>(
270 const root = (workInProgress.stateNode : FiberRoot);
271 if (root.pendingContext) {
272 pushTopLevelContextObject(
273 + workInProgress,
274 root.pendingContext,
275 root.pendingContext !== root.context
276 );
277 } else {
277 - pushTopLevelContextObject(root.context, false);
278 + pushTopLevelContextObject(
279 + workInProgress,
280 + root.context,
281 + false
282 + );
283 }
284
280 - pushHostContainer(root.containerInfo);
285 + pushHostContainer(workInProgress, root.containerInfo);
286
287 const updateQueue = workInProgress.updateQueue;
288 if (updateQueue) {
@@ -444,7 +449,7 @@ module.exports = function<T, P, I, TI, C, CX>(
449 }
450
451 function updatePortalComponent(current, workInProgress) {
447 - pushHostContainer(workInProgress.stateNode.containerInfo);
452 + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
453 const priorityLevel = workInProgress.pendingWorkPriority;
454 let nextChildren = workInProgress.pendingProps;
455 if (hasContextChanged()) {
@@ -535,7 +540,7 @@ module.exports = function<T, P, I, TI, C, CX>(
540 }
541 break;
542 case HostPortal:
538 - pushHostContainer(workInProgress.stateNode.containerInfo);
543 + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
544 break;
545 }
546 // TODO: What if this is currently in progress?
@@ -628,4 +633,4 @@ module.exports = function<T, P, I, TI, C, CX>(
633 beginFailedWork,
634 };
635
631 -};
636 +};
\ No newline at end of file
src/renderers/shared/fiber/ReactFiberCompleteWork.js
+1 -1
@@ -176,7 +176,7 @@ module.exports = function<T, P, I, TI, C, CX>(
176 case ClassComponent: {
177 // We are leaving this subtree, so pop context if any.
178 if (isContextProvider(workInProgress)) {
179 - popContextProvider();
179 + popContextProvider(workInProgress);
180 }
181 // Don't use the state queue to compute the memoized state. We already
182 // merged it and assigned it to the instance. Transfer it from there.
src/renderers/shared/fiber/ReactFiberContext.js
+24 -23
@@ -13,6 +13,7 @@
13 'use strict';
14
15 import type { Fiber } from 'ReactFiber';
16 +import type { StackCursor } from 'ReactFiberStack';
17
18 var emptyObject = require('emptyObject');
19 var invariant = require('invariant');
@@ -24,20 +25,22 @@ var {
25 ClassComponent,
26 HostRoot,
27 } = require('ReactTypeOfWork');
28 +const {
29 + createCursor,
30 + pop,
31 + push,
32 + reset,
33 +} = require('ReactFiberStack');
34
35 if (__DEV__) {
36 var checkReactTypeSpec = require('checkReactTypeSpec');
37 }
38
32 -let index = -1;
33 -const contextStack : Array<Object> = [];
34 -const didPerformWorkStack : Array<boolean> = [];
39 +let contextStackCursor : StackCursor = createCursor();
40 +let didPerformWorkStackCursor : StackCursor = createCursor();
41
42 function getUnmaskedContext() {
37 - if (index === -1) {
38 - return emptyObject;
39 - }
40 - return contextStack[index];
43 + return contextStackCursor.current;
44 }
45
46 exports.getMaskedContext = function(workInProgress : Fiber) {
@@ -62,7 +65,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) {
65 };
66
67 exports.hasContextChanged = function() : boolean {
65 - return index > -1 && didPerformWorkStack[index];
68 + return Boolean(didPerformWorkStackCursor.current);
69 };
70
71 function isContextProvider(fiber : Fiber) : boolean {
@@ -75,19 +78,17 @@ function isContextProvider(fiber : Fiber) : boolean {
78 }
79 exports.isContextProvider = isContextProvider;
80
78 -function popContextProvider() : void {
79 - invariant(index > -1, 'Unexpected context pop');
80 - contextStack[index] = emptyObject;
81 - didPerformWorkStack[index] = false;
82 - index--;
81 +function popContextProvider(fiber : Fiber) : void {
82 + pop(didPerformWorkStackCursor, fiber);
83 + pop(contextStackCursor, fiber);
84 }
85 exports.popContextProvider = popContextProvider;
86
86 -exports.pushTopLevelContextObject = function(context : Object, didChange : boolean) : void {
87 - invariant(index === -1, 'Unexpected context found on stack');
88 - index++;
89 - contextStack[index] = context;
90 - didPerformWorkStack[index] = didChange;
87 +exports.pushTopLevelContextObject = function(fiber : Fiber, context : Object, didChange : boolean) : void {
88 + invariant(contextStackCursor.cursor == null, 'Unexpected context found on stack');
89 +
90 + push(contextStackCursor, context, fiber);
91 + push(didPerformWorkStackCursor, didChange, fiber);
92 };
93
94 function processChildContext(fiber : Fiber, parentContext : Object, isReconciling : boolean): Object {
@@ -129,13 +130,13 @@ exports.pushContextProvider = function(workInProgress : Fiber, didPerformWork :
130 instance.__reactInternalMemoizedMergedChildContext = mergedContext;
131 }
132
132 - index++;
133 - contextStack[index] = mergedContext;
134 - didPerformWorkStack[index] = didPerformWork;
133 + push(contextStackCursor, mergedContext, workInProgress);
134 + push(didPerformWorkStackCursor, didPerformWork, workInProgress);
135 };
136
137 exports.resetContext = function() : void {
138 - index = -1;
138 + reset(contextStackCursor);
139 + reset(didPerformWorkStackCursor);
140 };
141
142 exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object {
@@ -162,7 +163,7 @@ exports.unwindContext = function(from : Fiber, to: Fiber) {
163 let node = from;
164 while (node && (node !== to) && (node.alternate !== to)) {
165 if (isContextProvider(node)) {
165 - popContextProvider();
166 + popContextProvider(node);
167 }
168 node = node.return;
169 }
src/renderers/shared/fiber/ReactFiberHostContext.js
+44 -90
@@ -14,16 +14,22 @@
14
15 import type { Fiber } from 'ReactFiber';
16 import type { HostConfig } from 'ReactFiberReconciler';
17 +import type { StackCursor } from 'ReactFiberStack';
18 +
19 +const {
20 + createCursor,
21 + pop,
22 + push,
23 + reset,
24 +} = require('ReactFiberStack');
25
26 export type HostContext<C, CX> = {
19 - getRootHostContainer() : C,
27 getHostContext() : CX,
21 -
22 - pushHostContext(fiber : Fiber) : void,
28 + getRootHostContainer() : C,
29 + popHostContainer() : void,
30 popHostContext(fiber : Fiber) : void,
24 -
31 pushHostContainer(container : C) : void,
26 - popHostContainer() : void,
32 + pushHostContext(fiber : Fiber) : void,
33 resetHostContainer() : void,
34 };
35
@@ -35,129 +41,77 @@ module.exports = function<T, P, I, TI, C, CX>(
41 getRootHostContext,
42 } = config;
43
38 - // Context stack is reused across the subtrees.
39 - // We use a null sentinel on the fiber stack to separate them.
40 - let contextFibers : Array<Fiber | null> = [];
41 - let contextValues : Array<CX | null> = [];
42 - let contextDepth : number = -1;
43 - // Current context for fast access.
44 - let currentContextValue : CX | null = null;
45 - // Current root instance for fast access.
46 - let rootInstance : C | null = null;
47 - // A stack of outer root instances if we're in a portal.
48 - let portalStack : Array<C | null> = [];
49 - let portalDepth : number = -1;
44 + let contextStackCursor : StackCursor = createCursor();
45 + let rootInstanceStackCursor : StackCursor = createCursor();
46
47 function getRootHostContainer() : C {
52 - if (rootInstance == null) {
48 + if (rootInstanceStackCursor.current == null) {
49 throw new Error('Expected root container to exist.');
50 }
55 - return rootInstance;
51 + return rootInstanceStackCursor.current;
52 }
53
54 + // TODO (bvaughn) Pass the host container fiber to push()
55 function pushHostContainer(nextRootInstance : C) {
56 + // Push current root instance onto the stack;
57 + // This allows us to reset root when portals are popped.
58 + push(rootInstanceStackCursor, nextRootInstance, null);
59 +
60 const nextRootContext = getRootHostContext(nextRootInstance);
60 - if (rootInstance == null) {
61 - // We're entering a root.
62 - rootInstance = nextRootInstance;
63 - } else {
64 - // We're entering a portal.
65 - // Save the current root to the portal stack.
66 - portalDepth++;
67 - portalStack[portalDepth] = rootInstance;
68 - rootInstance = nextRootInstance;
69 - }
70 - // Push the next root or portal context.
71 - contextDepth++;
72 - contextFibers[contextDepth] = null;
73 - contextValues[contextDepth] = nextRootContext;
74 - currentContextValue = nextRootContext;
61 +
62 + push(contextStackCursor, nextRootContext, null);
63 }
64
65 + // TODO (bvaughn) Pass the host container fiber to pop()
66 function popHostContainer() {
78 - if (portalDepth === -1) {
79 - // We're popping the root.
80 - rootInstance = null;
81 - currentContextValue = null;
82 - contextDepth = -1;
83 - } else {
84 - // We're popping a portal.
85 - // Restore the root instance.
86 - rootInstance = portalStack[portalDepth];
87 - portalStack[portalDepth] = null;
88 - portalDepth--;
89 - // If we pushed any context while in a portal, we need to roll it back.
90 - if (contextDepth > -1) {
91 - contextDepth--;
92 - if (contextDepth > -1) {
93 - currentContextValue = contextValues[contextDepth];
94 - } else {
95 - currentContextValue = null;
96 - }
97 - }
98 - }
67 + pop(contextStackCursor, null);
68 + pop(rootInstanceStackCursor, null);
69 }
70
71 function getHostContext() : CX {
102 - if (currentContextValue == null) {
72 + if (contextStackCursor.current == null) {
73 throw new Error('Expected host context to exist.');
74 }
105 - return currentContextValue;
75 +
76 + return contextStackCursor.current;
77 }
78
79 function pushHostContext(fiber : Fiber) : void {
109 - if (currentContextValue == null) {
80 + if (rootInstanceStackCursor.current == null) {
81 throw new Error('Expected root host context to exist.');
82 }
112 - const nextContextValue = getChildHostContext(currentContextValue, fiber.type, rootInstance);
113 - if (currentContextValue === nextContextValue) {
83 +
84 + const context = contextStackCursor.current;
85 + const rootInstance = rootInstanceStackCursor.current;
86 + const nextContext = getChildHostContext(context, fiber.type, rootInstance);
87 +
88 + if (context === nextContext) {
89 return;
90 }
116 - contextDepth++;
117 - contextFibers[contextDepth] = fiber;
118 - contextValues[contextDepth] = nextContextValue;
119 - currentContextValue = nextContextValue;
91 +
92 + push(contextStackCursor, nextContext, fiber);
93 }
94
95 function popHostContext(fiber : Fiber) : void {
123 - if (contextDepth === -1) {
124 - return;
125 - }
126 - if (contextFibers == null || contextValues == null) {
127 - throw new Error('Expected host context stacks to exist when index is more than -1.');
128 - }
129 - if (fiber !== contextFibers[contextDepth]) {
96 + if (contextStackCursor.current == null) {
97 return;
98 }
99
133 - contextFibers[contextDepth] = null;
134 - contextValues[contextDepth] = null;
135 - contextDepth--;
136 - if (contextDepth > -1) {
137 - currentContextValue = contextValues[contextDepth];
138 - } else {
139 - currentContextValue = null;
140 - }
100 + pop(contextStackCursor, fiber);
101 }
102
103 function resetHostContainer() {
144 - // Reset portal stack pointer because we're starting from the very top.
145 - portalDepth = -1;
146 - // Reset current container state.
147 - rootInstance = null;
148 - contextDepth = -1;
149 - currentContextValue = null;
104 + reset(contextStackCursor);
105 + reset(rootInstanceStackCursor);
106 }
107
108 return {
153 - getRootHostContainer,
109 getHostContext,
155 -
156 - pushHostContext,
110 + getRootHostContainer,
111 + popHostContainer,
112 popHostContext,
158 -
113 pushHostContainer,
160 - popHostContainer,
114 + pushHostContext,
115 resetHostContainer,
116 };
117 };
src/renderers/shared/fiber/ReactFiberStack.js new
+102
@@ -0,0 +1,102 @@
1 +/**
2 + * Copyright 2013-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule ReactFiberStack
10 + * @flow
11 + */
12 +
13 +'use strict';
14 +
15 +import type { Fiber } from 'ReactFiber';
16 +
17 +export type StackCursor = {|
18 + current: any
19 +|};
20 +
21 +const emptyObject = require('emptyObject');
22 +const warning = require('warning');
23 +
24 +const valueStack : Array<Object> = [];
25 +
26 +if (__DEV__) {
27 + var fiberStack : Array<Fiber | null> = [];
28 +}
29 +
30 +let index = -1;
31 +
32 +exports.createCursor = function() : StackCursor {
33 + return {
34 + current: null,
35 + };
36 +};
37 +
38 +exports.isEmpty = function() : boolean {
39 + return index === -1;
40 +};
41 +
42 +exports.pop = function(
43 + cursor : StackCursor,
44 + fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber
45 +) : void {
46 + if (index < 0) {
47 + if (__DEV__) {
48 + warning(false, 'Unexpected pop.');
49 + }
50 + return;
51 + }
52 +
53 + if (__DEV__) {
54 + if (fiber !== fiberStack[index]) {
55 + warning(false, 'Unexpected Fiber popped.');
56 + }
57 + }
58 +
59 + valueStack[index] = emptyObject;
60 +
61 + if (__DEV__) {
62 + fiberStack[index] = emptyObject;
63 + }
64 +
65 + index--;
66 +
67 + cursor.current = index > -1
68 + ? valueStack[index]
69 + : null;
70 +};
71 +
72 +exports.push = function(
73 + cursor : StackCursor,
74 + value : any,
75 + fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber
76 +) : void {
77 + cursor.current = value;
78 +
79 + index++;
80 +
81 + valueStack[index] = value;
82 +
83 + if (__DEV__) {
84 + fiberStack[index] = fiber;
85 + }
86 +};
87 +
88 +exports.reset = function(
89 + cursor : StackCursor,
90 +) : void {
91 + while (index > -1) {
92 + valueStack[index] = emptyObject;
93 +
94 + if (__DEV__) {
95 + fiberStack[index] = emptyObject;
96 + }
97 +
98 + index--;
99 + }
100 +
101 + cursor.current = null;
102 +};