@samitouri / QOS-React / commits / 3b5e3b5a23

Refactor Debug Frames to Enable Renderers to Provide Custom Logic (#10105)

* Extract the top element frame from ReactDebugCurrentFrame This is part of a larger refactor to decouple stack addendums. All renderers have their own way of getting the stack of the currently executing components. There is one special case in Element Validator that adds an additional line for the element being validated. This commit moves that special case in into the validator. There is another case where it looked like this was used in shallow renderer but this is actually something different. It is part of the component stack. It just happens to be that shallow renderer has a simpler implementation of the component stack that just happens to be a single element. This will let us decouple the implementation to get a stack from ReactDebugCurrentFrame and put that in each renderer. * Stop using ReactComponentTreeHook for Fiber Currently we fall back to ReactCurrentOwner in ReactComponentTreeHook for stack addendums. We shouldn't need to because we should use ReactDebugCurrrentFiber. Ensure we always set both ReactDebugCurrentFiber and ReactDebugCurrentFrame so that we can rely on these for all stacks. * Make ReactDebugCurrentFrame implementation independent Introduced ReactDebugCurrentStack for the Stack renderer which does the same thing as ReactDebugCurrentFiber. ReactDebugCurrentFrame no longer keeps track of the current fiber/debug id. That's handled by the individual renderers. Instead, it is now used to keep track of the current *implementation* of the current stack frame. That way it is decoupled from the specifics of the renderers. There can be multiple renderers in a context. What matters is which one is currently executing a debuggable context (such as a render function). * Add debug frames to ReactPartialRenderer (ssr) Basic functionality. * Add shared modules to shallow renderer This is now needed because we share describeComponentFrame.

Sebastian Markbåge committed Jul 14, 2017 at 15:36 UTC 3b5e3b5a23069dd03feaa6979bccb64de4d76d09
23 files changed +320 -169
scripts/fiber/tests-passing-except-dev.txt
-3
@@ -1,5 +1,2 @@
1 src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
2 -* gives source code refs for unknown prop warning (ssr)
3 -* gives source code refs for unknown prop warning for exact elements (ssr)
2 * gives source code refs for unknown prop warning for exact elements in composition (ssr)
5 -* should suggest property name if available (ssr)
scripts/fiber/tests-passing.txt
+3
@@ -853,10 +853,13 @@ src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
853 * should warn about props that are no longer supported
854 * should warn about props that are no longer supported (ssr)
855 * gives source code refs for unknown prop warning
856 +* gives source code refs for unknown prop warning (ssr)
857 * gives source code refs for unknown prop warning for update render
858 * gives source code refs for unknown prop warning for exact elements
859 +* gives source code refs for unknown prop warning for exact elements (ssr)
860 * gives source code refs for unknown prop warning for exact elements in composition
861 * should suggest property name if available
862 +* should suggest property name if available (ssr)
863 * renders innerHTML and preserves whitespace
864 * render and then updates innerHTML and preserves whitespace
865
scripts/rollup/bundles.js
+5 -1
@@ -381,7 +381,11 @@ const bundles = [
381 label: 'shallow-renderer',
382 manglePropertiesOnProd: false,
383 name: 'react-test-renderer/shallow',
384 - paths: ['src/renderers/shared/**/*.js', 'src/renderers/testing/**/*.js'],
384 + paths: [
385 + 'src/renderers/shared/**/*.js',
386 + 'src/renderers/testing/**/*.js',
387 + 'src/shared/**/*.js',
388 + ],
389 },
390
391 /******* React Noop Renderer (used only for fixtures/fiber-debugger) *******/
src/isomorphic/children/traverseAllChildren.js
+3 -3
@@ -24,7 +24,7 @@ var REACT_ELEMENT_TYPE =
24 0xeac7;
25
26 if (__DEV__) {
27 - var {getCurrentStackAddendum} = require('ReactComponentTreeHook');
27 + var {getStackAddendum} = require('ReactDebugCurrentFrame');
28 }
29
30 var SEPARATOR = '.';
@@ -171,7 +171,7 @@ function traverseAllChildrenImpl(
171 'Using Maps as children is unsupported and will likely yield ' +
172 'unexpected results. Convert it to a sequence/iterable of keyed ' +
173 'ReactElements instead.%s',
174 - getCurrentStackAddendum(),
174 + getStackAddendum(),
175 );
176 didWarnAboutMaps = true;
177 }
@@ -196,7 +196,7 @@ function traverseAllChildrenImpl(
196 addendum =
197 ' If you meant to render a collection of children, use an array ' +
198 'instead.' +
199 - getCurrentStackAddendum();
199 + getStackAddendum();
200 }
201 var childrenString = '' + children;
202 invariant(
src/isomorphic/classic/element/ReactDebugCurrentFrame.js
+5 -33
@@ -12,46 +12,18 @@
12
13 'use strict';
14
15 -import type {Fiber} from 'ReactFiber';
16 -import type {DebugID} from 'ReactInstanceType';
17 -
15 const ReactDebugCurrentFrame = {};
16
17 if (__DEV__) {
21 - var {
22 - getStackAddendumByID,
23 - getCurrentStackAddendum,
24 - } = require('ReactComponentTreeHook');
25 - var {
26 - getStackAddendumByWorkInProgressFiber,
27 - } = require('ReactFiberComponentTreeHook');
28 -
18 // Component that is being worked on
30 - ReactDebugCurrentFrame.current = (null: Fiber | DebugID | null);
31 -
32 - // Element that is being cloned or created
33 - ReactDebugCurrentFrame.element = (null: *);
19 + ReactDebugCurrentFrame.getCurrentStack = (null: null | (() => string | null));
20
21 ReactDebugCurrentFrame.getStackAddendum = function(): string | null {
36 - let stack = null;
37 - const current = ReactDebugCurrentFrame.current;
38 - const element = ReactDebugCurrentFrame.element;
39 - if (current !== null) {
40 - if (typeof current === 'number') {
41 - // DebugID from Stack.
42 - const debugID = current;
43 - stack = getStackAddendumByID(debugID);
44 - } else if (typeof current.tag === 'number') {
45 - // This is a Fiber.
46 - // The stack will only be correct if this is a work in progress
47 - // version and we're calling it during reconciliation.
48 - const workInProgress = current;
49 - stack = getStackAddendumByWorkInProgressFiber(workInProgress);
50 - }
51 - } else if (element !== null) {
52 - stack = getCurrentStackAddendum(element);
22 + const impl = ReactDebugCurrentFrame.getCurrentStack;
23 + if (impl) {
24 + return impl();
25 }
54 - return stack;
26 + return null;
27 };
28 }
29
src/isomorphic/classic/element/ReactElementValidator.js
+38 -26
@@ -21,14 +21,42 @@
21 var ReactCurrentOwner = require('ReactCurrentOwner');
22 var ReactElement = require('ReactElement');
23
24 -var getComponentName = require('getComponentName');
25 -
24 if (__DEV__) {
25 var checkPropTypes = require('prop-types/checkPropTypes');
26 var lowPriorityWarning = require('lowPriorityWarning');
27 var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
28 var warning = require('fbjs/lib/warning');
31 - var {getCurrentStackAddendum} = require('ReactComponentTreeHook');
29 + var describeComponentFrame = require('describeComponentFrame');
30 + var getComponentName = require('getComponentName');
31 +
32 + var currentlyValidatingElement = null;
33 +
34 + var getDisplayName = function(element: ?ReactElement): string {
35 + if (element == null) {
36 + return '#empty';
37 + } else if (typeof element === 'string' || typeof element === 'number') {
38 + return '#text';
39 + } else if (typeof element.type === 'string') {
40 + return element.type;
41 + } else {
42 + return element.type.displayName || element.type.name || 'Unknown';
43 + }
44 + };
45 +
46 + var getStackAddendum = function(): string {
47 + var stack = '';
48 + if (currentlyValidatingElement) {
49 + var name = getDisplayName(currentlyValidatingElement);
50 + var owner = currentlyValidatingElement._owner;
51 + stack += describeComponentFrame(
52 + name,
53 + currentlyValidatingElement._source,
54 + owner && getComponentName(owner),
55 + );
56 + }
57 + stack += ReactDebugCurrentFrame.getStackAddendum() || '';
58 + return stack;
59 + };
60 }
61
62 var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
@@ -115,14 +143,16 @@ function validateExplicitKey(element, parentType) {
143 childOwner = ` It was passed a child from ${getComponentName(element._owner)}.`;
144 }
145
146 + currentlyValidatingElement = element;
147 warning(
148 false,
149 'Each child in an array or iterator should have a unique "key" prop.' +
150 '%s%s See https://fb.me/react-warning-keys for more information.%s',
151 currentComponentErrorInfo,
152 childOwner,
124 - getCurrentStackAddendum(element),
153 + getStackAddendum(),
154 );
155 + currentlyValidatingElement = null;
156 }
157
158 /**
@@ -193,13 +223,9 @@ function validatePropTypes(element) {
223 : componentClass.propTypes;
224
225 if (propTypes) {
196 - checkPropTypes(
197 - propTypes,
198 - element.props,
199 - 'prop',
200 - name,
201 - ReactDebugCurrentFrame.getStackAddendum,
202 - );
226 + currentlyValidatingElement = element;
227 + checkPropTypes(propTypes, element.props, 'prop', name, getStackAddendum);
228 + currentlyValidatingElement = null;
229 }
230 if (typeof componentClass.getDefaultProps === 'function') {
231 warning(
@@ -235,7 +261,7 @@ var ReactElementValidator = {
261 info += getDeclarationErrorAddendum();
262 }
263
238 - info += getCurrentStackAddendum();
264 + info += ReactDebugCurrentFrame.getStackAddendum() || '';
265
266 warning(
267 false,
@@ -255,10 +281,6 @@ var ReactElementValidator = {
281 return element;
282 }
283
258 - if (__DEV__) {
259 - ReactDebugCurrentFrame.element = element;
260 - }
261 -
284 // Skip key warning if the type isn't valid since our key validation logic
285 // doesn't expect a non-string/function type and can throw confusing errors.
286 // We don't want exception behavior to differ between dev and prod.
@@ -272,10 +294,6 @@ var ReactElementValidator = {
294
295 validatePropTypes(element);
296
275 - if (__DEV__) {
276 - ReactDebugCurrentFrame.element = null;
277 - }
278 -
297 return element;
298 },
299
@@ -306,16 +324,10 @@ var ReactElementValidator = {
324
325 cloneElement: function(element, props, children) {
326 var newElement = ReactElement.cloneElement.apply(this, arguments);
309 - if (__DEV__) {
310 - ReactDebugCurrentFrame.element = newElement;
311 - }
327 for (var i = 2; i < arguments.length; i++) {
328 validateChildKeys(arguments[i], newElement.type);
329 }
330 validatePropTypes(newElement);
316 - if (__DEV__) {
317 - ReactDebugCurrentFrame.element = null;
318 - }
331 return newElement;
332 },
333 };
src/isomorphic/hooks/ReactComponentTreeHook.js
+7 -23
@@ -13,17 +13,12 @@
13 'use strict';
14
15 var ReactCurrentOwner = require('ReactCurrentOwner');
16 -var {
17 - getStackAddendumByWorkInProgressFiber,
18 - describeComponentFrame,
19 -} = require('ReactFiberComponentTreeHook');
16 var invariant = require('fbjs/lib/invariant');
17 var warning = require('fbjs/lib/warning');
22 -var getComponentName = require('getComponentName');
18 +var describeComponentFrame = require('describeComponentFrame');
19
20 import type {ReactElement, Source} from 'ReactElementType';
21 import type {DebugID} from 'ReactInstanceType';
26 -import type {Fiber} from 'ReactFiber';
22
23 function isNative(fn) {
24 // Based on isNative() from Lodash
@@ -313,26 +308,15 @@ var ReactComponentTreeHook = {
308 return item ? item.isMounted : false;
309 },
310
316 - getCurrentStackAddendum(topElement: ?ReactElement): string {
311 + getCurrentStackAddendum(): string {
312 var info = '';
318 - if (topElement) {
319 - var name = getDisplayName(topElement);
320 - var owner = topElement._owner;
321 - info += describeComponentFrame(
322 - name,
323 - topElement._source,
324 - owner && getComponentName(owner),
325 - );
326 - }
327 -
313 var currentOwner = ReactCurrentOwner.current;
314 if (currentOwner) {
330 - if (typeof currentOwner.tag === 'number') {
331 - const workInProgress = ((currentOwner: any): Fiber);
332 - // Safe because if current owner exists, we are reconciling,
333 - // and it is guaranteed to be the work-in-progress version.
334 - info += getStackAddendumByWorkInProgressFiber(workInProgress);
335 - } else if (typeof currentOwner._debugID === 'number') {
315 + invariant(
316 + typeof currentOwner.tag !== 'number',
317 + 'Fiber owners should not show up in Stack stack traces.',
318 + );
319 + if (typeof currentOwner._debugID === 'number') {
320 info += ReactComponentTreeHook.getStackAddendumByID(
321 currentOwner._debugID,
322 );
src/renderers/__tests__/ReactComponentTreeHook-test.js
+21 -17
@@ -20,6 +20,7 @@ describe('ReactComponentTreeHook', () => {
20 var ReactDOMServer;
21 var ReactInstanceMap;
22 var ReactComponentTreeHook;
23 + var ReactDebugCurrentFiber;
24 var ReactComponentTreeTestUtils;
25
26 beforeEach(() => {
@@ -29,6 +30,7 @@ describe('ReactComponentTreeHook', () => {
30 ReactDOM = require('react-dom');
31 ReactDOMServer = require('react-dom/server');
32 ReactInstanceMap = require('ReactInstanceMap');
33 + ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
34 ReactComponentTreeHook = require('ReactComponentTreeHook');
35 ReactComponentTreeTestUtils = require('ReactComponentTreeTestUtils');
36 });
@@ -37,7 +39,9 @@ describe('ReactComponentTreeHook', () => {
39 describe('stack addenda', () => {
40 it('gets created', () => {
41 function getAddendum(element) {
40 - var addendum = ReactComponentTreeHook.getCurrentStackAddendum(element);
42 + var addendum = ReactDOMFeatureFlags.useFiber
43 + ? ReactDebugCurrentFiber.getCurrentFiberStackAddendum() || ''
44 + : ReactComponentTreeHook.getCurrentStackAddendum();
45 return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
46 }
47
@@ -47,23 +51,23 @@ describe('ReactComponentTreeHook', () => {
51 Object.defineProperty(Anon, 'name', {
52 value: null,
53 });
50 - function Orange() {
51 - return null;
52 - }
54 + // function Orange() {
55 + // return null;
56 + // }
57
58 expectDev(getAddendum()).toBe('');
55 - expectDev(getAddendum(<div />)).toBe('\n in div (at **)');
56 - expectDev(getAddendum(<Anon />)).toBe('\n in Unknown (at **)');
57 - expectDev(getAddendum(<Orange />)).toBe('\n in Orange (at **)');
58 - expectDev(getAddendum(React.createElement(Orange))).toBe(
59 - '\n in Orange',
60 - );
59 + // expectDev(getAddendum(<div />)).toBe('\n in div (at **)');
60 + // expectDev(getAddendum(<Anon />)).toBe('\n in Unknown (at **)');
61 + // expectDev(getAddendum(<Orange />)).toBe('\n in Orange (at **)');
62 + // expectDev(getAddendum(React.createElement(Orange))).toBe(
63 + // '\n in Orange',
64 + // );
65
66 var renders = 0;
63 - var rOwnedByQ;
67 + //var rOwnedByQ;
68
69 function Q() {
66 - return (rOwnedByQ = React.createElement(R));
70 + return /*rOwnedByQ =*/ React.createElement(R);
71 }
72 function R() {
73 return <div><S /></div>;
@@ -82,15 +86,15 @@ describe('ReactComponentTreeHook', () => {
86 '\n in Q (at **)',
87 );
88 expectDev(getAddendum(<span />)).toBe(
85 - '\n in span (at **)' +
86 - '\n in S (at **)' +
89 + // '\n in span (at **)' +
90 + '\n in S (at **)' +
91 '\n in div (at **)' +
92 '\n in R (created by Q)' +
93 '\n in Q (at **)',
94 );
95 expectDev(getAddendum(React.createElement('span'))).toBe(
92 - '\n in span (created by S)' +
93 - '\n in S (at **)' +
96 + // '\n in span (created by S)' +
97 + '\n in S (at **)' +
98 '\n in div (at **)' +
99 '\n in R (created by Q)' +
100 '\n in Q (at **)',
@@ -103,7 +107,7 @@ describe('ReactComponentTreeHook', () => {
107 expectDev(renders).toBe(2);
108
109 // Make sure owner is fetched for the top element too.
106 - expectDev(getAddendum(rOwnedByQ)).toBe('\n in R (created by Q)');
110 + // expectDev(getAddendum(rOwnedByQ)).toBe('\n in R (created by Q)');
111 });
112
113 // These are features and regression tests that only affect
src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js
+7 -4
@@ -12,7 +12,6 @@
12 'use strict';
13
14 var DOMProperty = require('DOMProperty');
15 -var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
15
16 var warning = require('fbjs/lib/warning');
17
@@ -20,7 +19,10 @@ var warnedProperties = {};
19 var rARIA = new RegExp('^(aria)-[' + DOMProperty.ATTRIBUTE_NAME_CHAR + ']*$');
20
21 if (__DEV__) {
23 - var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
22 + var {
23 + ReactComponentTreeHook,
24 + ReactDebugCurrentFrame,
25 + } = require('ReactGlobalSharedState');
26 var {getStackAddendumByID} = ReactComponentTreeHook;
27 }
28
@@ -29,8 +31,9 @@ function getStackAddendum(debugID) {
31 // This can only happen on Stack
32 return getStackAddendumByID(debugID);
33 } else {
32 - // This can only happen on Fiber
33 - return ReactDebugCurrentFiber.getCurrentFiberStackAddendum();
34 + // This can only happen on Fiber / Server
35 + var stack = ReactDebugCurrentFrame.getStackAddendum();
36 + return stack != null ? stack : '';
37 }
38 }
39
src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js
+7 -4
@@ -11,11 +11,13 @@
11
12 'use strict';
13
14 -var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
14 var warning = require('fbjs/lib/warning');
15
16 if (__DEV__) {
18 - var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
17 + var {
18 + ReactComponentTreeHook,
19 + ReactDebugCurrentFrame,
20 + } = require('ReactGlobalSharedState');
21 var {getStackAddendumByID} = ReactComponentTreeHook;
22 }
23
@@ -26,8 +28,9 @@ function getStackAddendum(debugID) {
28 // This can only happen on Stack
29 return getStackAddendumByID(debugID);
30 } else {
29 - // This can only happen on Fiber
30 - return ReactDebugCurrentFiber.getCurrentFiberStackAddendum();
31 + // This can only happen on Fiber / Server
32 + var stack = ReactDebugCurrentFrame.getStackAddendum();
33 + return stack != null ? stack : '';
34 }
35 }
36
src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js
+10 -4
@@ -13,18 +13,24 @@
13
14 var DOMProperty = require('DOMProperty');
15 var EventPluginRegistry = require('EventPluginRegistry');
16 -var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
17 -var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
16
17 var warning = require('fbjs/lib/warning');
18
19 +if (__DEV__) {
20 + var {
21 + ReactComponentTreeHook,
22 + ReactDebugCurrentFrame,
23 + } = require('ReactGlobalSharedState');
24 + var {getStackAddendumByID} = ReactComponentTreeHook;
25 +}
26 +
27 function getStackAddendum(debugID) {
28 if (debugID != null) {
29 // This can only happen on Stack
24 - return ReactComponentTreeHook.getStackAddendumByID(debugID);
30 + return getStackAddendumByID(debugID);
31 } else {
32 // This can only happen on Fiber / Server
27 - var stack = ReactDebugCurrentFiber.getCurrentFiberStackAddendum();
33 + var stack = ReactDebugCurrentFrame.getStackAddendum();
34 return stack != null ? stack : '';
35 }
36 }
src/renderers/shared/fiber/ReactDebugCurrentFiber.js
+16 -1
@@ -16,6 +16,8 @@ import type {Fiber} from 'ReactFiber';
16
17 type LifeCyclePhase = 'render' | 'getChildContext';
18
19 +var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
20 +
21 if (__DEV__) {
22 var getComponentName = require('getComponentName');
23 var {
@@ -49,10 +51,23 @@ function getCurrentFiberStackAddendum(): string | null {
51 return null;
52 }
53
54 +function resetCurrentFiber() {
55 + ReactDebugCurrentFrame.getCurrentStack = null;
56 + ReactDebugCurrentFiber.current = null;
57 + ReactDebugCurrentFiber.phase = null;
58 +}
59 +
60 +function setCurrentFiber(fiber: Fiber | null, phase: LifeCyclePhase | null) {
61 + ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum;
62 + ReactDebugCurrentFiber.current = fiber;
63 + ReactDebugCurrentFiber.phase = phase;
64 +}
65 +
66 var ReactDebugCurrentFiber = {
67 current: (null: Fiber | null),
68 phase: (null: LifeCyclePhase | null),
55 -
69 + resetCurrentFiber,
70 + setCurrentFiber,
71 getCurrentFiberOwnerName,
72 getCurrentFiberStackAddendum,
73 };
src/renderers/shared/fiber/ReactFiberBeginWork.js
+5 -5
@@ -217,9 +217,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
217
218 if (__DEV__) {
219 ReactCurrentOwner.current = workInProgress;
220 - ReactDebugCurrentFiber.phase = 'render';
220 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, 'render');
221 nextChildren = fn(nextProps, context);
222 - ReactDebugCurrentFiber.phase = null;
222 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, null);
223 } else {
224 nextChildren = fn(nextProps, context);
225 }
@@ -286,9 +286,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
286 ReactCurrentOwner.current = workInProgress;
287 let nextChildren;
288 if (__DEV__) {
289 - ReactDebugCurrentFiber.phase = 'render';
289 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, 'render');
290 nextChildren = instance.render();
291 - ReactDebugCurrentFiber.phase = null;
291 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, null);
292 } else {
293 nextChildren = instance.render();
294 }
@@ -725,7 +725,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
725 }
726
727 if (__DEV__) {
728 - ReactDebugCurrentFiber.current = workInProgress;
728 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, null);
729 }
730
731 switch (workInProgress.tag) {
src/renderers/shared/fiber/ReactFiberCompleteWork.js
+1 -1
@@ -187,7 +187,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
187 renderPriority: PriorityLevel,
188 ): Fiber | null {
189 if (__DEV__) {
190 - ReactDebugCurrentFiber.current = workInProgress;
190 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, null);
191 }
192
193 // Get the latest props.
src/renderers/shared/fiber/ReactFiberContext.js
+8 -9
@@ -26,7 +26,6 @@ const {createCursor, pop, push} = require('ReactFiberStack');
26
27 if (__DEV__) {
28 var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
29 - var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
29 var {startPhaseTimer, stopPhaseTimer} = require('ReactDebugFiberPerf');
30 var warnedAboutMissingGetChildContext = {};
31 }
@@ -92,15 +91,15 @@ exports.getMaskedContext = function(
91
92 if (__DEV__) {
93 const name = getComponentName(workInProgress) || 'Unknown';
95 - ReactDebugCurrentFrame.current = workInProgress;
94 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, null);
95 checkPropTypes(
96 contextTypes,
97 context,
98 'context',
99 name,
101 - ReactDebugCurrentFrame.getStackAddendum,
100 + ReactDebugCurrentFiber.getCurrentFiberStackAddendum,
101 );
103 - ReactDebugCurrentFrame.current = null;
102 + ReactDebugCurrentFiber.resetCurrentFiber();
103 }
104
105 // Cache unmasked context so we can avoid recreating masked context unless necessary.
@@ -181,11 +180,11 @@ function processChildContext(
180
181 let childContext;
182 if (__DEV__) {
184 - ReactDebugCurrentFiber.phase = 'getChildContext';
183 + ReactDebugCurrentFiber.setCurrentFiber(fiber, 'getChildContext');
184 startPhaseTimer(fiber, 'getChildContext');
185 childContext = instance.getChildContext();
186 stopPhaseTimer();
188 - ReactDebugCurrentFiber.phase = null;
187 + ReactDebugCurrentFiber.resetCurrentFiber();
188 } else {
189 childContext = instance.getChildContext();
190 }
@@ -205,15 +204,15 @@ function processChildContext(
204 // assume anything about the given fiber. We won't pass it down if we aren't sure.
205 // TODO: remove this hack when we delete unstable_renderSubtree in Fiber.
206 const workInProgress = isReconciling ? fiber : null;
208 - ReactDebugCurrentFrame.current = workInProgress;
207 + ReactDebugCurrentFiber.setCurrentFiber(workInProgress, null);
208 checkPropTypes(
209 childContextTypes,
210 childContext,
211 'child context',
212 name,
214 - ReactDebugCurrentFrame.getStackAddendum,
213 + ReactDebugCurrentFiber.getCurrentFiberStackAddendum,
214 );
216 - ReactDebugCurrentFrame.current = null;
215 + ReactDebugCurrentFiber.resetCurrentFiber();
216 }
217
218 return {...parentContext, ...childContext};
src/renderers/shared/fiber/ReactFiberScheduler.js
+5 -6
@@ -322,7 +322,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
322 function commitAllHostEffects() {
323 while (nextEffect !== null) {
324 if (__DEV__) {
325 - ReactDebugCurrentFiber.current = nextEffect;
325 + ReactDebugCurrentFiber.setCurrentFiber(nextEffect, null);
326 recordEffect();
327 }
328
@@ -383,7 +383,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
383 }
384
385 if (__DEV__) {
386 - ReactDebugCurrentFiber.current = null;
386 + ReactDebugCurrentFiber.resetCurrentFiber();
387 }
388 }
389
@@ -711,7 +711,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
711
712 ReactCurrentOwner.current = null;
713 if (__DEV__) {
714 - ReactDebugCurrentFiber.current = null;
714 + ReactDebugCurrentFiber.resetCurrentFiber();
715 }
716
717 return next;
@@ -740,7 +740,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
740
741 ReactCurrentOwner.current = null;
742 if (__DEV__) {
743 - ReactDebugCurrentFiber.current = null;
743 + ReactDebugCurrentFiber.resetCurrentFiber();
744 }
745
746 return next;
@@ -1024,8 +1024,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1024 // It is no longer valid because we exited the user code.
1025 ReactCurrentOwner.current = null;
1026 if (__DEV__) {
1027 - ReactDebugCurrentFiber.current = null;
1028 - ReactDebugCurrentFiber.phase = null;
1027 + ReactDebugCurrentFiber.resetCurrentFiber();
1028 }
1029 // It is no longer valid because this unit of work failed.
1030 nextUnitOfWork = null;
src/renderers/shared/server/ReactPartialRenderer.js
+50 -7
@@ -42,6 +42,49 @@ if (__DEV__) {
42 validateInputPropertes(type, props);
43 validateUnknownPropertes(type, props);
44 };
45 +
46 + var describeComponentFrame = require('describeComponentFrame');
47 + var describeStackFrame = function(
48 + frame: {
49 + tag?: string,
50 + children: Array<*>,
51 + childIndex: number,
52 + },
53 + ): string {
54 + var element = frame.children[frame.childIndex - 1];
55 + if (!element) {
56 + return '';
57 + }
58 + var source = element._source;
59 + var type = element.type;
60 + var name = typeof type === 'string'
61 + ? type
62 + : typeof type === 'function' ? type.displayName || type.name : null;
63 + var ownerName = null;
64 + return describeComponentFrame(name, source, ownerName);
65 + };
66 +
67 + var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
68 + var currentDebugStack = null;
69 + var setCurrentDebugStack = function(stack) {
70 + currentDebugStack = stack;
71 + ReactDebugCurrentFrame.getCurrentStack = getStackAddendum;
72 + };
73 + var resetCurrentDebugStack = function() {
74 + currentDebugStack = null;
75 + ReactDebugCurrentFrame.getCurrentStack = null;
76 + };
77 + var getStackAddendum = function(): null | string {
78 + if (currentDebugStack === null) {
79 + return null;
80 + }
81 + let stack = '';
82 + let debugStack = currentDebugStack;
83 + for (let i = debugStack.length - 1; i >= 0; i--) {
84 + stack += describeStackFrame(debugStack[i]);
85 + }
86 + return stack;
87 + };
88 }
89
90 var didWarnDefaultInputValue = false;
@@ -145,13 +188,7 @@ function maskContext(type, context) {
188
189 function checkContextTypes(typeSpecs, values, location: string) {
190 if (__DEV__) {
148 - checkPropTypes(
149 - typeSpecs,
150 - values,
151 - location,
152 - 'Component',
153 - () => '', // ReactDebugCurrentFrame.getStackAddendum,
154 - );
191 + checkPropTypes(typeSpecs, values, location, 'Component', getStackAddendum);
192 }
193 }
194
@@ -374,7 +411,13 @@ class ReactDOMServerRenderer {
411 continue;
412 }
413 var child = frame.children[frame.childIndex++];
414 + if (__DEV__) {
415 + setCurrentDebugStack(this.stack);
416 + }
417 out += this.render(child, frame.context);
418 + if (__DEV__) {
419 + resetCurrentDebugStack();
420 + }
421 }
422 return out;
423 }
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+14 -3
@@ -23,6 +23,7 @@ var {ReactCurrentOwner} = require('ReactGlobalSharedState');
23
24 if (__DEV__) {
25 var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
26 + var ReactDebugCurrentStack = require('ReactDebugCurrentStack');
27 var warningAboutMissingGetChildContext = {};
28 }
29
@@ -403,6 +404,8 @@ var ReactCompositeComponent = {
404 ) {
405 if (__DEV__) {
406 ReactCurrentOwner.current = this;
407 + ReactDebugCurrentFrame.getCurrentStack =
408 + ReactDebugCurrentStack.getStackAddendum;
409 try {
410 return this._constructComponentWithoutOwner(
411 doConstruct,
@@ -412,6 +415,7 @@ var ReactCompositeComponent = {
415 );
416 } finally {
417 ReactCurrentOwner.current = null;
418 + ReactDebugCurrentFrame.getCurrentStack = null;
419 }
420 } else {
421 return this._constructComponentWithoutOwner(
@@ -746,15 +750,15 @@ var ReactCompositeComponent = {
750 */
751 _checkContextTypes: function(typeSpecs, values, location: string) {
752 if (__DEV__) {
749 - ReactDebugCurrentFrame.current = this._debugID;
753 + ReactDebugCurrentStack.current = this._debugID;
754 checkPropTypes(
755 typeSpecs,
756 values,
757 location,
758 this.getName(),
755 - ReactDebugCurrentFrame.getStackAddendum,
759 + ReactDebugCurrentStack.getStackAddendum,
760 );
757 - ReactDebugCurrentFrame.current = null;
761 + ReactDebugCurrentStack.current = null;
762 }
763 },
764
@@ -1246,10 +1250,17 @@ var ReactCompositeComponent = {
1250 this._compositeType !== ReactCompositeComponentTypes.StatelessFunctional
1251 ) {
1252 ReactCurrentOwner.current = this;
1253 + if (__DEV__) {
1254 + ReactDebugCurrentFrame.getCurrentStack =
1255 + ReactDebugCurrentStack.getStackAddendum;
1256 + }
1257 try {
1258 renderedElement = this._renderValidatedComponentWithoutOwnerOrContext();
1259 } finally {
1260 ReactCurrentOwner.current = null;
1261 + if (__DEV__) {
1262 + ReactDebugCurrentFrame.getCurrentStack = null;
1263 + }
1264 }
1265 } else {
1266 renderedElement = this._renderValidatedComponentWithoutOwnerOrContext();
src/renderers/shared/stack/reconciler/ReactDebugCurrentStack.js new
+38
@@ -0,0 +1,38 @@
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 ReactDebugCurrentStack
10 + * @flow
11 + */
12 +
13 +'use strict';
14 +
15 +import type {DebugID} from 'ReactInstanceType';
16 +
17 +const ReactDebugCurrentStack = {};
18 +
19 +if (__DEV__) {
20 + var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
21 + var {getStackAddendumByID, getCurrentStackAddendum} = ReactComponentTreeHook;
22 +
23 + // Component that is being worked on
24 + ReactDebugCurrentStack.current = (null: DebugID | null);
25 +
26 + ReactDebugCurrentStack.getStackAddendum = function(): string | null {
27 + let stack = null;
28 + const current = ReactDebugCurrentStack.current;
29 + if (current !== null) {
30 + stack = getStackAddendumByID(current);
31 + } else {
32 + stack = getCurrentStackAddendum();
33 + }
34 + return stack;
35 + };
36 +}
37 +
38 +module.exports = ReactDebugCurrentStack;
src/renderers/shared/stack/reconciler/ReactMultiChild.js
+10
@@ -18,6 +18,10 @@ var ReactInstrumentation = require('ReactInstrumentation');
18 var ReactReconciler = require('ReactReconciler');
19 var ReactChildReconciler = require('ReactChildReconciler');
20 var {ReactCurrentOwner} = require('ReactGlobalSharedState');
21 +if (__DEV__) {
22 + var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
23 + var ReactDebugCurrentStack = require('ReactDebugCurrentStack');
24 +}
25
26 var emptyFunction = require('fbjs/lib/emptyFunction');
27 var flattenStackChildren = require('flattenStackChildren');
@@ -179,6 +183,8 @@ var ReactMultiChild = {
183 if (this._currentElement) {
184 try {
185 ReactCurrentOwner.current = this._currentElement._owner;
186 + ReactDebugCurrentFrame.getCurrentStack =
187 + ReactDebugCurrentStack.getStackAddendum;
188 return ReactChildReconciler.instantiateChildren(
189 nestedChildren,
190 transaction,
@@ -187,6 +193,7 @@ var ReactMultiChild = {
193 );
194 } finally {
195 ReactCurrentOwner.current = null;
196 + ReactDebugCurrentFrame.getCurrentStack = null;
197 }
198 }
199 }
@@ -212,12 +219,15 @@ var ReactMultiChild = {
219 if (this._currentElement) {
220 try {
221 ReactCurrentOwner.current = this._currentElement._owner;
222 + ReactDebugCurrentFrame.getCurrentStack =
223 + ReactDebugCurrentStack.getStackAddendum;
224 nextChildren = flattenStackChildren(
225 nextNestedChildrenElements,
226 selfDebugID,
227 );
228 } finally {
229 ReactCurrentOwner.current = null;
230 + ReactDebugCurrentFrame.getCurrentStack = null;
231 }
232 ReactChildReconciler.updateChildren(
233 prevChildren,
src/renderers/testing/ReactShallowRendererEntry.js
+35 -4
@@ -18,7 +18,38 @@ const React = require('react');
18 const emptyObject = require('fbjs/lib/emptyObject');
19 const invariant = require('fbjs/lib/invariant');
20
21 -const {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
21 +if (__DEV__) {
22 + var describeComponentFrame = require('describeComponentFrame');
23 + var getComponentName = require('getComponentName');
24 +
25 + var currentlyValidatingElement = null;
26 +
27 + var getDisplayName = function(element: ?ReactElement): string {
28 + if (element == null) {
29 + return '#empty';
30 + } else if (typeof element === 'string' || typeof element === 'number') {
31 + return '#text';
32 + } else if (typeof element.type === 'string') {
33 + return element.type;
34 + } else {
35 + return element.type.displayName || element.type.name || 'Unknown';
36 + }
37 + };
38 +
39 + var getStackAddendum = function(): string {
40 + var stack = '';
41 + if (currentlyValidatingElement) {
42 + var name = getDisplayName(currentlyValidatingElement);
43 + var owner = currentlyValidatingElement._owner;
44 + stack += describeComponentFrame(
45 + name,
46 + currentlyValidatingElement._source,
47 + owner && getComponentName(owner),
48 + );
49 + }
50 + return stack;
51 + };
52 +}
53
54 class ReactShallowRenderer {
55 static createRenderer = function() {
@@ -79,17 +110,17 @@ class ReactShallowRenderer {
110 );
111
112 if (element.type.hasOwnProperty('contextTypes')) {
82 - ReactDebugCurrentFrame.element = element;
113 + currentlyValidatingElement = element;
114
115 checkPropTypes(
116 element.type.contextTypes,
117 context,
118 'context',
119 getName(element.type, this._instance),
89 - ReactDebugCurrentFrame.getStackAddendum,
120 + getStackAddendum,
121 );
122
92 - ReactDebugCurrentFrame.element = null;
123 + currentlyValidatingElement = null;
124 }
125
126 this._mountClassComponent(element.props, context);
src/shared/ReactFiberComponentTreeHook.js
+1 -15
@@ -19,24 +19,11 @@ var {
19 ClassComponent,
20 HostComponent,
21 } = ReactTypeOfWork;
22 +var describeComponentFrame = require('describeComponentFrame');
23 var getComponentName = require('getComponentName');
24
25 import type {Fiber} from 'ReactFiber';
26
26 -function describeComponentFrame(name, source: any, ownerName) {
27 - return (
28 - '\n in ' +
29 - (name || 'Unknown') +
30 - (source
31 - ? ' (at ' +
32 - source.fileName.replace(/^.*[\\\/]/, '') +
33 - ':' +
34 - source.lineNumber +
35 - ')'
36 - : ownerName ? ' (created by ' + ownerName + ')' : '')
37 - );
38 -}
39 -
27 function describeFiber(fiber: Fiber): string {
28 switch (fiber.tag) {
29 case IndeterminateComponent:
@@ -72,5 +59,4 @@ function getStackAddendumByWorkInProgressFiber(workInProgress: Fiber): string {
59
60 module.exports = {
61 getStackAddendumByWorkInProgressFiber,
75 - describeComponentFrame,
62 };
src/shared/describeComponentFrame.js new
+31
@@ -0,0 +1,31 @@
1 +/**
2 + * Copyright 2016-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 + * @flow
10 + * @providesModule describeComponentFrame
11 + */
12 +
13 +'use strict';
14 +
15 +module.exports = function(
16 + name: null | string,
17 + source: any,
18 + ownerName: null | string,
19 +) {
20 + return (
21 + '\n in ' +
22 + (name || 'Unknown') +
23 + (source
24 + ? ' (at ' +
25 + source.fileName.replace(/^.*[\\\/]/, '') +
26 + ':' +
27 + source.lineNumber +
28 + ')'
29 + : ownerName ? ' (created by ' + ownerName + ')' : '')
30 + );
31 +};