Improve cyclic thenable detection in ReactFlightReplyServer (#35369)

## Summary

This PR improves cyclic thenable detection in
`ReactFlightReplyServer.js`. Fixes #35368.
The previous fix only detected direct self-references (`inspectedValue
=== chunk`) and relied on the `cycleProtection` counter to eventually
bail out of longer cycles. This change keeps the existing
MAX_THENABLE_CYCLE_DEPTH ($1000$) `cycleProtection` cap as a hard
guardrail and adds a visited set so that we can detect self-cycles and
multi-node cycles as soon as any `ReactPromise` is revisited and while
still bounding the amount of work we do for deep acyclic chains via
`cycleProtection`.

## How did you test this change?

- Ran the existing test suite for the server renderer:

  ```bash
  yarn test react-server
  yarn test --prod react-server
  yarn flow dom-node
  yarn linc
  ```

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
This commit is contained in:
Christian Van
2025-12-17 06:22:26 -05:00
committed by GitHub
parent 88ee1f5955
commit b731fe28cc

View File

@@ -133,14 +133,20 @@ ReactPromise.prototype.then = function <T>(
// Recursively check if the value is itself a ReactPromise and if so if it points
// back to itself. This helps catch recursive thenables early error.
let cycleProtection = 0;
const visited = new Set<typeof ReactPromise>();
while (inspectedValue instanceof ReactPromise) {
cycleProtection++;
if (inspectedValue === chunk || cycleProtection > 1000) {
if (
inspectedValue === chunk ||
visited.has(inspectedValue) ||
cycleProtection > 1000
) {
if (typeof reject === 'function') {
reject(new Error('Cannot have cyclic thenables.'));
}
return;
}
visited.add(inspectedValue);
if (inspectedValue.status === INITIALIZED) {
inspectedValue = inspectedValue.value;
} else {