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
+};