Skip to content

Commit c364caf

Browse files
committed
Ensure the temporary corruption of internal state goes unnoticed
1 parent d814027 commit c364caf

File tree

2 files changed

+23
-11
lines changed

2 files changed

+23
-11
lines changed

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,7 @@ describe('ReactFlightDOMNode', () => {
809809
resolvePendingPromise = value => {
810810
p2.status = 'fulfilled';
811811
p2.value = value;
812+
resolve(value);
812813
};
813814
});
814815
const p3 = new Promise(() => {});
@@ -888,11 +889,11 @@ describe('ReactFlightDOMNode', () => {
888889
},
889890
);
890891

892+
resolvePendingPromise('custom-instrum-resolve');
891893
await serverAct(
892894
async () =>
893895
new Promise(resolve => {
894896
setImmediate(() => {
895-
resolvePendingPromise();
896897
clientAbortController.abort();
897898
resolve();
898899
});
@@ -908,7 +909,9 @@ describe('ReactFlightDOMNode', () => {
908909
expect(normalizeCodeLocInfo(componentStack)).toBe(
909910
'\n' +
910911
' in SharedComponent (at **)\n' +
911-
' in ServerComponent\n' +
912+
' in ServerComponent' +
913+
(gate(flags => flags.enableAsyncDebugInfo) ? ' (at **)' : '') +
914+
'\n' +
912915
' in Suspense\n' +
913916
' in body\n' +
914917
' in html\n' +
@@ -930,11 +933,13 @@ describe('ReactFlightDOMNode', () => {
930933
expect(ignoreListStack(ownerStack)).toBe(
931934
// eslint-disable-next-line react-internal/safe-string-coercion
932935
'' +
936+
// The concrete location may change as this test is updated.
937+
// Just make sure they still point at React.use(p2)
933938
(gate(flags => flags.enableAsyncDebugInfo)
934939
? '\n at SharedComponent (./ReactFlightDOMNode-test.js:794:7)'
935940
: '') +
936-
'\n at ServerComponent (file://./ReactFlightDOMNode-test.js:815:26)' +
937-
'\n at App (file://./ReactFlightDOMNode-test.js:832:25)',
941+
'\n at ServerComponent (file://./ReactFlightDOMNode-test.js:816:26)' +
942+
'\n at App (file://./ReactFlightDOMNode-test.js:833:25)',
938943
);
939944
} else {
940945
expect(ownerStack).toBeNull();
@@ -1525,12 +1530,12 @@ describe('ReactFlightDOMNode', () => {
15251530
'\n' +
15261531
' in Dynamic' +
15271532
(gate(flags => flags.enableAsyncDebugInfo)
1528-
? ' (file://ReactFlightDOMNode-test.js:1395:27)\n'
1533+
? ' (file://ReactFlightDOMNode-test.js:1400:27)\n'
15291534
: '\n') +
15301535
' in body\n' +
15311536
' in html\n' +
1532-
' in App (file://ReactFlightDOMNode-test.js:1412:25)\n' +
1533-
' in ClientRoot (ReactFlightDOMNode-test.js:1487:16)',
1537+
' in App (file://ReactFlightDOMNode-test.js:1417:25)\n' +
1538+
' in ClientRoot (ReactFlightDOMNode-test.js:1492:16)',
15341539
);
15351540
} else {
15361541
expect(
@@ -1539,7 +1544,7 @@ describe('ReactFlightDOMNode', () => {
15391544
'\n' +
15401545
' in body\n' +
15411546
' in html\n' +
1542-
' in ClientRoot (ReactFlightDOMNode-test.js:1487:16)',
1547+
' in ClientRoot (ReactFlightDOMNode-test.js:1492:16)',
15431548
);
15441549
}
15451550

@@ -1549,16 +1554,16 @@ describe('ReactFlightDOMNode', () => {
15491554
normalizeCodeLocInfo(ownerStack, {preserveLocation: true}),
15501555
).toBe(
15511556
'\n' +
1552-
' in Dynamic (file://ReactFlightDOMNode-test.js:1395:27)\n' +
1553-
' in App (file://ReactFlightDOMNode-test.js:1412:25)',
1557+
' in Dynamic (file://ReactFlightDOMNode-test.js:1400:27)\n' +
1558+
' in App (file://ReactFlightDOMNode-test.js:1417:25)',
15541559
);
15551560
} else {
15561561
expect(
15571562
normalizeCodeLocInfo(ownerStack, {preserveLocation: true}),
15581563
).toBe(
15591564
'' +
15601565
'\n' +
1561-
' in App (file://ReactFlightDOMNode-test.js:1412:25)',
1566+
' in App (file://ReactFlightDOMNode-test.js:1417:25)',
15621567
);
15631568
}
15641569
} else {

packages/react-server/src/ReactFizzThenable.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,19 @@ export function ensureSuspendableThenableStateDEV(
255255
): () => void {
256256
if (__DEV__) {
257257
const lastThenable = thenableState[thenableState.length - 1];
258+
// Reset the last thenable back to pending.
258259
switch (lastThenable.status) {
259260
case 'fulfilled':
260261
const previousThenableValue = lastThenable.value;
262+
const previousThenableThen = lastThenable.then;
261263
delete lastThenable.value;
262264
delete (lastThenable: any).status;
265+
// We'll call .then again if we resuspend. Since we potentially corrupted
266+
// the internal state of unknown classes, we need to diffuse the potential
267+
// crash by replacing the .then method with a noop.
268+
lastThenable.then = noop;
263269
return () => {
270+
lastThenable.then = previousThenableThen.bind(lastThenable);
264271
lastThenable.value = previousThenableValue;
265272
lastThenable.status = 'fulfilled';
266273
};

0 commit comments

Comments
 (0)