@samitouri / QOS-React-2 / commits / 0f34bdc622

[Fiber] Remove array indirection in host context (#8544)

* Remove array indirection in host context * Keep a single context stack with a null sentinel This lets us keep subtrees separated without maintaining independent context arrays for subtrees. * There is always exactly one null by the time we pop a portal I was trying to be smart but didn't need to. * Cache current context

Dan Abramov committed Dec 12, 2016 at 15:20 UTC 0f34bdc622cc79835b599d2f1d8b0889b138e579
1 file changed +68 -61
src/renderers/shared/fiber/ReactFiberHostContext.js
+68 -61
@@ -34,113 +34,120 @@ module.exports = function<T, P, I, TI, C, CX>(
34 getChildHostContext,
35 } = config;
36
37 - type ContainerState = {
38 - rootInstance : C,
39 - contextFibers : Array<Fiber | null> | null,
40 - contextValues : Array<CX | null> | null,
41 - contextDepth: number
42 - };
43 -
44 - function createContainerState(rootInstance) {
45 - return {
46 - rootInstance,
47 - contextFibers: null,
48 - contextValues: null,
49 - contextDepth: -1,
50 - };
51 - }
52 -
53 - // If we meet any portals, we'll go deeper.
54 - let containerStack : Array<ContainerState> = [];
55 - let containerDepth : number = -1;
37 + // Context stack is reused across the subtrees.
38 + // We use a null sentinel on the fiber stack to separate them.
39 + let contextFibers : Array<Fiber | null> | null = null;
40 + let contextValues : Array<CX | null> | null = null;
41 + let contextDepth : number = -1;
42 + // Current context for fast access.
43 + let currentContextValue : CX | null = null;
44 + // Current root instance for fast access.
45 + let rootInstance : C | null = null;
46 + // A stack of outer root instances if we're in a portal.
47 + let portalStack : Array<C | null> = [];
48 + let portalDepth : number = -1;
49
50 function getRootHostContainer() : C {
58 - if (containerDepth === -1) {
59 - throw new Error('Expected to find a root container.');
51 + if (rootInstance == null) {
52 + throw new Error('Expected root container to exist.');
53 }
61 - const containerState = containerStack[containerDepth];
62 - const {rootInstance} = containerState;
54 return rootInstance;
55 }
56
66 - function pushHostContainer(portalHostContainer) {
67 - containerDepth++;
68 - if (containerDepth === containerStack.length) {
69 - containerStack[containerDepth] = createContainerState(portalHostContainer);
57 + function pushHostContainer(nextRootInstance : C) {
58 + if (rootInstance == null) {
59 + // We're entering a root.
60 + rootInstance = nextRootInstance;
61 } else {
71 - const containerState = containerStack[containerDepth];
72 - containerState.rootInstance = portalHostContainer;
73 - containerState.contextFibers = null;
74 - containerState.contextValues = null;
75 - containerState.contextDepth = -1;
62 + // We're entering a portal.
63 + // Save the current root to the portal stack.
64 + portalDepth++;
65 + portalStack[portalDepth] = rootInstance;
66 + rootInstance = nextRootInstance;
67 + // Delimit subtree context with a sentinel so we know where to pop later.
68 + if (contextFibers != null && contextValues != null) {
69 + contextDepth++;
70 + contextFibers[contextDepth] = null;
71 + contextValues[contextDepth] = null;
72 + currentContextValue = null;
73 + }
74 }
75 }
76
77 function popHostContainer() {
80 - if (containerDepth === -1) {
81 - throw new Error('Already reached the root.');
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 && contextValues != null) {
93 + currentContextValue = contextValues[contextDepth];
94 + } else {
95 + currentContextValue = null;
96 + }
97 + }
98 }
83 - containerDepth--;
99 }
100
101 function getHostContext() : CX | null {
87 - if (containerDepth == null) {
88 - throw new Error('Expected to find a root container.');
89 - }
90 - const containerState = containerStack[containerDepth];
91 - const {contextDepth, contextValues} = containerState;
92 - if (contextDepth === -1) {
93 - return null;
94 - }
95 - if (contextValues == null) {
96 - throw new Error('Expected context values to exist.');
97 - }
98 - return contextValues[contextDepth];
102 + return currentContextValue;
103 }
104
105 function pushHostContext(fiber : Fiber) : void {
102 - const parentHostContext = getHostContext();
103 - const currentHostContext = getChildHostContext(parentHostContext, fiber.type);
104 - if (parentHostContext === currentHostContext) {
106 + const nextContextValue = getChildHostContext(currentContextValue, fiber.type);
107 + if (currentContextValue === nextContextValue) {
108 return;
109 }
107 - const containerState = containerStack[containerDepth];
108 - let {contextDepth, contextFibers, contextValues} = containerState;
110 if (contextFibers == null) {
111 contextFibers = [];
111 - containerState.contextFibers = contextFibers;
112 }
113 if (contextValues == null) {
114 contextValues = [];
115 - containerState.contextValues = contextValues;
115 }
116 contextDepth++;
118 - containerState.contextDepth = contextDepth;
117 contextFibers[contextDepth] = fiber;
120 - contextValues[contextDepth] = currentHostContext;
118 + contextValues[contextDepth] = nextContextValue;
119 + currentContextValue = nextContextValue;
120 }
121
122 function popHostContext(fiber : Fiber) : void {
124 - const containerState = containerStack[containerDepth];
125 - let {contextDepth} = containerState;
123 if (contextDepth === -1) {
124 return;
125 }
129 - const {contextFibers, contextValues} = containerState;
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]) {
130 return;
131 }
132 +
133 contextFibers[contextDepth] = null;
134 contextValues[contextDepth] = null;
135 contextDepth--;
139 - containerState.contextDepth = contextDepth;
136 + if (contextDepth > -1) {
137 + currentContextValue = contextValues[contextDepth];
138 + } else {
139 + currentContextValue = null;
140 + }
141 }
142
143 function resetHostContainer() {
143 - containerDepth = -1;
144 + // Reset portal stack pointer because we're starting from the very top.
145 + portalDepth = -1;
146 + // Reset current container state.
147 + // Don't reset arrays because we reuse them.
148 + rootInstance = null;
149 + contextDepth = -1;
150 + currentContextValue = null;
151 }
152
153 return {