Skip to content

Commit 651e755

Browse files
committed
stash the component stack on the thrown value and reuse (#25790)
ErrorBoundaries are currently not fully composable. The reason is if you decide your boundary cannot handle a particular error and rethrow it to higher boundary the React runtime does not understand that this throw is a forward and it recreates the component stack from the Boundary position. This loses fidelity and is especially bad if the boundary is limited it what it handles and high up in the component tree. This implementation uses a WeakMap to store component stacks for values that are objects. If an error is rethrown from an ErrorBoundary the stack will be pulled from the map if it exists. This doesn't work for thrown primitives but this is uncommon and stashing the stack on the primitive also wouldn't work DiffTrain build for [a9cc325](a9cc325)
1 parent b19c9fe commit 651e755

19 files changed

+647
-447
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
fea900e45447214ddd6ef69076ab7e38433b5ffd
1+
a9cc32511a12c261ee719e5383818182800d6af4

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,4 +618,4 @@ exports.useSyncExternalStore = function (
618618
exports.useTransition = function () {
619619
return ReactCurrentDispatcher.current.useTransition();
620620
};
621-
exports.version = "18.3.0-www-classic-22e81a9f";
621+
exports.version = "18.3.0-www-classic-707cd731";

compiled/facebook-www/React-profiling.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ exports.useSyncExternalStore = function (
622622
exports.useTransition = function () {
623623
return ReactCurrentDispatcher.current.useTransition();
624624
};
625-
exports.version = "18.3.0-www-classic-49d2df34";
625+
exports.version = "18.3.0-www-classic-7063c424";
626626
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
627627
"function" ===
628628
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ if (__DEV__) {
6666
return self;
6767
}
6868

69-
var ReactVersion = "18.3.0-www-classic-ed34a3bc";
69+
var ReactVersion = "18.3.0-www-classic-fc548c7d";
7070

7171
var LegacyRoot = 0;
7272
var ConcurrentRoot = 1;
@@ -13644,17 +13644,37 @@ if (__DEV__) {
1364413644
return shouldUpdate;
1364513645
}
1364613646

13647+
var CapturedStacks = new WeakMap();
1364713648
function createCapturedValueAtFiber(value, source) {
1364813649
// If the value is an error, call this function immediately after it is thrown
1364913650
// so the stack is accurate.
13651+
var stack;
13652+
13653+
if (typeof value === "object" && value !== null) {
13654+
var capturedStack = CapturedStacks.get(value);
13655+
13656+
if (typeof capturedStack === "string") {
13657+
stack = capturedStack;
13658+
} else {
13659+
stack = getStackByFiberInDevAndProd(source);
13660+
CapturedStacks.set(value, stack);
13661+
}
13662+
} else {
13663+
stack = getStackByFiberInDevAndProd(source);
13664+
}
13665+
1365013666
return {
1365113667
value: value,
1365213668
source: source,
13653-
stack: getStackByFiberInDevAndProd(source),
13669+
stack: stack,
1365413670
digest: null
1365513671
};
1365613672
}
13657-
function createCapturedValue(value, digest, stack) {
13673+
function createCapturedValueFromError(value, digest, stack) {
13674+
if (typeof stack === "string") {
13675+
CapturedStacks.set(value, stack);
13676+
}
13677+
1365813678
return {
1365913679
value: value,
1366013680
source: null,
@@ -16638,7 +16658,7 @@ if (__DEV__) {
1663816658
}
1663916659

1664016660
error.digest = digest;
16641-
capturedValue = createCapturedValue(error, digest, stack);
16661+
capturedValue = createCapturedValueFromError(error, digest, stack);
1664216662
}
1664316663

1664416664
return retrySuspenseComponentWithoutHydrating(
@@ -16763,7 +16783,7 @@ if (__DEV__) {
1676316783
pushPrimaryTreeSuspenseHandler(workInProgress);
1676416784
workInProgress.flags &= ~ForceClientRender;
1676516785

16766-
var _capturedValue = createCapturedValue(
16786+
var _capturedValue = createCapturedValueFromError(
1676716787
new Error(
1676816788
"There was an error while hydrating this Suspense boundary. " +
1676916789
"Switched to client rendering."

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ if (__DEV__) {
6666
return self;
6767
}
6868

69-
var ReactVersion = "18.3.0-www-modern-d27591d2";
69+
var ReactVersion = "18.3.0-www-modern-cdfb5e55";
7070

7171
var LegacyRoot = 0;
7272
var ConcurrentRoot = 1;
@@ -13368,17 +13368,37 @@ if (__DEV__) {
1336813368
return shouldUpdate;
1336913369
}
1337013370

13371+
var CapturedStacks = new WeakMap();
1337113372
function createCapturedValueAtFiber(value, source) {
1337213373
// If the value is an error, call this function immediately after it is thrown
1337313374
// so the stack is accurate.
13375+
var stack;
13376+
13377+
if (typeof value === "object" && value !== null) {
13378+
var capturedStack = CapturedStacks.get(value);
13379+
13380+
if (typeof capturedStack === "string") {
13381+
stack = capturedStack;
13382+
} else {
13383+
stack = getStackByFiberInDevAndProd(source);
13384+
CapturedStacks.set(value, stack);
13385+
}
13386+
} else {
13387+
stack = getStackByFiberInDevAndProd(source);
13388+
}
13389+
1337413390
return {
1337513391
value: value,
1337613392
source: source,
13377-
stack: getStackByFiberInDevAndProd(source),
13393+
stack: stack,
1337813394
digest: null
1337913395
};
1338013396
}
13381-
function createCapturedValue(value, digest, stack) {
13397+
function createCapturedValueFromError(value, digest, stack) {
13398+
if (typeof stack === "string") {
13399+
CapturedStacks.set(value, stack);
13400+
}
13401+
1338213402
return {
1338313403
value: value,
1338413404
source: null,
@@ -16332,7 +16352,7 @@ if (__DEV__) {
1633216352
}
1633316353

1633416354
error.digest = digest;
16335-
capturedValue = createCapturedValue(error, digest, stack);
16355+
capturedValue = createCapturedValueFromError(error, digest, stack);
1633616356
}
1633716357

1633816358
return retrySuspenseComponentWithoutHydrating(
@@ -16457,7 +16477,7 @@ if (__DEV__) {
1645716477
pushPrimaryTreeSuspenseHandler(workInProgress);
1645816478
workInProgress.flags &= ~ForceClientRender;
1645916479

16460-
var _capturedValue = createCapturedValue(
16480+
var _capturedValue = createCapturedValueFromError(
1646116481
new Error(
1646216482
"There was an error while hydrating this Suspense boundary. " +
1646316483
"Switched to client rendering."

compiled/facebook-www/ReactART-prod.classic.js

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,18 @@ function describeFiber(fiber) {
17481748
return "";
17491749
}
17501750
}
1751+
function getStackByFiberInDevAndProd(workInProgress) {
1752+
try {
1753+
var info = "";
1754+
do
1755+
(info += describeFiber(workInProgress)),
1756+
(workInProgress = workInProgress.return);
1757+
while (workInProgress);
1758+
return info;
1759+
} catch (x) {
1760+
return "\nError generating stack: " + x.message + "\n" + x.stack;
1761+
}
1762+
}
17511763
var SuspenseException = Error(formatProdErrorMessage(460)),
17521764
SuspenseyCommitException = Error(formatProdErrorMessage(474)),
17531765
noopSuspenseyCommitThenable = { then: function () {} };
@@ -3978,25 +3990,18 @@ function mountClassInstance(workInProgress, ctor, newProps, renderLanes) {
39783990
"function" === typeof instance.componentDidMount &&
39793991
(workInProgress.flags |= 4194308);
39803992
}
3993+
var CapturedStacks = new WeakMap();
39813994
function createCapturedValueAtFiber(value, source) {
3982-
try {
3983-
var info = "",
3984-
node = source;
3985-
do (info += describeFiber(node)), (node = node.return);
3986-
while (node);
3987-
var JSCompiler_inline_result = info;
3988-
} catch (x) {
3989-
JSCompiler_inline_result =
3990-
"\nError generating stack: " + x.message + "\n" + x.stack;
3991-
}
3992-
return {
3993-
value: value,
3994-
source: source,
3995-
stack: JSCompiler_inline_result,
3996-
digest: null
3997-
};
3998-
}
3999-
function createCapturedValue(value, digest, stack) {
3995+
if ("object" === typeof value && null !== value) {
3996+
var stack = CapturedStacks.get(value);
3997+
"string" !== typeof stack &&
3998+
((stack = getStackByFiberInDevAndProd(source)),
3999+
CapturedStacks.set(value, stack));
4000+
} else stack = getStackByFiberInDevAndProd(source);
4001+
return { value: value, source: source, stack: stack, digest: null };
4002+
}
4003+
function createCapturedValueFromError(value, digest, stack) {
4004+
"string" === typeof stack && CapturedStacks.set(value, stack);
40004005
return {
40014006
value: value,
40024007
source: null,
@@ -5178,7 +5183,7 @@ function updateDehydratedSuspenseComponent(
51785183
return (
51795184
pushPrimaryTreeSuspenseHandler(workInProgress),
51805185
(workInProgress.flags &= -257),
5181-
(didPrimaryChildrenDefer = createCapturedValue(
5186+
(didPrimaryChildrenDefer = createCapturedValueFromError(
51825187
Error(formatProdErrorMessage(422))
51835188
)),
51845189
retrySuspenseComponentWithoutHydrating(
@@ -5240,7 +5245,7 @@ function updateDehydratedSuspenseComponent(
52405245
(didPrimaryChildrenDefer = shim$2().digest),
52415246
(suspenseState = Error(formatProdErrorMessage(419))),
52425247
(suspenseState.digest = didPrimaryChildrenDefer),
5243-
(didPrimaryChildrenDefer = createCapturedValue(
5248+
(didPrimaryChildrenDefer = createCapturedValueFromError(
52445249
suspenseState,
52455250
didPrimaryChildrenDefer,
52465251
void 0
@@ -10582,19 +10587,19 @@ var slice = Array.prototype.slice,
1058210587
};
1058310588
return Text;
1058410589
})(React.Component),
10585-
devToolsConfig$jscomp$inline_1154 = {
10590+
devToolsConfig$jscomp$inline_1148 = {
1058610591
findFiberByHostInstance: function () {
1058710592
return null;
1058810593
},
1058910594
bundleType: 0,
10590-
version: "18.3.0-www-classic-84c34a04",
10595+
version: "18.3.0-www-classic-7ccd01b3",
1059110596
rendererPackageName: "react-art"
1059210597
};
10593-
var internals$jscomp$inline_1323 = {
10594-
bundleType: devToolsConfig$jscomp$inline_1154.bundleType,
10595-
version: devToolsConfig$jscomp$inline_1154.version,
10596-
rendererPackageName: devToolsConfig$jscomp$inline_1154.rendererPackageName,
10597-
rendererConfig: devToolsConfig$jscomp$inline_1154.rendererConfig,
10598+
var internals$jscomp$inline_1317 = {
10599+
bundleType: devToolsConfig$jscomp$inline_1148.bundleType,
10600+
version: devToolsConfig$jscomp$inline_1148.version,
10601+
rendererPackageName: devToolsConfig$jscomp$inline_1148.rendererPackageName,
10602+
rendererConfig: devToolsConfig$jscomp$inline_1148.rendererConfig,
1059810603
overrideHookState: null,
1059910604
overrideHookStateDeletePath: null,
1060010605
overrideHookStateRenamePath: null,
@@ -10611,26 +10616,26 @@ var internals$jscomp$inline_1323 = {
1061110616
return null === fiber ? null : fiber.stateNode;
1061210617
},
1061310618
findFiberByHostInstance:
10614-
devToolsConfig$jscomp$inline_1154.findFiberByHostInstance ||
10619+
devToolsConfig$jscomp$inline_1148.findFiberByHostInstance ||
1061510620
emptyFindFiberByHostInstance,
1061610621
findHostInstancesForRefresh: null,
1061710622
scheduleRefresh: null,
1061810623
scheduleRoot: null,
1061910624
setRefreshHandler: null,
1062010625
getCurrentFiber: null,
10621-
reconcilerVersion: "18.3.0-www-classic-84c34a04"
10626+
reconcilerVersion: "18.3.0-www-classic-7ccd01b3"
1062210627
};
1062310628
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
10624-
var hook$jscomp$inline_1324 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
10629+
var hook$jscomp$inline_1318 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
1062510630
if (
10626-
!hook$jscomp$inline_1324.isDisabled &&
10627-
hook$jscomp$inline_1324.supportsFiber
10631+
!hook$jscomp$inline_1318.isDisabled &&
10632+
hook$jscomp$inline_1318.supportsFiber
1062810633
)
1062910634
try {
10630-
(rendererID = hook$jscomp$inline_1324.inject(
10631-
internals$jscomp$inline_1323
10635+
(rendererID = hook$jscomp$inline_1318.inject(
10636+
internals$jscomp$inline_1317
1063210637
)),
10633-
(injectedHook = hook$jscomp$inline_1324);
10638+
(injectedHook = hook$jscomp$inline_1318);
1063410639
} catch (err) {}
1063510640
}
1063610641
var Path = Mode$1.Path;

0 commit comments

Comments
 (0)