mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[Flight] Create a fast path parseStackTrace which skips generating a string stack (#33735)
When we know that the object that we pass in is immediately parsed, then we know it couldn't have been reified into a unstructured stack yet. In this path we assume that we'll trigger `Error.prepareStackTrace`. Since we know that nobody else will read the stack after us, we can skip generating a string stack and just return empty. We can also skip caching.
This commit is contained in:
parent
8ba3501cd9
commit
3a43e72d66
|
|
@ -94,6 +94,7 @@ import {
|
|||
getCurrentAsyncSequence,
|
||||
getAsyncSequenceFromPromise,
|
||||
parseStackTrace,
|
||||
parseStackTracePrivate,
|
||||
supportsComponentStorage,
|
||||
componentStorage,
|
||||
unbadgeConsole,
|
||||
|
|
@ -316,7 +317,7 @@ function patchConsole(consoleInst: typeof console, methodName: string) {
|
|||
// one stack frame but keeping it simple for now and include all frames.
|
||||
const stack = filterStackTrace(
|
||||
request,
|
||||
parseStackTrace(new Error('react-stack-top-frame'), 1),
|
||||
parseStackTracePrivate(new Error('react-stack-top-frame'), 1) || [],
|
||||
);
|
||||
request.pendingDebugChunks++;
|
||||
const owner: null | ReactComponentInfo = resolveOwner();
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import {resolveOwner} from './flight/ReactFlightCurrentOwner';
|
|||
import {resolveRequest, isAwaitInUserspace} from './ReactFlightServer';
|
||||
import {createHook, executionAsyncId, AsyncResource} from 'async_hooks';
|
||||
import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags';
|
||||
import {parseStackTrace} from './ReactFlightServerConfig';
|
||||
import {parseStackTracePrivate} from './ReactFlightServerConfig';
|
||||
|
||||
// $FlowFixMe[method-unbinding]
|
||||
const getAsyncId = AsyncResource.prototype.asyncId;
|
||||
|
|
@ -129,8 +129,8 @@ export function initAsyncDebugInfo(): void {
|
|||
if (request === null) {
|
||||
// We don't collect stacks for awaits that weren't in the scope of a specific render.
|
||||
} else {
|
||||
stack = parseStackTrace(new Error(), 5);
|
||||
if (!isAwaitInUserspace(request, stack)) {
|
||||
stack = parseStackTracePrivate(new Error(), 5);
|
||||
if (stack !== null && !isAwaitInUserspace(request, stack)) {
|
||||
// If this await was not done directly in user space, then clear the stack. We won't use it
|
||||
// anyway. This lets future awaits on this await know that we still need to get their stacks
|
||||
// until we find one in user space.
|
||||
|
|
@ -153,7 +153,8 @@ export function initAsyncDebugInfo(): void {
|
|||
node = ({
|
||||
tag: UNRESOLVED_PROMISE_NODE,
|
||||
owner: owner,
|
||||
stack: owner === null ? null : parseStackTrace(new Error(), 5),
|
||||
stack:
|
||||
owner === null ? null : parseStackTracePrivate(new Error(), 5),
|
||||
start: performance.now(),
|
||||
end: -1.1, // Set when we resolve.
|
||||
promise: new WeakRef((resource: Promise<any>)),
|
||||
|
|
@ -175,7 +176,8 @@ export function initAsyncDebugInfo(): void {
|
|||
node = ({
|
||||
tag: IO_NODE,
|
||||
owner: owner,
|
||||
stack: owner === null ? parseStackTrace(new Error(), 3) : null,
|
||||
stack:
|
||||
owner === null ? parseStackTracePrivate(new Error(), 3) : null,
|
||||
start: performance.now(),
|
||||
end: -1.1, // Only set when pinged.
|
||||
promise: null,
|
||||
|
|
@ -191,7 +193,8 @@ export function initAsyncDebugInfo(): void {
|
|||
node = ({
|
||||
tag: IO_NODE,
|
||||
owner: owner,
|
||||
stack: owner === null ? parseStackTrace(new Error(), 3) : null,
|
||||
stack:
|
||||
owner === null ? parseStackTracePrivate(new Error(), 3) : null,
|
||||
start: performance.now(),
|
||||
end: -1.1, // Only set when pinged.
|
||||
promise: null,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ function getMethodCallName(callSite: CallSite): string {
|
|||
return result;
|
||||
}
|
||||
|
||||
function collectStackTrace(
|
||||
function collectStackTracePrivate(
|
||||
error: Error,
|
||||
structuredStackTrace: CallSite[],
|
||||
): string {
|
||||
|
|
@ -79,13 +79,13 @@ function collectStackTrace(
|
|||
let filename = callSite.getScriptNameOrSourceURL() || '<anonymous>';
|
||||
if (filename === '<anonymous>') {
|
||||
filename = '';
|
||||
}
|
||||
if (callSite.isEval() && !filename) {
|
||||
if (callSite.isEval()) {
|
||||
const origin = callSite.getEvalOrigin();
|
||||
if (origin) {
|
||||
filename = origin.toString() + ', <anonymous>';
|
||||
}
|
||||
}
|
||||
}
|
||||
const line = callSite.getLineNumber() || 0;
|
||||
const col = callSite.getColumnNumber() || 0;
|
||||
const enclosingLine: number =
|
||||
|
|
@ -101,6 +101,15 @@ function collectStackTrace(
|
|||
result.push([name, filename, line, col, enclosingLine, enclosingCol]);
|
||||
}
|
||||
}
|
||||
collectedStackTrace = result;
|
||||
return '';
|
||||
}
|
||||
|
||||
function collectStackTrace(
|
||||
error: Error,
|
||||
structuredStackTrace: CallSite[],
|
||||
): string {
|
||||
collectStackTracePrivate(error, structuredStackTrace);
|
||||
// At the same time we generate a string stack trace just in case someone
|
||||
// else reads it. Ideally, we'd call the previous prepareStackTrace to
|
||||
// ensure it's in the expected format but it's common for that to be
|
||||
|
|
@ -115,7 +124,6 @@ function collectStackTrace(
|
|||
for (let i = 0; i < structuredStackTrace.length; i++) {
|
||||
stack += '\n at ' + structuredStackTrace[i].toString();
|
||||
}
|
||||
collectedStackTrace = result;
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +139,26 @@ const stackTraceCache: WeakMap<Error, ReactStackTrace> = __DEV__
|
|||
? new WeakMap()
|
||||
: (null: any);
|
||||
|
||||
// This version is only used when React fully owns the Error object and there's no risk of it having
|
||||
// been already initialized and no risky that anyone else will initialize it later.
|
||||
export function parseStackTracePrivate(
|
||||
error: Error,
|
||||
skipFrames: number,
|
||||
): null | ReactStackTrace {
|
||||
collectedStackTrace = null;
|
||||
framesToSkip = skipFrames;
|
||||
const previousPrepare = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = collectStackTracePrivate;
|
||||
try {
|
||||
if (error.stack !== '') {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
Error.prepareStackTrace = previousPrepare;
|
||||
}
|
||||
return collectedStackTrace;
|
||||
}
|
||||
|
||||
export function parseStackTrace(
|
||||
error: Error,
|
||||
skipFrames: number,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user