@samitouri / QOS-React-2 / commits / b73dcdc04f

[Fizz] Refactor Component Stack Nodes (#30298)

Component stacks have a similar problem to the problem with keyPath where we had to move it down and set it late right before recursing. Currently we work around that by popping exactly one off when something suspends. That doesn't work with the new server stacks being added which are more than one. It also meant that we kept having add a single frame that could be popped when there shouldn't need to be one. Unlike keyPath component stacks has this weird property that once something throws we might need the stack that was attempted for errors or the previous stack if we're going to retry and just recreate it. I've tried a few different approaches and I didn't like either but this is the one that seems least problematic. I first split out renderNodeDestructive into a retryNode helper. During retries only retryNode is called. When we first discover a node, we pass through renderNodeDestructive. Instead of add a component stack frame deep inside renderNodeDestructive after we've already refined a node, we now add it before in renderNodeDestructive. That way it's only added once before being attempted. This is similar to how Fiber works where in ChildFiber we match the node once to create the instance and then later do we attempt to actually render it and it's only the second part that's ever retried. This unfortunately means that we now have to refine the node down to element/lazy/thenables twice. To avoid refining the type too I move that to be done lazily.

Sebastian Markbåge committed Jul 9, 2024 at 15:44 UTC b73dcdc04ffa2dd9f2197d796388657d64ad53be
9 files changed +346 -461
packages/react-devtools-shared/src/backend/DevToolsFiberComponentStack.js
+1
@@ -47,6 +47,7 @@ export function describeFiber(
47 case HostComponent:
48 return describeBuiltInComponentFrame(workInProgress.type);
49 case LazyComponent:
50 + // TODO: When we support Thenables as component types we should rename this.
51 return describeBuiltInComponentFrame('Lazy');
52 case SuspenseComponent:
53 return describeBuiltInComponentFrame('Suspense');
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+38 -19
@@ -700,17 +700,39 @@ describe('ReactDOMFizzServer', () => {
700
701 it('should client render a boundary if a lazy component rejects', async () => {
702 let rejectComponent;
703 + const promise = new Promise((resolve, reject) => {
704 + rejectComponent = reject;
705 + });
706 const LazyComponent = React.lazy(() => {
704 - return new Promise((resolve, reject) => {
705 - rejectComponent = reject;
706 - });
707 + return promise;
708 + });
709 +
710 + const LazyLazy = React.lazy(async () => {
711 + return {
712 + default: LazyComponent,
713 + };
714 + });
715 +
716 + function Wrapper({children}) {
717 + return children;
718 + }
719 + const LazyWrapper = React.lazy(() => {
720 + return {
721 + then(callback) {
722 + callback({
723 + default: Wrapper,
724 + });
725 + },
726 + };
727 });
728
729 function App({isClient}) {
730 return (
731 <div>
732 <Suspense fallback={<Text text="Loading..." />}>
713 - {isClient ? <Text text="Hello" /> : <LazyComponent text="Hello" />}
733 + <LazyWrapper>
734 + {isClient ? <Text text="Hello" /> : <LazyLazy text="Hello" />}
735 + </LazyWrapper>
736 </Suspense>
737 </div>
738 );
@@ -744,6 +766,7 @@ describe('ReactDOMFizzServer', () => {
766 });
767 pipe(writable);
768 });
769 +
770 expect(loggedErrors).toEqual([]);
771 expect(bootstrapped).toBe(true);
772
@@ -772,7 +795,7 @@ describe('ReactDOMFizzServer', () => {
795 'Switched to client rendering because the server rendering errored:\n\n' +
796 theError.message,
797 expectedDigest,
775 - componentStack(['Lazy', 'Suspense', 'div', 'App']),
798 + componentStack(['Lazy', 'Wrapper', 'Suspense', 'div', 'App']),
799 ],
800 ],
801 [
@@ -852,13 +875,9 @@ describe('ReactDOMFizzServer', () => {
875 }
876
877 await act(() => {
855 - const {pipe} = renderToPipeableStream(
856 - <App isClient={false} />,
857 -
858 - {
859 - onError,
860 - },
861 - );
878 + const {pipe} = renderToPipeableStream(<App isClient={false} />, {
879 + onError,
880 + });
881 pipe(writable);
882 });
883 expect(loggedErrors).toEqual([]);
@@ -896,7 +915,7 @@ describe('ReactDOMFizzServer', () => {
915 'Switched to client rendering because the server rendering errored:\n\n' +
916 theError.message,
917 expectedDigest,
899 - componentStack(['Lazy', 'Suspense', 'div', 'App']),
918 + componentStack(['Suspense', 'div', 'App']),
919 ],
920 ],
921 [
@@ -1395,13 +1414,13 @@ describe('ReactDOMFizzServer', () => {
1414 'The render was aborted by the server without a reason.',
1415 expectedDigest,
1416 // We get the stack of the task when it was aborted which is why we see `h1`
1398 - componentStack(['h1', 'Suspense', 'div', 'App']),
1417 + componentStack(['AsyncText', 'h1', 'Suspense', 'div', 'App']),
1418 ],
1419 [
1420 'Switched to client rendering because the server rendering aborted due to:\n\n' +
1421 'The render was aborted by the server without a reason.',
1422 expectedDigest,
1404 - componentStack(['Suspense', 'main', 'div', 'App']),
1423 + componentStack(['AsyncText', 'Suspense', 'main', 'div', 'App']),
1424 ],
1425 ],
1426 [
@@ -3523,13 +3542,13 @@ describe('ReactDOMFizzServer', () => {
3542 'Switched to client rendering because the server rendering aborted due to:\n\n' +
3543 'foobar',
3544 'a digest',
3526 - componentStack(['Suspense', 'p', 'div', 'App']),
3545 + componentStack(['AsyncText', 'Suspense', 'p', 'div', 'App']),
3546 ],
3547 [
3548 'Switched to client rendering because the server rendering aborted due to:\n\n' +
3549 'foobar',
3550 'a digest',
3532 - componentStack(['Suspense', 'span', 'div', 'App']),
3551 + componentStack(['AsyncText', 'Suspense', 'span', 'div', 'App']),
3552 ],
3553 ],
3554 [
@@ -3606,13 +3625,13 @@ describe('ReactDOMFizzServer', () => {
3625 'Switched to client rendering because the server rendering aborted due to:\n\n' +
3626 'uh oh',
3627 'a digest',
3609 - componentStack(['Suspense', 'p', 'div', 'App']),
3628 + componentStack(['AsyncText', 'Suspense', 'p', 'div', 'App']),
3629 ],
3630 [
3631 'Switched to client rendering because the server rendering aborted due to:\n\n' +
3632 'uh oh',
3633 'a digest',
3615 - componentStack(['Suspense', 'span', 'div', 'App']),
3634 + componentStack(['AsyncText', 'Suspense', 'span', 'div', 'App']),
3635 ],
3636 ],
3637 [
packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
+1 -1
@@ -585,7 +585,7 @@ describe('ReactDOMFizzServerNode', () => {
585 let isComplete = false;
586 let rendered = false;
587 const promise = new Promise(r => (resolve = r));
588 - function Wait() {
588 + function Wait({prop}) {
589 if (!hasLoaded) {
590 throw promise;
591 }
packages/react-html/src/__tests__/ReactHTMLServer-test.js
+1 -3
@@ -250,9 +250,7 @@ if (!__EXPERIMENTAL__) {
250 '\n in Bar (at **)' +
251 '\n in Foo (at **)' +
252 '\n in div (at **)'
253 - : '\n in Lazy (at **)' +
254 - '\n in div (at **)' +
255 - '\n in div (at **)',
253 + : '\n in div (at **)' + '\n in div (at **)',
254 );
255 expect(normalizeCodeLocInfo(caughtErrors[0].ownerStack)).toBe(
256 __DEV__ && gate(flags => flags.enableOwnerStacks)
packages/react-reconciler/src/ReactFiberComponentStack.js
+1
@@ -39,6 +39,7 @@ function describeFiber(fiber: Fiber): string {
39 case HostComponent:
40 return describeBuiltInComponentFrame(fiber.type);
41 case LazyComponent:
42 + // TODO: When we support Thenables as component types we should rename this.
43 return describeBuiltInComponentFrame('Lazy');
44 case SuspenseComponent:
45 return describeBuiltInComponentFrame('Suspense');
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js
+70
@@ -930,4 +930,74 @@ describe('ReactFlightDOMEdge', () => {
930 '\n in Bar (at **)' + '\n in Foo (at **)',
931 );
932 });
933 +
934 + it('supports server components in ssr component stacks', async () => {
935 + let reject;
936 + const promise = new Promise((_, r) => (reject = r));
937 + async function Erroring() {
938 + await promise;
939 + return 'should not render';
940 + }
941 +
942 + const model = {
943 + root: ReactServer.createElement(Erroring),
944 + };
945 +
946 + const stream = ReactServerDOMServer.renderToReadableStream(
947 + model,
948 + webpackMap,
949 + {
950 + onError() {},
951 + },
952 + );
953 +
954 + const rootModel = await ReactServerDOMClient.createFromReadableStream(
955 + stream,
956 + {
957 + ssrManifest: {
958 + moduleMap: null,
959 + moduleLoading: null,
960 + },
961 + },
962 + );
963 +
964 + const errors = [];
965 + const result = ReactDOMServer.renderToReadableStream(
966 + <div>{rootModel.root}</div>,
967 + {
968 + onError(error, {componentStack}) {
969 + errors.push({
970 + error,
971 + componentStack: normalizeCodeLocInfo(componentStack),
972 + });
973 + },
974 + },
975 + );
976 +
977 + const theError = new Error('my error');
978 + reject(theError);
979 +
980 + const expectedMessage = __DEV__
981 + ? 'my error'
982 + : 'An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.';
983 +
984 + try {
985 + await result;
986 + } catch (x) {
987 + expect(x).toEqual(
988 + expect.objectContaining({
989 + message: expectedMessage,
990 + }),
991 + );
992 + }
993 +
994 + expect(errors).toEqual([
995 + {
996 + error: expect.objectContaining({
997 + message: expectedMessage,
998 + }),
999 + componentStack: (__DEV__ ? '\n in Erroring' : '') + '\n in div',
1000 + },
1001 + ]);
1002 + });
1003 });
packages/react-server/src/ReactFizzComponentStack.js
+105 -94
@@ -8,52 +8,96 @@
8 */
9
10 import type {ReactComponentInfo} from 'shared/ReactTypes';
11 +import type {LazyComponent} from 'react/src/ReactLazy';
12
13 import {
14 describeBuiltInComponentFrame,
15 describeFunctionComponentFrame,
16 describeClassComponentFrame,
17 + describeDebugInfoFrame,
18 } from 'shared/ReactComponentStackFrame';
19
20 +import {
21 + REACT_FORWARD_REF_TYPE,
22 + REACT_MEMO_TYPE,
23 + REACT_LAZY_TYPE,
24 + REACT_SUSPENSE_LIST_TYPE,
25 + REACT_SUSPENSE_TYPE,
26 +} from 'shared/ReactSymbols';
27 +
28 import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
29
30 import {formatOwnerStack} from './ReactFizzOwnerStack';
31
22 -// DEV-only reverse linked list representing the current component stack
23 -type BuiltInComponentStackNode = {
24 - tag: 0,
25 - parent: null | ComponentStackNode,
26 - type: string,
27 - owner?: null | ReactComponentInfo | ComponentStackNode, // DEV only
28 - stack?: null | string | Error, // DEV only
29 -};
30 -type FunctionComponentStackNode = {
31 - tag: 1,
32 - parent: null | ComponentStackNode,
33 - type: Function,
34 - owner?: null | ReactComponentInfo | ComponentStackNode, // DEV only
35 - stack?: null | string | Error, // DEV only
36 -};
37 -type ClassComponentStackNode = {
38 - tag: 2,
32 +export type ComponentStackNode = {
33 parent: null | ComponentStackNode,
40 - type: Function,
34 + type:
35 + | symbol
36 + | string
37 + | Function
38 + | LazyComponent<any, any>
39 + | ReactComponentInfo,
40 owner?: null | ReactComponentInfo | ComponentStackNode, // DEV only
41 stack?: null | string | Error, // DEV only
42 };
44 -type ServerComponentStackNode = {
45 - // DEV only
46 - tag: 3,
47 - parent: null | ComponentStackNode,
48 - type: string, // name + env
49 - owner?: null | ReactComponentInfo | ComponentStackNode, // DEV only
50 - stack?: null | string | Error, // DEV only
51 -};
52 -export type ComponentStackNode =
53 - | BuiltInComponentStackNode
54 - | FunctionComponentStackNode
55 - | ClassComponentStackNode
56 - | ServerComponentStackNode;
43 +
44 +function shouldConstruct(Component: any) {
45 + return Component.prototype && Component.prototype.isReactComponent;
46 +}
47 +
48 +function describeComponentStackByType(
49 + type:
50 + | symbol
51 + | string
52 + | Function
53 + | LazyComponent<any, any>
54 + | ReactComponentInfo,
55 +): string {
56 + if (typeof type === 'string') {
57 + return describeBuiltInComponentFrame(type);
58 + }
59 + if (typeof type === 'function') {
60 + if (shouldConstruct(type)) {
61 + return describeClassComponentFrame(type);
62 + } else {
63 + return describeFunctionComponentFrame(type);
64 + }
65 + }
66 + if (typeof type === 'object' && type !== null) {
67 + switch (type.$$typeof) {
68 + case REACT_FORWARD_REF_TYPE: {
69 + return describeFunctionComponentFrame((type: any).render);
70 + }
71 + case REACT_MEMO_TYPE: {
72 + return describeFunctionComponentFrame((type: any).type);
73 + }
74 + case REACT_LAZY_TYPE: {
75 + const lazyComponent: LazyComponent<any, any> = (type: any);
76 + const payload = lazyComponent._payload;
77 + const init = lazyComponent._init;
78 + try {
79 + type = init(payload);
80 + } catch (x) {
81 + // TODO: When we support Thenables as component types we should rename this.
82 + return describeBuiltInComponentFrame('Lazy');
83 + }
84 + return describeComponentStackByType(type);
85 + }
86 + }
87 + if (typeof type.name === 'string') {
88 + return describeDebugInfoFrame(type.name, type.env);
89 + }
90 + }
91 + switch (type) {
92 + case REACT_SUSPENSE_LIST_TYPE: {
93 + return describeBuiltInComponentFrame('SuspenseList');
94 + }
95 + case REACT_SUSPENSE_TYPE: {
96 + return describeBuiltInComponentFrame('Suspense');
97 + }
98 + }
99 + return '';
100 +}
101
102 export function getStackByComponentStackNode(
103 componentStack: ComponentStackNode,
@@ -62,22 +106,7 @@ export function getStackByComponentStackNode(
106 let info = '';
107 let node: ComponentStackNode = componentStack;
108 do {
65 - switch (node.tag) {
66 - case 0:
67 - info += describeBuiltInComponentFrame(node.type);
68 - break;
69 - case 1:
70 - info += describeFunctionComponentFrame(node.type);
71 - break;
72 - case 2:
73 - info += describeClassComponentFrame(node.type);
74 - break;
75 - case 3:
76 - if (__DEV__) {
77 - info += describeBuiltInComponentFrame(node.type);
78 - break;
79 - }
80 - }
109 + info += describeComponentStackByType(node.type);
110 // $FlowFixMe[incompatible-type] we bail out when we get a null
111 node = node.parent;
112 } while (node);
@@ -110,59 +139,41 @@ export function getOwnerStackByComponentStackNodeInDev(
139 // add one extra frame just to describe the "current" built-in component by name.
140 // Similarly, if there is no owner at all, then there's no stack frame so we add the name
141 // of the root component to the stack to know which component is currently executing.
113 - switch (componentStack.tag) {
114 - case 0:
115 - info += describeBuiltInComponentFrame(componentStack.type);
116 - break;
117 - case 1:
118 - case 2:
119 - if (!componentStack.owner) {
120 - // Only if we have no other data about the callsite do we add
121 - // the component name as the single stack frame.
122 - info += describeFunctionComponentFrameWithoutLineNumber(
123 - componentStack.type,
124 - );
125 - }
126 - break;
127 - case 3:
128 - if (!componentStack.owner) {
129 - info += describeBuiltInComponentFrame(componentStack.type);
130 - }
131 - break;
142 + if (typeof componentStack.type === 'string') {
143 + info += describeBuiltInComponentFrame(componentStack.type);
144 + } else if (typeof componentStack.type === 'function') {
145 + if (!componentStack.owner) {
146 + // Only if we have no other data about the callsite do we add
147 + // the component name as the single stack frame.
148 + info += describeFunctionComponentFrameWithoutLineNumber(
149 + componentStack.type,
150 + );
151 + }
152 + } else {
153 + if (!componentStack.owner) {
154 + info += describeComponentStackByType(componentStack.type);
155 + }
156 }
157
158 let owner: void | null | ComponentStackNode | ReactComponentInfo =
159 componentStack;
160
161 while (owner) {
138 - if (typeof owner.tag === 'number') {
139 - const node: ComponentStackNode = (owner: any);
140 - owner = node.owner;
141 - let debugStack = node.stack;
142 - // If we don't actually print the stack if there is no owner of this JSX element.
143 - // In a real app it's typically not useful since the root app is always controlled
144 - // by the framework. These also tend to have noisy stacks because they're not rooted
145 - // in a React render but in some imperative bootstrapping code. It could be useful
146 - // if the element was created in module scope. E.g. hoisted. We could add a a single
147 - // stack frame for context for example but it doesn't say much if that's a wrapper.
148 - if (owner && debugStack) {
149 - if (typeof debugStack !== 'string') {
150 - // Stash the formatted stack so that we can avoid redoing the filtering.
151 - node.stack = debugStack = formatOwnerStack(debugStack);
152 - }
153 - if (debugStack !== '') {
154 - info += '\n' + debugStack;
155 - }
156 - }
157 - } else if (typeof owner.stack === 'string') {
158 - // Server Component
159 - const ownerStack: string = owner.stack;
160 - owner = owner.owner;
161 - if (owner && ownerStack !== '') {
162 - info += '\n' + ownerStack;
163 - }
164 - } else {
165 - break;
162 + let debugStack: void | null | string | Error = owner.stack;
163 + if (typeof debugStack !== 'string' && debugStack != null) {
164 + // Stash the formatted stack so that we can avoid redoing the filtering.
165 + // $FlowFixMe[cannot-write]: This has been refined to a ComponentStackNode.
166 + owner.stack = debugStack = formatOwnerStack(debugStack);
167 + }
168 + owner = owner.owner;
169 + // If we don't actually print the stack if there is no owner of this JSX element.
170 + // In a real app it's typically not useful since the root app is always controlled
171 + // by the framework. These also tend to have noisy stacks because they're not rooted
172 + // in a React render but in some imperative bootstrapping code. It could be useful
173 + // if the element was created in module scope. E.g. hoisted. We could add a a single
174 + // stack frame for context for example but it doesn't say much if that's a wrapper.
175 + if (owner && debugStack) {
176 + info += '\n' + debugStack;
177 }
178 }
179 return info;
packages/react-server/src/ReactFizzServer.js
+128 -343
@@ -472,6 +472,7 @@ function RequestInstance(
472 emptyContextObject,
473 null,
474 );
475 + pushComponentStack(rootTask);
476 pingedTasks.push(rootTask);
477 }
478
@@ -615,6 +616,7 @@ export function resumeRequest(
616 emptyContextObject,
617 null,
618 );
619 + pushComponentStack(rootTask);
620 pingedTasks.push(rootTask);
621 return request;
622 }
@@ -642,6 +644,7 @@ export function resumeRequest(
644 emptyContextObject,
645 null,
646 );
647 + pushComponentStack(rootTask);
648 pingedTasks.push(rootTask);
649 return request;
650 }
@@ -837,69 +840,6 @@ function getStackFromNode(stackNode: ComponentStackNode): string {
840 return getStackByComponentStackNode(stackNode);
841 }
842
840 -function createBuiltInComponentStack(
841 - task: Task,
842 - type: string,
843 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
844 - stack: null | Error, // DEV only
845 -): ComponentStackNode {
846 - if (__DEV__) {
847 - return {
848 - tag: 0,
849 - parent: task.componentStack,
850 - type,
851 - owner,
852 - stack,
853 - };
854 - }
855 - return {
856 - tag: 0,
857 - parent: task.componentStack,
858 - type,
859 - };
860 -}
861 -function createFunctionComponentStack(
862 - task: Task,
863 - type: Function,
864 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
865 - stack: null | Error, // DEV only
866 -): ComponentStackNode {
867 - if (__DEV__) {
868 - return {
869 - tag: 1,
870 - parent: task.componentStack,
871 - type,
872 - owner,
873 - stack,
874 - };
875 - }
876 - return {
877 - tag: 1,
878 - parent: task.componentStack,
879 - type,
880 - };
881 -}
882 -function createClassComponentStack(
883 - task: Task,
884 - type: Function,
885 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
886 - stack: null | Error, // DEV only
887 -): ComponentStackNode {
888 - if (__DEV__) {
889 - return {
890 - tag: 2,
891 - parent: task.componentStack,
892 - type,
893 - owner,
894 - stack,
895 - };
896 - }
897 - return {
898 - tag: 2,
899 - parent: task.componentStack,
900 - type,
901 - };
902 -}
843 function pushServerComponentStack(
844 task: Task,
845 debugInfo: void | null | ReactDebugInfo,
@@ -921,15 +861,9 @@ function pushServerComponentStack(
861 if (enableOwnerStacks && componentInfo.stack === undefined) {
862 continue;
863 }
924 - let name = componentInfo.name;
925 - const env = componentInfo.env;
926 - if (env) {
927 - name += ' [' + env + ']';
928 - }
864 task.componentStack = {
930 - tag: 3,
865 parent: task.componentStack,
932 - type: name,
866 + type: componentInfo,
867 owner: componentInfo.owner,
868 stack: componentInfo.stack,
869 };
@@ -940,19 +874,70 @@ function pushServerComponentStack(
874 }
875 }
876
877 +function pushComponentStack(task: Task): void {
878 + const node = task.node;
879 + // Create the Component Stack frame for the element we're about to try.
880 + // It's unfortunate that we need to do this refinement twice. Once for
881 + // the stack frame and then once again while actually
882 + if (typeof node === 'object' && node !== null) {
883 + switch ((node: any).$$typeof) {
884 + case REACT_ELEMENT_TYPE: {
885 + const element: any = node;
886 + const type = element.type;
887 + const owner = __DEV__ ? element._owner : null;
888 + const stack = __DEV__ && enableOwnerStacks ? element._debugStack : null;
889 + if (__DEV__) {
890 + pushServerComponentStack(task, element._debugInfo);
891 + if (enableOwnerStacks) {
892 + task.debugTask = element._debugTask;
893 + }
894 + }
895 + task.componentStack = createComponentStackFromType(
896 + task.componentStack,
897 + type,
898 + owner,
899 + stack,
900 + );
901 + break;
902 + }
903 + case REACT_LAZY_TYPE: {
904 + if (__DEV__) {
905 + const lazyNode: LazyComponentType<any, any> = (node: any);
906 + pushServerComponentStack(task, lazyNode._debugInfo);
907 + }
908 + break;
909 + }
910 + default: {
911 + if (__DEV__) {
912 + const maybeUsable: Object = node;
913 + if (typeof maybeUsable.then === 'function') {
914 + const thenable: Thenable<ReactNodeList> = (maybeUsable: any);
915 + pushServerComponentStack(task, thenable._debugInfo);
916 + }
917 + }
918 + }
919 + }
920 + }
921 +}
922 +
923 function createComponentStackFromType(
944 - task: Task,
945 - type: Function | string,
924 + parent: null | ComponentStackNode,
925 + type: Function | string | symbol,
926 owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
927 stack: null | Error, // DEV only
928 ): ComponentStackNode {
949 - if (typeof type === 'string') {
950 - return createBuiltInComponentStack(task, type, owner, stack);
951 - }
952 - if (shouldConstruct(type)) {
953 - return createClassComponentStack(task, type, owner, stack);
929 + if (__DEV__) {
930 + return {
931 + parent,
932 + type,
933 + owner,
934 + stack,
935 + };
936 }
955 - return createFunctionComponentStack(task, type, owner, stack);
937 + return {
938 + parent,
939 + type,
940 + };
941 }
942
943 type ThrownInfo = {
@@ -1088,8 +1073,6 @@ function renderSuspenseBoundary(
1073 someTask: Task,
1074 keyPath: KeyNode,
1075 props: Object,
1091 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1092 - stack: null | Error, // DEV only
1076 ): void {
1077 if (someTask.replay !== null) {
1078 // If we're replaying through this pass, it means we're replaying through
@@ -1108,12 +1091,6 @@ function renderSuspenseBoundary(
1091 // $FlowFixMe: Refined.
1092 const task: RenderTask = someTask;
1093
1111 - const previousComponentStack = task.componentStack;
1112 - // If we end up creating the fallback task we need it to have the correct stack which is
1113 - // the stack for the boundary itself. We stash it here so we can use it if needed later
1114 - const suspenseComponentStack = (task.componentStack =
1115 - createBuiltInComponentStack(task, 'Suspense', owner, stack));
1116 -
1094 const prevKeyPath = task.keyPath;
1095 const parentBoundary = task.blockedBoundary;
1096 const parentHoistableState = task.hoistableState;
@@ -1189,9 +1166,6 @@ function renderSuspenseBoundary(
1166 // Therefore we won't need the fallback. We early return so that we don't have to create
1167 // the fallback.
1168 newBoundary.status = COMPLETED;
1192 -
1193 - // We are returning early so we need to restore the
1194 - task.componentStack = previousComponentStack;
1169 return;
1170 }
1171 } catch (error: mixed) {
@@ -1234,7 +1208,6 @@ function renderSuspenseBoundary(
1208 task.hoistableState = parentHoistableState;
1209 task.blockedSegment = parentSegment;
1210 task.keyPath = prevKeyPath;
1237 - task.componentStack = previousComponentStack;
1211 }
1212
1213 const fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]];
@@ -1274,13 +1247,12 @@ function renderSuspenseBoundary(
1247 task.formatContext,
1248 task.context,
1249 task.treeContext,
1277 - // This stack should be the Suspense boundary stack because while the fallback is actually a child segment
1278 - // of the parent boundary from a component standpoint the fallback is a child of the Suspense boundary itself
1279 - suspenseComponentStack,
1250 + task.componentStack,
1251 true,
1252 !disableLegacyContext ? task.legacyContext : emptyContextObject,
1253 __DEV__ && enableOwnerStacks ? task.debugTask : null,
1254 );
1255 + pushComponentStack(suspendedFallbackTask);
1256 // TODO: This should be queued at a separate lower priority queue so that we only work
1257 // on preparing fallbacks if we don't have any more main content to task on.
1258 request.pingedTasks.push(suspendedFallbackTask);
@@ -1296,15 +1268,7 @@ function replaySuspenseBoundary(
1268 childSlots: ResumeSlots,
1269 fallbackNodes: Array<ReplayNode>,
1270 fallbackSlots: ResumeSlots,
1299 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1300 - stack: null | Error, // DEV only
1271 ): void {
1302 - const previousComponentStack = task.componentStack;
1303 - // If we end up creating the fallback task we need it to have the correct stack which is
1304 - // the stack for the boundary itself. We stash it here so we can use it if needed later
1305 - const suspenseComponentStack = (task.componentStack =
1306 - createBuiltInComponentStack(task, 'Suspense', owner, stack));
1307 -
1272 const prevKeyPath = task.keyPath;
1273 const previousReplaySet: ReplaySet = task.replay;
1274
@@ -1400,7 +1364,6 @@ function replaySuspenseBoundary(
1364 task.hoistableState = parentHoistableState;
1365 task.replay = previousReplaySet;
1366 task.keyPath = prevKeyPath;
1403 - task.componentStack = previousComponentStack;
1367 }
1368
1369 const fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]];
@@ -1425,13 +1388,12 @@ function replaySuspenseBoundary(
1388 task.formatContext,
1389 task.context,
1390 task.treeContext,
1428 - // This stack should be the Suspense boundary stack because while the fallback is actually a child segment
1429 - // of the parent boundary from a component standpoint the fallback is a child of the Suspense boundary itself
1430 - suspenseComponentStack,
1391 + task.componentStack,
1392 true,
1393 !disableLegacyContext ? task.legacyContext : emptyContextObject,
1394 __DEV__ && enableOwnerStacks ? task.debugTask : null,
1395 );
1396 + pushComponentStack(suspendedFallbackTask);
1397 // TODO: This should be queued at a separate lower priority queue so that we only work
1398 // on preparing fallbacks if we don't have any more main content to task on.
1399 request.pingedTasks.push(suspendedFallbackTask);
@@ -1442,17 +1404,7 @@ function renderBackupSuspenseBoundary(
1404 task: Task,
1405 keyPath: KeyNode,
1406 props: Object,
1445 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1446 - stack: null | Error, // DEV only
1407 ) {
1448 - const previousComponentStack = task.componentStack;
1449 - task.componentStack = createBuiltInComponentStack(
1450 - task,
1451 - 'Suspense',
1452 - owner,
1453 - stack,
1454 - );
1455 -
1408 const content = props.children;
1409 const segment = task.blockedSegment;
1410 const prevKeyPath = task.keyPath;
@@ -1467,7 +1419,6 @@ function renderBackupSuspenseBoundary(
1419 pushEndCompletedSuspenseBoundary(segment.chunks);
1420 }
1421 task.keyPath = prevKeyPath;
1470 - task.componentStack = previousComponentStack;
1422 }
1423
1424 function renderHostElement(
@@ -1476,11 +1427,7 @@ function renderHostElement(
1427 keyPath: KeyNode,
1428 type: string,
1429 props: Object,
1479 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1480 - stack: null | Error, // DEV only
1430 ): void {
1482 - const previousComponentStack = task.componentStack;
1483 - task.componentStack = createBuiltInComponentStack(task, type, owner, stack);
1431 const segment = task.blockedSegment;
1432 if (segment === null) {
1433 // Replay
@@ -1534,7 +1481,6 @@ function renderHostElement(
1481 );
1482 segment.lastPushedText = false;
1483 }
1537 - task.componentStack = previousComponentStack;
1484 }
1485
1486 function shouldConstruct(Component: any) {
@@ -1670,17 +1616,8 @@ function renderClassComponent(
1616 keyPath: KeyNode,
1617 Component: any,
1618 props: any,
1673 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1674 - stack: null | Error, // DEV only
1619 ): void {
1620 const resolvedProps = resolveClassComponentProps(Component, props);
1677 - const previousComponentStack = task.componentStack;
1678 - task.componentStack = createClassComponentStack(
1679 - task,
1680 - Component,
1681 - owner,
1682 - stack,
1683 - );
1621 const maskedContext = !disableLegacyContext
1622 ? getMaskedContext(Component, task.legacyContext)
1623 : undefined;
@@ -1698,7 +1635,6 @@ function renderClassComponent(
1635 Component,
1636 resolvedProps,
1637 );
1701 - task.componentStack = previousComponentStack;
1638 }
1639
1640 const didWarnAboutBadClass: {[string]: boolean} = {};
@@ -1715,21 +1651,11 @@ function renderFunctionComponent(
1651 keyPath: KeyNode,
1652 Component: any,
1653 props: any,
1718 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1719 - stack: null | Error, // DEV only
1654 ): void {
1655 let legacyContext;
1656 if (!disableLegacyContext) {
1657 legacyContext = getMaskedContext(Component, task.legacyContext);
1658 }
1725 - const previousComponentStack = task.componentStack;
1726 - task.componentStack = createFunctionComponentStack(
1727 - task,
1728 - Component,
1729 - owner,
1730 - stack,
1731 - );
1732 -
1659 if (__DEV__) {
1660 if (
1661 Component.prototype &&
@@ -1782,7 +1708,6 @@ function renderFunctionComponent(
1708 actionStateCount,
1709 actionStateMatchingIndex,
1710 );
1785 - task.componentStack = previousComponentStack;
1711 }
1712
1713 function finishFunctionComponent(
@@ -1931,17 +1856,7 @@ function renderForwardRef(
1856 type: any,
1857 props: Object,
1858 ref: any,
1934 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1935 - stack: null | Error, // DEV only
1859 ): void {
1937 - const previousComponentStack = task.componentStack;
1938 - task.componentStack = createFunctionComponentStack(
1939 - task,
1940 - type.render,
1941 - owner,
1942 - stack,
1943 - );
1944 -
1860 let propsWithoutRef;
1861 if (enableRefAsProp && 'ref' in props) {
1862 // `ref` is just a prop now, but `forwardRef` expects it to not appear in
@@ -1980,7 +1895,6 @@ function renderForwardRef(
1895 actionStateCount,
1896 actionStateMatchingIndex,
1897 );
1983 - task.componentStack = previousComponentStack;
1898 }
1899
1900 function renderMemo(
@@ -1990,24 +1904,13 @@ function renderMemo(
1904 type: any,
1905 props: Object,
1906 ref: any,
1993 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
1994 - stack: null | Error, // DEV only
1907 ): void {
1908 const innerType = type.type;
1909 const resolvedProps = resolveDefaultPropsOnNonClassComponent(
1910 innerType,
1911 props,
1912 );
2001 - renderElement(
2002 - request,
2003 - task,
2004 - keyPath,
2005 - innerType,
2006 - resolvedProps,
2007 - ref,
2008 - owner,
2009 - stack,
2010 - );
1913 + renderElement(request, task, keyPath, innerType, resolvedProps, ref);
1914 }
1915
1916 function renderContextConsumer(
@@ -2074,12 +1977,7 @@ function renderLazyComponent(
1977 lazyComponent: LazyComponentType<any, any>,
1978 props: Object,
1979 ref: any,
2077 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
2078 - stack: null | Error, // DEV only
1980 ): void {
2080 - const previousComponentStack = task.componentStack;
2081 - // TODO: Do we really need this stack frame? We don't on the client.
2082 - task.componentStack = createBuiltInComponentStack(task, 'Lazy', owner, stack);
1981 let Component;
1982 if (__DEV__) {
1983 Component = callLazyInitInDEV(lazyComponent);
@@ -2092,17 +1990,7 @@ function renderLazyComponent(
1990 Component,
1991 props,
1992 );
2095 - renderElement(
2096 - request,
2097 - task,
2098 - keyPath,
2099 - Component,
2100 - resolvedProps,
2101 - ref,
2102 - owner,
2103 - stack,
2104 - );
2105 - task.componentStack = previousComponentStack;
1993 + renderElement(request, task, keyPath, Component, resolvedProps, ref);
1994 }
1995
1996 function renderOffscreen(
@@ -2132,28 +2020,18 @@ function renderElement(
2020 type: any,
2021 props: Object,
2022 ref: any,
2135 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
2136 - stack: null | Error, // DEV only
2023 ): void {
2024 if (typeof type === 'function') {
2025 if (shouldConstruct(type)) {
2140 - renderClassComponent(request, task, keyPath, type, props, owner, stack);
2026 + renderClassComponent(request, task, keyPath, type, props);
2027 return;
2028 } else {
2143 - renderFunctionComponent(
2144 - request,
2145 - task,
2146 - keyPath,
2147 - type,
2148 - props,
2149 - owner,
2150 - stack,
2151 - );
2029 + renderFunctionComponent(request, task, keyPath, type, props);
2030 return;
2031 }
2032 }
2033 if (typeof type === 'string') {
2156 - renderHostElement(request, task, keyPath, type, props, owner, stack);
2034 + renderHostElement(request, task, keyPath, type, props);
2035 return;
2036 }
2037
@@ -2183,19 +2061,11 @@ function renderElement(
2061 return;
2062 }
2063 case REACT_SUSPENSE_LIST_TYPE: {
2186 - const preiousComponentStack = task.componentStack;
2187 - task.componentStack = createBuiltInComponentStack(
2188 - task,
2189 - 'SuspenseList',
2190 - owner,
2191 - stack,
2192 - );
2064 // TODO: SuspenseList should control the boundaries.
2065 const prevKeyPath = task.keyPath;
2066 task.keyPath = keyPath;
2067 renderNodeDestructive(request, task, props.children, -1);
2068 task.keyPath = prevKeyPath;
2198 - task.componentStack = preiousComponentStack;
2069 return;
2070 }
2071 case REACT_SCOPE_TYPE: {
@@ -2213,16 +2083,9 @@ function renderElement(
2083 enableSuspenseAvoidThisFallbackFizz &&
2084 props.unstable_avoidThisFallback === true
2085 ) {
2216 - renderBackupSuspenseBoundary(
2217 - request,
2218 - task,
2219 - keyPath,
2220 - props,
2221 - owner,
2222 - stack,
2223 - );
2086 + renderBackupSuspenseBoundary(request, task, keyPath, props);
2087 } else {
2225 - renderSuspenseBoundary(request, task, keyPath, props, owner, stack);
2088 + renderSuspenseBoundary(request, task, keyPath, props);
2089 }
2090 return;
2091 }
@@ -2231,20 +2094,11 @@ function renderElement(
2094 if (typeof type === 'object' && type !== null) {
2095 switch (type.$$typeof) {
2096 case REACT_FORWARD_REF_TYPE: {
2234 - renderForwardRef(
2235 - request,
2236 - task,
2237 - keyPath,
2238 - type,
2239 - props,
2240 - ref,
2241 - owner,
2242 - stack,
2243 - );
2097 + renderForwardRef(request, task, keyPath, type, props, ref);
2098 return;
2099 }
2100 case REACT_MEMO_TYPE: {
2247 - renderMemo(request, task, keyPath, type, props, ref, owner, stack);
2101 + renderMemo(request, task, keyPath, type, props, ref);
2102 return;
2103 }
2104 case REACT_PROVIDER_TYPE: {
@@ -2281,16 +2135,7 @@ function renderElement(
2135 // Fall through
2136 }
2137 case REACT_LAZY_TYPE: {
2284 - renderLazyComponent(
2285 - request,
2286 - task,
2287 - keyPath,
2288 - type,
2289 - props,
2290 - ref,
2291 - owner,
2292 - stack,
2293 - );
2138 + renderLazyComponent(request, task, keyPath, type, props, ref);
2139 return;
2140 }
2141 }
@@ -2370,8 +2215,6 @@ function replayElement(
2215 props: Object,
2216 ref: any,
2217 replay: ReplaySet,
2373 - owner: null | ReactComponentInfo | ComponentStackNode, // DEV only
2374 - stack: null | Error, // DEV only
2218 ): void {
2219 // We're replaying. Find the path to follow.
2220 const replayNodes = replay.nodes;
@@ -2399,7 +2242,7 @@ function replayElement(
2242 const currentNode = task.node;
2243 task.replay = {nodes: childNodes, slots: childSlots, pendingTasks: 1};
2244 try {
2402 - renderElement(request, task, keyPath, type, props, ref, owner, stack);
2245 + renderElement(request, task, keyPath, type, props, ref);
2246 if (
2247 task.replay.pendingTasks === 1 &&
2248 task.replay.nodes.length > 0
@@ -2466,8 +2309,6 @@ function replayElement(
2309 node[3],
2310 node[4] === null ? [] : node[4][2],
2311 node[4] === null ? null : node[4][3],
2469 - owner,
2470 - stack,
2312 );
2313 }
2314 // We finished rendering this node, so now we can consume this
@@ -2496,7 +2337,7 @@ function validateIterable(
2337 const isGeneratorComponent =
2338 childIndex === -1 && // Only the root child is valid
2339 task.componentStack !== null &&
2499 - task.componentStack.tag === 1 && // FunctionComponent
2340 + typeof task.componentStack.type === 'function' && // FunctionComponent
2341 // $FlowFixMe[method-unbinding]
2342 Object.prototype.toString.call(task.componentStack.type) ===
2343 '[object GeneratorFunction]' &&
@@ -2543,7 +2384,7 @@ function validateAsyncIterable(
2384 const isGeneratorComponent =
2385 childIndex === -1 && // Only the root child is valid
2386 task.componentStack !== null &&
2546 - task.componentStack.tag === 1 && // FunctionComponent
2387 + typeof task.componentStack.type === 'function' && // FunctionComponent
2388 // $FlowFixMe[method-unbinding]
2389 Object.prototype.toString.call(task.componentStack.type) ===
2390 '[object AsyncGeneratorFunction]' &&
@@ -2604,6 +2445,24 @@ function renderNodeDestructive(
2445 task.node = node;
2446 task.childIndex = childIndex;
2447
2448 + const previousComponentStack = task.componentStack;
2449 + const previousDebugTask =
2450 + __DEV__ && enableOwnerStacks ? task.debugTask : null;
2451 +
2452 + pushComponentStack(task);
2453 +
2454 + retryNode(request, task);
2455 +
2456 + task.componentStack = previousComponentStack;
2457 + if (__DEV__ && enableOwnerStacks) {
2458 + task.debugTask = previousDebugTask;
2459 + }
2460 +}
2461 +
2462 +function retryNode(request: Request, task: Task): void {
2463 + const node = task.node;
2464 + const childIndex = task.childIndex;
2465 +
2466 if (node === null) {
2467 return;
2468 }
@@ -2628,21 +2487,8 @@ function renderNodeDestructive(
2487 ref = element.ref;
2488 }
2489
2631 - const owner = __DEV__ ? element._owner : null;
2632 - const stack = __DEV__ && enableOwnerStacks ? element._debugStack : null;
2633 -
2634 - let previousDebugTask: null | ConsoleTask = null;
2635 - const previousComponentStack = task.componentStack;
2636 - let debugTask: null | ConsoleTask;
2637 - if (__DEV__) {
2638 - if (enableOwnerStacks) {
2639 - previousDebugTask = task.debugTask;
2640 - }
2641 - pushServerComponentStack(task, element._debugInfo);
2642 - if (enableOwnerStacks) {
2643 - task.debugTask = debugTask = element._debugTask;
2644 - }
2645 - }
2490 + const debugTask: null | ConsoleTask =
2491 + __DEV__ && enableOwnerStacks ? task.debugTask : null;
2492
2493 const name = getComponentNameFromType(type);
2494 const keyOrIndex =
@@ -2663,8 +2509,6 @@ function renderNodeDestructive(
2509 props,
2510 ref,
2511 task.replay,
2666 - owner,
2667 - stack,
2512 ),
2513 );
2514 } else {
@@ -2679,8 +2523,6 @@ function renderNodeDestructive(
2523 props,
2524 ref,
2525 task.replay,
2682 - owner,
2683 - stack,
2526 );
2527 }
2528 // No matches found for this node. We assume it's already emitted in the
@@ -2697,27 +2539,10 @@ function renderNodeDestructive(
2539 type,
2540 props,
2541 ref,
2700 - owner,
2701 - stack,
2542 ),
2543 );
2544 } else {
2705 - renderElement(
2706 - request,
2707 - task,
2708 - keyPath,
2709 - type,
2710 - props,
2711 - ref,
2712 - owner,
2713 - stack,
2714 - );
2715 - }
2716 - }
2717 - if (__DEV__) {
2718 - task.componentStack = previousComponentStack;
2719 - if (enableOwnerStacks) {
2720 - task.debugTask = previousDebugTask;
2545 + renderElement(request, task, keyPath, type, props, ref);
2546 }
2547 }
2548 return;
@@ -2729,23 +2554,6 @@ function renderNodeDestructive(
2554 );
2555 case REACT_LAZY_TYPE: {
2556 const lazyNode: LazyComponentType<any, any> = (node: any);
2732 - const previousComponentStack = task.componentStack;
2733 - let previousDebugTask = null;
2734 - if (__DEV__) {
2735 - if (enableOwnerStacks) {
2736 - previousDebugTask = task.debugTask;
2737 - }
2738 - pushServerComponentStack(task, lazyNode._debugInfo);
2739 - }
2740 - if (!__DEV__ || task.componentStack === previousComponentStack) {
2741 - // TODO: Do we really need this stack frame? We don't on the client.
2742 - task.componentStack = createBuiltInComponentStack(
2743 - task,
2744 - 'Lazy',
2745 - null,
2746 - null,
2747 - );
2748 - }
2557 let resolvedNode;
2558 if (__DEV__) {
2559 resolvedNode = callLazyInitInDEV(lazyNode);
@@ -2754,14 +2562,6 @@ function renderNodeDestructive(
2562 const init = lazyNode._init;
2563 resolvedNode = init(payload);
2564 }
2757 -
2758 - // We restore the stack before rendering the resolved node because once the Lazy
2759 - // has resolved any future errors
2760 - task.componentStack = previousComponentStack;
2761 - if (__DEV__ && enableOwnerStacks) {
2762 - task.debugTask = previousDebugTask;
2763 - }
2764 -
2565 // Now we render the resolved node
2566 renderNodeDestructive(request, task, resolvedNode, childIndex);
2567 return;
@@ -2813,15 +2613,6 @@ function renderNodeDestructive(
2613 // for new iterators, but we currently warn for rendering these
2614 // so needs some refactoring to deal with the warning.
2615
2816 - // We need to push a component stack because if this suspends, we'll pop a stack.
2817 - const previousComponentStack = task.componentStack;
2818 - task.componentStack = createBuiltInComponentStack(
2819 - task,
2820 - 'AsyncIterable',
2821 - null,
2822 - null,
2823 - );
2824 -
2616 // Restore the thenable state before resuming.
2617 const prevThenableState = task.thenableState;
2618 task.thenableState = null;
@@ -2859,7 +2650,6 @@ function renderNodeDestructive(
2650 step = unwrapThenable(iterator.next());
2651 }
2652 }
2862 - task.componentStack = previousComponentStack;
2653 renderChildrenArray(request, task, children, childIndex);
2654 return;
2655 }
@@ -2879,19 +2669,12 @@ function renderNodeDestructive(
2669 // Clear any previous thenable state that was created by the unwrapping.
2670 task.thenableState = null;
2671 const thenable: Thenable<ReactNodeList> = (maybeUsable: any);
2882 - const previousComponentStack = task.componentStack;
2883 - if (__DEV__) {
2884 - pushServerComponentStack(task, thenable._debugInfo);
2885 - }
2672 const result = renderNodeDestructive(
2673 request,
2674 task,
2675 unwrapThenable(thenable),
2676 childIndex,
2677 );
2892 - if (__DEV__) {
2893 - task.componentStack = previousComponentStack;
2894 - }
2678 return result;
2679 }
2680
@@ -3069,8 +2852,8 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void {
2852 const parentOwner = parentStackFrame.owner;
2853
2854 let currentComponentErrorInfo = '';
3072 - if (parentOwner && typeof parentOwner.tag === 'number') {
3073 - const name = getComponentNameFromType((parentOwner: any).type);
2855 + if (parentOwner && typeof parentOwner.type !== 'undefined') {
2856 + const name = getComponentNameFromType(parentOwner.type);
2857 if (name) {
2858 currentComponentErrorInfo =
2859 '\n\nCheck the render method of `' + name + '`.';
@@ -3088,8 +2871,8 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void {
2871 let childOwnerAppendix = '';
2872 if (childOwner != null && parentOwner !== childOwner) {
2873 let ownerName = null;
3091 - if (typeof childOwner.tag === 'number') {
3092 - ownerName = getComponentNameFromType((childOwner: any).type);
2874 + if (typeof childOwner.type !== 'undefined') {
2875 + ownerName = getComponentNameFromType(childOwner.type);
2876 } else if (typeof childOwner.name === 'string') {
2877 ownerName = childOwner.name;
2878 }
@@ -3100,8 +2883,9 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void {
2883 }
2884
2885 // We create a fake component stack for the child to log the stack trace from.
2886 + const previousComponentStack = task.componentStack;
2887 const stackFrame = createComponentStackFromType(
3104 - task,
2888 + task.componentStack,
2889 (child: any).type,
2890 (child: any)._owner,
2891 enableOwnerStacks ? (child: any)._debugStack : null,
@@ -3113,7 +2897,7 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void {
2897 currentComponentErrorInfo,
2898 childOwnerAppendix,
2899 );
3116 - task.componentStack = stackFrame.parent;
2900 + task.componentStack = previousComponentStack;
2901 }
2902 }
2903
@@ -3448,9 +3232,7 @@ function spawnNewSuspendedReplayTask(
3232 task.formatContext,
3233 task.context,
3234 task.treeContext,
3451 - // We pop one task off the stack because the node that suspended will be tried again,
3452 - // which will add it back onto the stack.
3453 - task.componentStack !== null ? task.componentStack.parent : null,
3235 + task.componentStack,
3236 task.isFallback,
3237 !disableLegacyContext ? task.legacyContext : emptyContextObject,
3238 __DEV__ && enableOwnerStacks ? task.debugTask : null,
@@ -3495,9 +3277,7 @@ function spawnNewSuspendedRenderTask(
3277 task.formatContext,
3278 task.context,
3279 task.treeContext,
3498 - // We pop one task off the stack because the node that suspended will be tried again,
3499 - // which will add it back onto the stack.
3500 - task.componentStack !== null ? task.componentStack.parent : null,
3280 + task.componentStack,
3281 task.isFallback,
3282 !disableLegacyContext ? task.legacyContext : emptyContextObject,
3283 __DEV__ && enableOwnerStacks ? task.debugTask : null,
@@ -3525,6 +3305,8 @@ function renderNode(
3305 const previousKeyPath = task.keyPath;
3306 const previousTreeContext = task.treeContext;
3307 const previousComponentStack = task.componentStack;
3308 + const previousDebugTask =
3309 + __DEV__ && enableOwnerStacks ? task.debugTask : null;
3310 let x;
3311 // Store how much we've pushed at this point so we can reset it in case something
3312 // suspended partially through writing something.
@@ -3569,6 +3351,9 @@ function renderNode(
3351 task.keyPath = previousKeyPath;
3352 task.treeContext = previousTreeContext;
3353 task.componentStack = previousComponentStack;
3354 + if (__DEV__ && enableOwnerStacks) {
3355 + task.debugTask = previousDebugTask;
3356 + }
3357 // Restore all active ReactContexts to what they were before.
3358 switchContext(previousContext);
3359 return;
@@ -3623,6 +3408,9 @@ function renderNode(
3408 task.keyPath = previousKeyPath;
3409 task.treeContext = previousTreeContext;
3410 task.componentStack = previousComponentStack;
3411 + if (__DEV__ && enableOwnerStacks) {
3412 + task.debugTask = previousDebugTask;
3413 + }
3414 // Restore all active ReactContexts to what they were before.
3415 switchContext(previousContext);
3416 return;
@@ -3659,6 +3447,9 @@ function renderNode(
3447 task.keyPath = previousKeyPath;
3448 task.treeContext = previousTreeContext;
3449 task.componentStack = previousComponentStack;
3450 + if (__DEV__ && enableOwnerStacks) {
3451 + task.debugTask = previousDebugTask;
3452 + }
3453 // Restore all active ReactContexts to what they were before.
3454 switchContext(previousContext);
3455 return;
@@ -4196,7 +3987,7 @@ function retryRenderTask(
3987 // We call the destructive form that mutates this task. That way if something
3988 // suspends again, we can reuse the same task instead of spawning a new one.
3989
4199 - renderNodeDestructive(request, task, task.node, task.childIndex);
3990 + retryNode(request, task);
3991 pushSegmentFinale(
3992 segment.chunks,
3993 request.renderState,
@@ -4231,11 +4022,6 @@ function retryRenderTask(
4022 const ping = task.ping;
4023 x.then(ping, ping);
4024 task.thenableState = getThenableStateAfterSuspending();
4234 - // We pop one task off the stack because the node that suspended will be tried again,
4235 - // which will add it back onto the stack.
4236 - if (task.componentStack !== null) {
4237 - task.componentStack = task.componentStack.parent;
4238 - }
4025 return;
4026 } else if (
4027 enablePostpone &&
@@ -4299,8 +4085,12 @@ function retryReplayTask(request: Request, task: ReplayTask): void {
4085 try {
4086 // We call the destructive form that mutates this task. That way if something
4087 // suspends again, we can reuse the same task instead of spawning a new one.
4302 -
4303 - renderNodeDestructive(request, task, task.node, task.childIndex);
4088 + if (typeof task.replay.slots === 'number') {
4089 + const resumeSegmentID = task.replay.slots;
4090 + resumeNode(request, task, resumeSegmentID, task.node, task.childIndex);
4091 + } else {
4092 + retryNode(request, task);
4093 + }
4094
4095 if (task.replay.pendingTasks === 1 && task.replay.nodes.length > 0) {
4096 throw new Error(
@@ -4332,11 +4122,6 @@ function retryReplayTask(request: Request, task: ReplayTask): void {
4122 const ping = task.ping;
4123 x.then(ping, ping);
4124 task.thenableState = getThenableStateAfterSuspending();
4335 - // We pop one task off the stack because the node that suspended will be tried again,
4336 - // which will add it back onto the stack.
4337 - if (task.componentStack !== null) {
4338 - task.componentStack = task.componentStack.parent;
4339 - }
4125 return;
4126 }
4127 }
scripts/babel/transform-prevent-infinite-loops.js
+1 -1
@@ -13,7 +13,7 @@
13 // This should be reasonable for all loops in the source.
14 // Note that if the numbers are too large, the tests will take too long to fail
15 // for this to be useful (each individual test case might hit an infinite loop).
16 -const MAX_SOURCE_ITERATIONS = 5000;
16 +const MAX_SOURCE_ITERATIONS = 6000;
17 // Code in tests themselves is permitted to run longer.
18 // For example, in the fuzz tester.
19 const MAX_TEST_ITERATIONS = 5000;