Skip to content

Commit c0c2f55

Browse files
committed
Encode ReactIOInfo as its own row type
1 parent 7d4ec85 commit c0c2f55

File tree

2 files changed

+102
-51
lines changed

2 files changed

+102
-51
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
enablePostpone,
4848
enableProfilerTimer,
4949
enableComponentPerformanceTrack,
50+
enableAsyncDebugInfo,
5051
} from 'shared/ReactFeatureFlags';
5152

5253
import {
@@ -2740,6 +2741,37 @@ function resolveConsoleEntry(
27402741
);
27412742
}
27422743

2744+
function resolveIOInfo(
2745+
response: Response,
2746+
id: number,
2747+
model: UninitializedModel,
2748+
): void {
2749+
const chunks = response._chunks;
2750+
let chunk = chunks.get(id);
2751+
if (!chunk) {
2752+
chunk = createResolvedModelChunk(response, model);
2753+
chunks.set(id, chunk);
2754+
initializeModelChunk(chunk);
2755+
} else {
2756+
resolveModelChunk(chunk, model);
2757+
if (chunk.status === RESOLVED_MODEL) {
2758+
initializeModelChunk(chunk);
2759+
}
2760+
}
2761+
if (chunk.status === INITIALIZED) {
2762+
// TODO: Log.
2763+
} else {
2764+
chunk.then(
2765+
v => {
2766+
// TODO: Log.
2767+
},
2768+
e => {
2769+
// Ignore debug info errors for now. Unnecessary noise.
2770+
},
2771+
);
2772+
}
2773+
}
2774+
27432775
function mergeBuffer(
27442776
buffer: Array<Uint8Array>,
27452777
lastChunk: Uint8Array,
@@ -2844,7 +2876,7 @@ function flushComponentPerformance(
28442876

28452877
// First find the start time of the first component to know if it was running
28462878
// in parallel with the previous.
2847-
const debugInfo = root._debugInfo;
2879+
const debugInfo = __DEV__ && root._debugInfo;
28482880
if (debugInfo) {
28492881
for (let i = 1; i < debugInfo.length; i++) {
28502882
const info = debugInfo[i];
@@ -3101,6 +3133,17 @@ function processFullStringRow(
31013133
}
31023134
// Fallthrough to share the error with Console entries.
31033135
}
3136+
case 74 /* "J" */: {
3137+
if (
3138+
enableProfilerTimer &&
3139+
enableComponentPerformanceTrack &&
3140+
enableAsyncDebugInfo
3141+
) {
3142+
resolveIOInfo(response, id, row);
3143+
return;
3144+
}
3145+
// Fallthrough to share the error with Console entries.
3146+
}
31043147
case 87 /* "W" */: {
31053148
if (__DEV__) {
31063149
resolveConsoleEntry(response, row);

packages/react-server/src/ReactFlightServer.js

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,7 +1905,7 @@ function visitAsyncNode(
19051905
return ioNode;
19061906
}
19071907
// Outline the IO node.
1908-
emitIOChunk(request, ioNode);
1908+
serializeIONode(request, ioNode);
19091909
// Then emit a reference to us awaiting it in the current task.
19101910
request.pendingChunks++;
19111911
emitDebugChunk(request, task.id, {
@@ -1942,7 +1942,7 @@ function emitAsyncSequence(
19421942
// each occurrence. Right now we'll only track the first time it is invoked.
19431943
awaitedNode.end = performance.now();
19441944
}
1945-
emitIOChunk(request, awaitedNode);
1945+
serializeIONode(request, awaitedNode);
19461946
request.pendingChunks++;
19471947
emitDebugChunk(request, task.id, {
19481948
awaited: ((awaitedNode: any): ReactIOInfo), // This is deduped by this reference.
@@ -3493,80 +3493,88 @@ function outlineComponentInfo(
34933493
request.writtenObjects.set(componentInfo, serializeByValueID(id));
34943494
}
34953495

3496-
function outlineIOInfo(request: Request, ioInfo: ReactIOInfo): void {
3496+
function emitIOInfoChunk(
3497+
request: Request,
3498+
id: number,
3499+
start: number,
3500+
end: number,
3501+
stack: ?ReactStackTrace,
3502+
): void {
34973503
if (!__DEV__) {
34983504
// These errors should never make it into a build so we don't need to encode them in codes.json
34993505
// eslint-disable-next-line react-internal/prod-error-codes
35003506
throw new Error(
3501-
'outlineIOInfo should never be called in production mode. This is a bug in React.',
3507+
'emitIOInfoChunk should never be called in production mode. This is a bug in React.',
35023508
);
35033509
}
35043510

3505-
if (request.writtenObjects.has(ioInfo)) {
3506-
// Already written
3507-
return;
3508-
}
3509-
3510-
// Limit the number of objects we write to prevent emitting giant props objects.
35113511
let objectLimit = 10;
3512-
if (ioInfo.stack != null) {
3513-
// Ensure we have enough object limit to encode the stack trace.
3514-
objectLimit += ioInfo.stack.length;
3512+
if (stack) {
3513+
objectLimit += stack.length;
35153514
}
3516-
3517-
// We use the console encoding so that we can dedupe objects but don't necessarily
3518-
// use the full serialization that requires a task.
35193515
const counter = {objectLimit};
3516+
function replacer(
3517+
this:
3518+
| {+[key: string | number]: ReactClientValue}
3519+
| $ReadOnlyArray<ReactClientValue>,
3520+
parentPropertyName: string,
3521+
value: ReactClientValue,
3522+
): ReactJSONValue {
3523+
return renderConsoleValue(
3524+
request,
3525+
counter,
3526+
this,
3527+
parentPropertyName,
3528+
value,
3529+
);
3530+
}
35203531

3521-
// We can't serialize the ConsoleTask/Error objects so we need to omit them before serializing.
3522-
const relativeStartTimestamp = ioInfo.start - request.timeOrigin;
3523-
const relativeEndTimestamp = ioInfo.end - request.timeOrigin;
3532+
const relativeStartTimestamp = start - request.timeOrigin;
3533+
const relativeEndTimestamp = end - request.timeOrigin;
35243534
const debugIOInfo: Omit<ReactIOInfo, 'debugTask' | 'debugStack'> = {
35253535
start: relativeStartTimestamp,
35263536
end: relativeEndTimestamp,
3527-
stack: ioInfo.stack,
3537+
stack: stack,
35283538
};
3529-
const id = outlineConsoleValue(request, counter, debugIOInfo);
3530-
request.writtenObjects.set(ioInfo, serializeByValueID(id));
3539+
// $FlowFixMe[incompatible-type] stringify can return null
3540+
const json: string = stringify(debugIOInfo, replacer);
3541+
const row = id.toString(16) + ':J' + json + '\n';
3542+
const processedChunk = stringToChunk(row);
3543+
request.completedRegularChunks.push(processedChunk);
35313544
}
35323545

3533-
function emitIOChunk(request: Request, ioNode: IONode | PromiseNode): void {
3534-
if (!__DEV__) {
3535-
// These errors should never make it into a build so we don't need to encode them in codes.json
3536-
// eslint-disable-next-line react-internal/prod-error-codes
3537-
throw new Error(
3538-
'outlineIOInfo should never be called in production mode. This is a bug in React.',
3539-
);
3546+
function outlineIOInfo(request: Request, ioInfo: ReactIOInfo): void {
3547+
if (request.writtenObjects.has(ioInfo)) {
3548+
// Already written
3549+
return;
35403550
}
3551+
// We can't serialize the ConsoleTask/Error objects so we need to omit them before serializing.
3552+
request.pendingChunks++;
3553+
const id = request.nextChunkId++;
3554+
emitIOInfoChunk(request, id, ioInfo.start, ioInfo.end, ioInfo.stack);
3555+
request.writtenObjects.set(ioInfo, serializeByValueID(id));
3556+
}
35413557

3542-
if (request.writtenObjects.has(ioNode)) {
3558+
function serializeIONode(
3559+
request: Request,
3560+
ioNode: IONode | PromiseNode,
3561+
): string {
3562+
const existingRef = request.writtenObjects.get(ioNode);
3563+
if (existingRef !== undefined) {
35433564
// Already written
3544-
return;
3565+
return existingRef;
35453566
}
35463567

3547-
// Limit the number of objects we write to prevent emitting giant props objects.
3548-
let objectLimit = 10;
35493568
let stack = null;
35503569
if (ioNode.stack !== null) {
35513570
stack = filterStackTrace(request, ioNode.stack, 1);
3552-
// Ensure we have enough object limit to encode the stack trace.
3553-
objectLimit += stack.length;
35543571
}
3555-
3556-
// We use the console encoding so that we can dedupe objects but don't necessarily
3557-
// use the full serialization that requires a task.
3558-
const counter = {objectLimit};
3559-
3560-
// We can't serialize the ConsoleTask/Error objects so we need to omit them before serializing.
3561-
const relativeStartTimestamp = ioNode.start - request.timeOrigin;
3562-
const relativeEndTimestamp = ioNode.end - request.timeOrigin;
3563-
const debugIOInfo: Omit<ReactIOInfo, 'debugTask' | 'debugStack'> = {
3564-
start: relativeStartTimestamp,
3565-
end: relativeEndTimestamp,
3566-
stack: stack,
3567-
};
3568-
const id = outlineConsoleValue(request, counter, debugIOInfo);
3569-
request.writtenObjects.set(ioNode, serializeByValueID(id));
3572+
request.pendingChunks++;
3573+
const id = request.nextChunkId++;
3574+
emitIOInfoChunk(request, id, ioNode.start, ioNode.end, stack);
3575+
const ref = serializeByValueID(id);
3576+
request.writtenObjects.set(ioNode, ref);
3577+
return ref;
35703578
}
35713579

35723580
function emitTypedArrayChunk(

0 commit comments

Comments
 (0)