Skip to content

Commit 1540081

Browse files
authored
[Flight] Encode Async I/O Tasks using the Enclosing Line/Column (#33403)
Stacked on #33402. There's a bug in Chrome Performance tracking which uses the enclosing line/column instead of the callsite in stacks. For our fake eval:ed functions that represents functions on the server, we can position the enclosing function body at the position of the callsite to simulate getting the right line. Unfortunately, that doesn't give us exactly the right callsite when it's used for other purposes that uses the callsite like console logs and error reporting and stacks inside breakpoints. So I don't think we want to always do this. For ReactAsyncInfo/ReactIOInfo, the only thing we're going to use the fake task for is the Performance tracking, so it doesn't have any downsides until Chrome fixes the bug and we'd have to revert it. Therefore this PR uses that techniques only for those entries. We could do this for Server Components too but we're going to use those for other things too like console logs. I don't think it's worth duplicating the Task objects. That would also make it inconsistent with Client Components. For Client Components, we could in theory also generate fake evals but that would be way slower since there's so many of them and currently we rely on the native implementation for those. So doesn't seem worth fixing. But since we can at least fix it for RSC I/O/awaits we can do this hack.
1 parent 9cc74fe commit 1540081

File tree

1 file changed

+38
-5
lines changed

1 file changed

+38
-5
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,13 @@ function createElement(
813813
console,
814814
getTaskName(type),
815815
);
816-
const callStack = buildFakeCallStack(response, stack, env, createTaskFn);
816+
const callStack = buildFakeCallStack(
817+
response,
818+
stack,
819+
env,
820+
false,
821+
createTaskFn,
822+
);
817823
// This owner should ideally have already been initialized to avoid getting
818824
// user stack frames on the stack.
819825
const ownerTask =
@@ -2134,6 +2140,7 @@ function resolveErrorDev(
21342140
response,
21352141
stack,
21362142
env,
2143+
false,
21372144
// $FlowFixMe[incompatible-use]
21382145
Error.bind(
21392146
null,
@@ -2196,6 +2203,7 @@ function resolvePostponeDev(
21962203
response,
21972204
stack,
21982205
env,
2206+
false,
21992207
// $FlowFixMe[incompatible-use]
22002208
Error.bind(null, reason || ''),
22012209
);
@@ -2404,12 +2412,17 @@ function buildFakeCallStack<T>(
24042412
response: Response,
24052413
stack: ReactStackTrace,
24062414
environmentName: string,
2415+
useEnclosingLine: boolean,
24072416
innerCall: () => T,
24082417
): () => T {
24092418
let callStack = innerCall;
24102419
for (let i = 0; i < stack.length; i++) {
24112420
const frame = stack[i];
2412-
const frameKey = frame.join('-') + '-' + environmentName;
2421+
const frameKey =
2422+
frame.join('-') +
2423+
'-' +
2424+
environmentName +
2425+
(useEnclosingLine ? '-e' : '-n');
24132426
let fn = fakeFunctionCache.get(frameKey);
24142427
if (fn === undefined) {
24152428
const [name, filename, line, col, enclosingLine, enclosingCol] = frame;
@@ -2423,8 +2436,8 @@ function buildFakeCallStack<T>(
24232436
sourceMap,
24242437
line,
24252438
col,
2426-
enclosingLine,
2427-
enclosingCol,
2439+
useEnclosingLine ? line : enclosingLine,
2440+
useEnclosingLine ? col : enclosingCol,
24282441
environmentName,
24292442
);
24302443
// TODO: This cache should technically live on the response since the _debugFindSourceMapURL
@@ -2470,6 +2483,15 @@ function initializeFakeTask(
24702483
// If it's null, we can't initialize a task.
24712484
return null;
24722485
}
2486+
2487+
// Workaround for a bug where Chrome Performance tracking uses the enclosing line/column
2488+
// instead of the callsite. For ReactAsyncInfo/ReactIOInfo, the only thing we're going
2489+
// to use the fake task for is the Performance tracking so we encode the enclosing line/
2490+
// column at the callsite to get a better line number. We could do this for Components too
2491+
// but we're going to use those for other things too like console logs and it's not worth
2492+
// duplicating. If this bug is every fixed in Chrome, this should be set to false.
2493+
const useEnclosingLine = debugInfo.key === undefined;
2494+
24732495
const stack = debugInfo.stack;
24742496
const env: string =
24752497
debugInfo.env == null ? response._rootEnvironmentName : debugInfo.env;
@@ -2486,6 +2508,7 @@ function initializeFakeTask(
24862508
stack,
24872509
'"use ' + childEnvironmentName.toLowerCase() + '"',
24882510
env,
2511+
useEnclosingLine,
24892512
);
24902513
} else {
24912514
const cachedEntry = debugInfo.debugTask;
@@ -2510,6 +2533,7 @@ function initializeFakeTask(
25102533
stack,
25112534
taskName,
25122535
env,
2536+
useEnclosingLine,
25132537
));
25142538
}
25152539
}
@@ -2520,9 +2544,16 @@ function buildFakeTask(
25202544
stack: ReactStackTrace,
25212545
taskName: string,
25222546
env: string,
2547+
useEnclosingLine: boolean,
25232548
): ConsoleTask {
25242549
const createTaskFn = (console: any).createTask.bind(console, taskName);
2525-
const callStack = buildFakeCallStack(response, stack, env, createTaskFn);
2550+
const callStack = buildFakeCallStack(
2551+
response,
2552+
stack,
2553+
env,
2554+
useEnclosingLine,
2555+
createTaskFn,
2556+
);
25262557
if (ownerTask === null) {
25272558
const rootTask = getRootTask(response, env);
25282559
if (rootTask != null) {
@@ -2545,6 +2576,7 @@ const createFakeJSXCallStack = {
25452576
response,
25462577
stack,
25472578
environmentName,
2579+
false,
25482580
fakeJSXCallSite,
25492581
);
25502582
return callStackForError();
@@ -2681,6 +2713,7 @@ const replayConsoleWithCallStack = {
26812713
response,
26822714
stackTrace,
26832715
env,
2716+
false,
26842717
bindToConsole(methodName, args, env),
26852718
);
26862719
if (owner != null) {

0 commit comments

Comments
 (0)