@samitouri / QOS-React / commits / 792f192114

Tag all user space call sites with the "react-stack-bottom-frame" name (#30369)

Ideally we wouldn't need to filter out React internals and it'd just be covered by ignore listing by any downstream tool. E.g. a framework using captureOwnerStack could have its own ignore listing. Printed owner stacks would get browser source map ignore-listing. React DevTools could have its own ignore list for internals. However, it's nice to be able to provide nice owner stacks without a bunch of noise by default. Especially on the server since they have to be serialized. We currently call each function that calls into user space and track its stack frame. However, this needs code for checking each one and doesn't let us work across bundles. Instead, we can name each of these frame something predictable by giving the function a name. Unfortunately, it's a common practice to rename functions or inline them in compilers. Even if we didn't, others downstream from us or a dev-mode minifier could. I use this `.bind()` trick to avoid minifying these functions and ensure they get a unique name added to them in all browsers. It's not 100% fool proof since a smart enough compiler could also discover that the `this` value is not used and strip out the function and then inline it but nobody does this yet at least. This lets us find the bottom stack easily from stack traces just by looking for the name.

Sebastian Markbåge committed Jul 22, 2024 at 10:47 UTC 792f1921145e51bd06b836ffa0a16ecc39c8ee82
7 files changed +237 -335
packages/react-reconciler/src/ReactFiberCallUserSpace.js
+52 -29
@@ -14,41 +14,64 @@ import {isRendering, setIsRendering} from './ReactCurrentFiber';
14 // These indirections exists so we can exclude its stack frame in DEV (and anything below it).
15 // TODO: Consider marking the whole bundle instead of these boundaries.
16
17 -/** @noinline */
18 -export function callComponentInDEV<Props, Arg, R>(
17 +const callComponent = {
18 + 'react-stack-bottom-frame': function <Props, Arg, R>(
19 + Component: (p: Props, arg: Arg) => R,
20 + props: Props,
21 + secondArg: Arg,
22 + ): R {
23 + const wasRendering = isRendering;
24 + setIsRendering(true);
25 + try {
26 + const result = Component(props, secondArg);
27 + return result;
28 + } finally {
29 + setIsRendering(wasRendering);
30 + }
31 + },
32 +};
33 +
34 +export const callComponentInDEV: <Props, Arg, R>(
35 Component: (p: Props, arg: Arg) => R,
36 props: Props,
37 secondArg: Arg,
22 -): R {
23 - const wasRendering = isRendering;
24 - setIsRendering(true);
25 - try {
26 - const result = Component(props, secondArg);
27 - return result;
28 - } finally {
29 - setIsRendering(wasRendering);
30 - }
31 -}
38 +) => R = __DEV__
39 + ? // We use this technique to trick minifiers to preserve the function name.
40 + (callComponent['react-stack-bottom-frame'].bind(callComponent): any)
41 + : (null: any);
42
43 interface ClassInstance<R> {
44 render(): R;
45 }
46
37 -/** @noinline */
38 -export function callRenderInDEV<R>(instance: ClassInstance<R>): R {
39 - const wasRendering = isRendering;
40 - setIsRendering(true);
41 - try {
42 - const result = instance.render();
43 - return result;
44 - } finally {
45 - setIsRendering(wasRendering);
46 - }
47 -}
47 +const callRender = {
48 + 'react-stack-bottom-frame': function <R>(instance: ClassInstance<R>): R {
49 + const wasRendering = isRendering;
50 + setIsRendering(true);
51 + try {
52 + const result = instance.render();
53 + return result;
54 + } finally {
55 + setIsRendering(wasRendering);
56 + }
57 + },
58 +};
59
49 -/** @noinline */
50 -export function callLazyInitInDEV(lazy: LazyComponent<any, any>): any {
51 - const payload = lazy._payload;
52 - const init = lazy._init;
53 - return init(payload);
54 -}
60 +export const callRenderInDEV: <R>(instance: ClassInstance<R>) => R => R =
61 + __DEV__
62 + ? // We use this technique to trick minifiers to preserve the function name.
63 + (callRender['react-stack-bottom-frame'].bind(callRender): any)
64 + : (null: any);
65 +
66 +const callLazyInit = {
67 + 'react-stack-bottom-frame': function (lazy: LazyComponent<any, any>): any {
68 + const payload = lazy._payload;
69 + const init = lazy._init;
70 + return init(payload);
71 + },
72 +};
73 +
74 +export const callLazyInitInDEV: (lazy: LazyComponent<any, any>) => any = __DEV__
75 + ? // We use this technique to trick minifiers to preserve the function name.
76 + (callLazyInit['react-stack-bottom-frame'].bind(callLazyInit): any)
77 + : (null: any);
packages/react-reconciler/src/ReactFiberOwnerStack.js
+7 -77
@@ -7,71 +7,13 @@
7 * @flow
8 */
9
10 -import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
11 -
12 -import {
13 - callLazyInitInDEV,
14 - callComponentInDEV,
15 - callRenderInDEV,
16 -} from './ReactFiberCallUserSpace';
17 -
10 // TODO: Make this configurable on the root.
11 const externalRegExp = /\/node\_modules\/|\(\<anonymous\>\)/;
12
21 -let callComponentFrame: null | string = null;
22 -let callIteratorFrame: null | string = null;
23 -let callLazyInitFrame: null | string = null;
24 -
13 function isNotExternal(stackFrame: string): boolean {
14 return !externalRegExp.test(stackFrame);
15 }
16
29 -function initCallComponentFrame(): string {
30 - // Extract the stack frame of the callComponentInDEV function.
31 - const error = callComponentInDEV(Error, 'react-stack-top-frame', {});
32 - const stack = error.stack;
33 - const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
34 - const endIdx = stack.indexOf('\n', startIdx);
35 - if (endIdx === -1) {
36 - return stack.slice(startIdx);
37 - }
38 - return stack.slice(startIdx, endIdx);
39 -}
40 -
41 -function initCallRenderFrame(): string {
42 - // Extract the stack frame of the callRenderInDEV function.
43 - try {
44 - (callRenderInDEV: any)({render: null});
45 - return '';
46 - } catch (error) {
47 - const stack = error.stack;
48 - const startIdx = stack.startsWith('TypeError: ')
49 - ? stack.indexOf('\n') + 1
50 - : 0;
51 - const endIdx = stack.indexOf('\n', startIdx);
52 - if (endIdx === -1) {
53 - return stack.slice(startIdx);
54 - }
55 - return stack.slice(startIdx, endIdx);
56 - }
57 -}
58 -
59 -function initCallLazyInitFrame(): string {
60 - // Extract the stack frame of the callLazyInitInDEV function.
61 - const error = callLazyInitInDEV({
62 - $$typeof: REACT_LAZY_TYPE,
63 - _init: Error,
64 - _payload: 'react-stack-top-frame',
65 - });
66 - const stack = error.stack;
67 - const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
68 - const endIdx = stack.indexOf('\n', startIdx);
69 - if (endIdx === -1) {
70 - return stack.slice(startIdx);
71 - }
72 - return stack.slice(startIdx, endIdx);
73 -}
74 -
17 function filterDebugStack(error: Error): string {
18 // Since stacks can be quite large and we pass a lot of them, we filter them out eagerly
19 // to save bandwidth even in DEV. We'll also replay these stacks on the client so by
@@ -83,32 +25,20 @@ function filterDebugStack(error: Error): string {
25 // don't want/need.
26 stack = stack.slice(29);
27 }
86 - const frames = stack.split('\n').slice(1);
87 - if (callComponentFrame === null) {
88 - callComponentFrame = initCallComponentFrame();
89 - }
90 - let lastFrameIdx = frames.indexOf(callComponentFrame);
91 - if (lastFrameIdx === -1) {
92 - if (callLazyInitFrame === null) {
93 - callLazyInitFrame = initCallLazyInitFrame();
94 - }
95 - lastFrameIdx = frames.indexOf(callLazyInitFrame);
96 - if (lastFrameIdx === -1) {
97 - if (callIteratorFrame === null) {
98 - callIteratorFrame = initCallRenderFrame();
99 - }
100 - lastFrameIdx = frames.indexOf(callIteratorFrame);
101 - }
28 + let idx = stack.indexOf('react-stack-bottom-frame');
29 + if (idx !== -1) {
30 + idx = stack.lastIndexOf('\n', idx);
31 }
103 - if (lastFrameIdx !== -1) {
104 - // Cut off everything after our "callComponent" slot since it'll be Fiber internals.
105 - frames.length = lastFrameIdx;
32 + if (idx !== -1) {
33 + // Cut off everything after the bottom frame since it'll be internals.
34 + stack = stack.slice(0, idx);
35 } else {
36 // We didn't find any internal callsite out to user space.
37 // This means that this was called outside an owner or the owner is fully internal.
38 // To keep things light we exclude the entire trace in this case.
39 return '';
40 }
41 + const frames = stack.split('\n').slice(1);
42 return frames.filter(isNotExternal).join('\n');
43 }
44
packages/react-server/src/ReactFizzCallUserSpace.js
+38 -15
@@ -12,27 +12,50 @@ import type {LazyComponent} from 'react/src/ReactLazy';
12 // These indirections exists so we can exclude its stack frame in DEV (and anything below it).
13 // TODO: Consider marking the whole bundle instead of these boundaries.
14
15 -/** @noinline */
16 -export function callComponentInDEV<Props, Arg, R>(
15 +const callComponent = {
16 + 'react-stack-bottom-frame': function <Props, Arg, R>(
17 + Component: (p: Props, arg: Arg) => R,
18 + props: Props,
19 + secondArg: Arg,
20 + ): R {
21 + return Component(props, secondArg);
22 + },
23 +};
24 +
25 +export const callComponentInDEV: <Props, Arg, R>(
26 Component: (p: Props, arg: Arg) => R,
27 props: Props,
28 secondArg: Arg,
20 -): R {
21 - return Component(props, secondArg);
22 -}
29 +) => R = __DEV__
30 + ? // We use this technique to trick minifiers to preserve the function name.
31 + (callComponent['react-stack-bottom-frame'].bind(callComponent): any)
32 + : (null: any);
33
34 interface ClassInstance<R> {
35 render(): R;
36 }
37
28 -/** @noinline */
29 -export function callRenderInDEV<R>(instance: ClassInstance<R>): R {
30 - return instance.render();
31 -}
38 +const callRender = {
39 + 'react-stack-bottom-frame': function <R>(instance: ClassInstance<R>): R {
40 + return instance.render();
41 + },
42 +};
43
33 -/** @noinline */
34 -export function callLazyInitInDEV(lazy: LazyComponent<any, any>): any {
35 - const payload = lazy._payload;
36 - const init = lazy._init;
37 - return init(payload);
38 -}
44 +export const callRenderInDEV: <R>(instance: ClassInstance<R>) => R => R =
45 + __DEV__
46 + ? // We use this technique to trick minifiers to preserve the function name.
47 + (callRender['react-stack-bottom-frame'].bind(callRender): any)
48 + : (null: any);
49 +
50 +const callLazyInit = {
51 + 'react-stack-bottom-frame': function (lazy: LazyComponent<any, any>): any {
52 + const payload = lazy._payload;
53 + const init = lazy._init;
54 + return init(payload);
55 + },
56 +};
57 +
58 +export const callLazyInitInDEV: (lazy: LazyComponent<any, any>) => any = __DEV__
59 + ? // We use this technique to trick minifiers to preserve the function name.
60 + (callLazyInit['react-stack-bottom-frame'].bind(callLazyInit): any)
61 + : (null: any);
packages/react-server/src/ReactFizzOwnerStack.js
+7 -77
@@ -7,71 +7,13 @@
7 * @flow
8 */
9
10 -import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
11 -
12 -import {
13 - callLazyInitInDEV,
14 - callComponentInDEV,
15 - callRenderInDEV,
16 -} from './ReactFizzCallUserSpace';
17 -
10 // TODO: Make this configurable on the root.
11 const externalRegExp = /\/node\_modules\/|\(\<anonymous\>\)/;
12
21 -let callComponentFrame: null | string = null;
22 -let callIteratorFrame: null | string = null;
23 -let callLazyInitFrame: null | string = null;
24 -
13 function isNotExternal(stackFrame: string): boolean {
14 return !externalRegExp.test(stackFrame);
15 }
16
29 -function initCallComponentFrame(): string {
30 - // Extract the stack frame of the callComponentInDEV function.
31 - const error = callComponentInDEV(Error, 'react-stack-top-frame', {});
32 - const stack = error.stack;
33 - const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
34 - const endIdx = stack.indexOf('\n', startIdx);
35 - if (endIdx === -1) {
36 - return stack.slice(startIdx);
37 - }
38 - return stack.slice(startIdx, endIdx);
39 -}
40 -
41 -function initCallRenderFrame(): string {
42 - // Extract the stack frame of the callRenderInDEV function.
43 - try {
44 - (callRenderInDEV: any)({render: null});
45 - return '';
46 - } catch (error) {
47 - const stack = error.stack;
48 - const startIdx = stack.startsWith('TypeError: ')
49 - ? stack.indexOf('\n') + 1
50 - : 0;
51 - const endIdx = stack.indexOf('\n', startIdx);
52 - if (endIdx === -1) {
53 - return stack.slice(startIdx);
54 - }
55 - return stack.slice(startIdx, endIdx);
56 - }
57 -}
58 -
59 -function initCallLazyInitFrame(): string {
60 - // Extract the stack frame of the callLazyInitInDEV function.
61 - const error = callLazyInitInDEV({
62 - $$typeof: REACT_LAZY_TYPE,
63 - _init: Error,
64 - _payload: 'react-stack-top-frame',
65 - });
66 - const stack = error.stack;
67 - const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
68 - const endIdx = stack.indexOf('\n', startIdx);
69 - if (endIdx === -1) {
70 - return stack.slice(startIdx);
71 - }
72 - return stack.slice(startIdx, endIdx);
73 -}
74 -
17 function filterDebugStack(error: Error): string {
18 // Since stacks can be quite large and we pass a lot of them, we filter them out eagerly
19 // to save bandwidth even in DEV. We'll also replay these stacks on the client so by
@@ -83,32 +25,20 @@ function filterDebugStack(error: Error): string {
25 // don't want/need.
26 stack = stack.slice(29);
27 }
86 - const frames = stack.split('\n').slice(1);
87 - if (callComponentFrame === null) {
88 - callComponentFrame = initCallComponentFrame();
89 - }
90 - let lastFrameIdx = frames.indexOf(callComponentFrame);
91 - if (lastFrameIdx === -1) {
92 - if (callLazyInitFrame === null) {
93 - callLazyInitFrame = initCallLazyInitFrame();
94 - }
95 - lastFrameIdx = frames.indexOf(callLazyInitFrame);
96 - if (lastFrameIdx === -1) {
97 - if (callIteratorFrame === null) {
98 - callIteratorFrame = initCallRenderFrame();
99 - }
100 - lastFrameIdx = frames.indexOf(callIteratorFrame);
101 - }
28 + let idx = stack.indexOf('react-stack-bottom-frame');
29 + if (idx !== -1) {
30 + idx = stack.lastIndexOf('\n', idx);
31 }
103 - if (lastFrameIdx !== -1) {
104 - // Cut off everything after our "callComponent" slot since it'll be Fiber internals.
105 - frames.length = lastFrameIdx;
32 + if (idx !== -1) {
33 + // Cut off everything after the bottom frame since it'll be internals.
34 + stack = stack.slice(0, idx);
35 } else {
36 // We didn't find any internal callsite out to user space.
37 // This means that this was called outside an owner or the owner is fully internal.
38 // To keep things light we exclude the entire trace in this case.
39 return '';
40 }
41 + const frames = stack.split('\n').slice(1);
42 return frames.filter(isNotExternal).join('\n');
43 }
44
packages/react-server/src/ReactFizzServer.js
+1 -1
@@ -1528,7 +1528,7 @@ function finishClassComponent(
1528 ): ReactNodeList {
1529 let nextChildren;
1530 if (__DEV__) {
1531 - nextChildren = callRenderInDEV(instance);
1531 + nextChildren = (callRenderInDEV(instance): any);
1532 } else {
1533 nextChildren = instance.render();
1534 }
packages/react-server/src/ReactFlightCallUserSpace.js new
+119
@@ -0,0 +1,119 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {LazyComponent} from 'react/src/ReactLazy';
11 +
12 +import type {ReactComponentInfo} from 'shared/ReactTypes';
13 +
14 +import type {ReactClientValue} from './ReactFlightServer';
15 +
16 +import {setCurrentOwner} from './flight/ReactFlightCurrentOwner';
17 +
18 +import {
19 + supportsComponentStorage,
20 + componentStorage,
21 +} from './ReactFlightServerConfig';
22 +
23 +import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
24 +
25 +// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
26 +// TODO: Consider marking the whole bundle instead of these boundaries.
27 +
28 +const callComponent = {
29 + 'react-stack-bottom-frame': function <Props, R>(
30 + Component: (p: Props, arg: void) => R,
31 + props: Props,
32 + componentDebugInfo: ReactComponentInfo,
33 + debugTask: null | ConsoleTask,
34 + ): R {
35 + // The secondArg is always undefined in Server Components since refs error early.
36 + const secondArg = undefined;
37 + setCurrentOwner(componentDebugInfo);
38 + try {
39 + if (supportsComponentStorage) {
40 + // Run the component in an Async Context that tracks the current owner.
41 + if (enableOwnerStacks && debugTask) {
42 + return debugTask.run(
43 + // $FlowFixMe[method-unbinding]
44 + componentStorage.run.bind(
45 + componentStorage,
46 + componentDebugInfo,
47 + Component,
48 + props,
49 + secondArg,
50 + ),
51 + );
52 + }
53 + return componentStorage.run(
54 + componentDebugInfo,
55 + Component,
56 + props,
57 + secondArg,
58 + );
59 + } else {
60 + if (enableOwnerStacks && debugTask) {
61 + return debugTask.run(Component.bind(null, props, secondArg));
62 + }
63 + return Component(props, secondArg);
64 + }
65 + } finally {
66 + setCurrentOwner(null);
67 + }
68 + },
69 +};
70 +
71 +export const callComponentInDEV: <Props, R>(
72 + Component: (p: Props, arg: void) => R,
73 + props: Props,
74 + componentDebugInfo: ReactComponentInfo,
75 + debugTask: null | ConsoleTask,
76 +) => R = __DEV__
77 + ? // We use this technique to trick minifiers to preserve the function name.
78 + (callComponent['react-stack-bottom-frame'].bind(callComponent): any)
79 + : (null: any);
80 +
81 +const callLazyInit = {
82 + 'react-stack-bottom-frame': function (lazy: LazyComponent<any, any>): any {
83 + const payload = lazy._payload;
84 + const init = lazy._init;
85 + return init(payload);
86 + },
87 +};
88 +
89 +export const callLazyInitInDEV: (lazy: LazyComponent<any, any>) => any = __DEV__
90 + ? // We use this technique to trick minifiers to preserve the function name.
91 + (callLazyInit['react-stack-bottom-frame'].bind(callLazyInit): any)
92 + : (null: any);
93 +
94 +const callIterator = {
95 + 'react-stack-bottom-frame': function (
96 + iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,
97 + progress: (
98 + entry:
99 + | {done: false, +value: ReactClientValue, ...}
100 + | {done: true, +value: ReactClientValue, ...},
101 + ) => void,
102 + error: (reason: mixed) => void,
103 + ): void {
104 + iterator.next().then(progress, error);
105 + },
106 +};
107 +
108 +export const callIteratorInDEV: (
109 + iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,
110 + progress: (
111 + entry:
112 + | {done: false, +value: ReactClientValue, ...}
113 + | {done: true, +value: ReactClientValue, ...},
114 + ) => void,
115 + error: (reason: mixed) => void,
116 +) => void = __DEV__
117 + ? // We use this technique to trick minifiers to preserve the function name.
118 + (callIterator['react-stack-bottom-frame'].bind(callIterator): any)
119 + : (null: any);
packages/react-server/src/ReactFlightServer.js
+13 -136
@@ -75,8 +75,6 @@ import {
75 isServerReference,
76 supportsRequestStorage,
77 requestStorage,
78 - supportsComponentStorage,
79 - componentStorage,
78 createHints,
79 initAsyncDebugInfo,
80 } from './ReactFlightServerConfig';
@@ -99,6 +97,12 @@ import {resolveOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner';
97
98 import {getOwnerStackByComponentInfoInDev} from './flight/ReactFlightComponentStack';
99
100 +import {
101 + callComponentInDEV,
102 + callLazyInitInDEV,
103 + callIteratorInDEV,
104 +} from './ReactFlightCallUserSpace';
105 +
106 import {
107 getIteratorFn,
108 REACT_ELEMENT_TYPE,
@@ -129,10 +133,6 @@ import {SuspenseException, getSuspendedThenable} from './ReactFlightThenable';
133 // TODO: Make this configurable on the Request.
134 const externalRegExp = /\/node\_modules\/| \(node\:| node\:|\(\<anonymous\>\)/;
135
132 -let callComponentFrame: null | string = null;
133 -let callIteratorFrame: null | string = null;
134 -let callLazyInitFrame: null | string = null;
135 -
136 function isNotExternal(stackFrame: string): boolean {
137 return !externalRegExp.test(stackFrame);
138 }
@@ -168,52 +168,6 @@ function getStack(error: Error): string {
168 }
169 }
170
171 -function initCallComponentFrame(): string {
172 - // Extract the stack frame of the callComponentInDEV function.
173 - const error = callComponentInDEV(Error, 'react-stack-top-frame', {}, null);
174 - const stack = getStack(error);
175 - const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
176 - const endIdx = stack.indexOf('\n', startIdx);
177 - if (endIdx === -1) {
178 - return stack.slice(startIdx);
179 - }
180 - return stack.slice(startIdx, endIdx);
181 -}
182 -
183 -function initCallIteratorFrame(): string {
184 - // Extract the stack frame of the callIteratorInDEV function.
185 - try {
186 - (callIteratorInDEV: any)({next: null});
187 - return '';
188 - } catch (error) {
189 - const stack = getStack(error);
190 - const startIdx = stack.startsWith('TypeError: ')
191 - ? stack.indexOf('\n') + 1
192 - : 0;
193 - const endIdx = stack.indexOf('\n', startIdx);
194 - if (endIdx === -1) {
195 - return stack.slice(startIdx);
196 - }
197 - return stack.slice(startIdx, endIdx);
198 - }
199 -}
200 -
201 -function initCallLazyInitFrame(): string {
202 - // Extract the stack frame of the callLazyInitInDEV function.
203 - const error = callLazyInitInDEV({
204 - $$typeof: REACT_LAZY_TYPE,
205 - _init: Error,
206 - _payload: 'react-stack-top-frame',
207 - });
208 - const stack = getStack(error);
209 - const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
210 - const endIdx = stack.indexOf('\n', startIdx);
211 - if (endIdx === -1) {
212 - return stack.slice(startIdx);
213 - }
214 - return stack.slice(startIdx, endIdx);
215 -}
216 -
171 function filterDebugStack(error: Error): string {
172 // Since stacks can be quite large and we pass a lot of them, we filter them out eagerly
173 // to save bandwidth even in DEV. We'll also replay these stacks on the client so by
@@ -225,27 +179,15 @@ function filterDebugStack(error: Error): string {
179 // don't want/need.
180 stack = stack.slice(29);
181 }
228 - const frames = stack.split('\n').slice(1);
229 - if (callComponentFrame === null) {
230 - callComponentFrame = initCallComponentFrame();
231 - }
232 - let lastFrameIdx = frames.indexOf(callComponentFrame);
233 - if (lastFrameIdx === -1) {
234 - if (callLazyInitFrame === null) {
235 - callLazyInitFrame = initCallLazyInitFrame();
236 - }
237 - lastFrameIdx = frames.indexOf(callLazyInitFrame);
238 - if (lastFrameIdx === -1) {
239 - if (callIteratorFrame === null) {
240 - callIteratorFrame = initCallIteratorFrame();
241 - }
242 - lastFrameIdx = frames.indexOf(callIteratorFrame);
243 - }
182 + let idx = stack.indexOf('react-stack-bottom-frame');
183 + if (idx !== -1) {
184 + idx = stack.lastIndexOf('\n', idx);
185 }
245 - if (lastFrameIdx !== -1) {
246 - // Cut off everything after our "callComponent" slot since it'll be Flight internals.
247 - frames.length = lastFrameIdx;
186 + if (idx !== -1) {
187 + // Cut off everything after the bottom frame since it'll be internals.
188 + stack = stack.slice(0, idx);
189 }
190 + const frames = stack.split('\n').slice(1);
191 return frames.filter(isNotExternal).join('\n');
192 }
193
@@ -816,20 +758,6 @@ function serializeReadableStream(
758 return serializeByValueID(streamTask.id);
759 }
760
819 -// This indirect exists so we can exclude its stack frame in DEV (and anything below it).
820 -/** @noinline */
821 -function callIteratorInDEV(
822 - iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,
823 - progress: (
824 - entry:
825 - | {done: false, +value: ReactClientValue, ...}
826 - | {done: true, +value: ReactClientValue, ...},
827 - ) => void,
828 - error: (reason: mixed) => void,
829 -) {
830 - iterator.next().then(progress, error);
831 -}
832 -
761 function serializeAsyncIterable(
762 request: Request,
763 task: Task,
@@ -1029,57 +957,6 @@ function createLazyWrapperAroundWakeable(wakeable: Wakeable) {
957 return lazyType;
958 }
959
1032 -// This indirect exists so we can exclude its stack frame in DEV (and anything below it).
1033 -/** @noinline */
1034 -function callComponentInDEV<Props, R>(
1035 - Component: (p: Props, arg: void) => R,
1036 - props: Props,
1037 - componentDebugInfo: ReactComponentInfo,
1038 - debugTask: null | ConsoleTask,
1039 -): R {
1040 - // The secondArg is always undefined in Server Components since refs error early.
1041 - const secondArg = undefined;
1042 - setCurrentOwner(componentDebugInfo);
1043 - try {
1044 - if (supportsComponentStorage) {
1045 - // Run the component in an Async Context that tracks the current owner.
1046 - if (enableOwnerStacks && debugTask) {
1047 - return debugTask.run(
1048 - // $FlowFixMe[method-unbinding]
1049 - componentStorage.run.bind(
1050 - componentStorage,
1051 - componentDebugInfo,
1052 - Component,
1053 - props,
1054 - secondArg,
1055 - ),
1056 - );
1057 - }
1058 - return componentStorage.run(
1059 - componentDebugInfo,
1060 - Component,
1061 - props,
1062 - secondArg,
1063 - );
1064 - } else {
1065 - if (enableOwnerStacks && debugTask) {
1066 - return debugTask.run(Component.bind(null, props, secondArg));
1067 - }
1068 - return Component(props, secondArg);
1069 - }
1070 - } finally {
1071 - setCurrentOwner(null);
1072 - }
1073 -}
1074 -
1075 -// This indirect exists so we can exclude its stack frame in DEV (and anything below it).
1076 -/** @noinline */
1077 -function callLazyInitInDEV(lazy: LazyComponent<any, any>): any {
1078 - const payload = lazy._payload;
1079 - const init = lazy._init;
1080 - return init(payload);
1081 -}
1082 -
960 function callWithDebugContextInDEV<A, T>(
961 task: Task,
962 callback: A => T,