Skip to content

Commit

Permalink
stash the component stack on the thrown value and reuse (#25790)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
gnoff committed Feb 16, 2024
1 parent b19c9fe commit 651e755
Show file tree
Hide file tree
Showing 19 changed files with 647 additions and 447 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fea900e45447214ddd6ef69076ab7e38433b5ffd
a9cc32511a12c261ee719e5383818182800d6af4
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -618,4 +618,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-www-classic-22e81a9f";
exports.version = "18.3.0-www-classic-707cd731";
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-profiling.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-www-classic-49d2df34";
exports.version = "18.3.0-www-classic-7063c424";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
30 changes: 25 additions & 5 deletions compiled/facebook-www/ReactART-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ if (__DEV__) {
return self;
}

var ReactVersion = "18.3.0-www-classic-ed34a3bc";
var ReactVersion = "18.3.0-www-classic-fc548c7d";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -13644,17 +13644,37 @@ if (__DEV__) {
return shouldUpdate;
}

var CapturedStacks = new WeakMap();
function createCapturedValueAtFiber(value, source) {
// If the value is an error, call this function immediately after it is thrown
// so the stack is accurate.
var stack;

if (typeof value === "object" && value !== null) {
var capturedStack = CapturedStacks.get(value);

if (typeof capturedStack === "string") {
stack = capturedStack;
} else {
stack = getStackByFiberInDevAndProd(source);
CapturedStacks.set(value, stack);
}
} else {
stack = getStackByFiberInDevAndProd(source);
}

return {
value: value,
source: source,
stack: getStackByFiberInDevAndProd(source),
stack: stack,
digest: null
};
}
function createCapturedValue(value, digest, stack) {
function createCapturedValueFromError(value, digest, stack) {
if (typeof stack === "string") {
CapturedStacks.set(value, stack);
}

return {
value: value,
source: null,
Expand Down Expand Up @@ -16638,7 +16658,7 @@ if (__DEV__) {
}

error.digest = digest;
capturedValue = createCapturedValue(error, digest, stack);
capturedValue = createCapturedValueFromError(error, digest, stack);
}

return retrySuspenseComponentWithoutHydrating(
Expand Down Expand Up @@ -16763,7 +16783,7 @@ if (__DEV__) {
pushPrimaryTreeSuspenseHandler(workInProgress);
workInProgress.flags &= ~ForceClientRender;

var _capturedValue = createCapturedValue(
var _capturedValue = createCapturedValueFromError(
new Error(
"There was an error while hydrating this Suspense boundary. " +
"Switched to client rendering."
Expand Down
30 changes: 25 additions & 5 deletions compiled/facebook-www/ReactART-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ if (__DEV__) {
return self;
}

var ReactVersion = "18.3.0-www-modern-d27591d2";
var ReactVersion = "18.3.0-www-modern-cdfb5e55";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -13368,17 +13368,37 @@ if (__DEV__) {
return shouldUpdate;
}

var CapturedStacks = new WeakMap();
function createCapturedValueAtFiber(value, source) {
// If the value is an error, call this function immediately after it is thrown
// so the stack is accurate.
var stack;

if (typeof value === "object" && value !== null) {
var capturedStack = CapturedStacks.get(value);

if (typeof capturedStack === "string") {
stack = capturedStack;
} else {
stack = getStackByFiberInDevAndProd(source);
CapturedStacks.set(value, stack);
}
} else {
stack = getStackByFiberInDevAndProd(source);
}

return {
value: value,
source: source,
stack: getStackByFiberInDevAndProd(source),
stack: stack,
digest: null
};
}
function createCapturedValue(value, digest, stack) {
function createCapturedValueFromError(value, digest, stack) {
if (typeof stack === "string") {
CapturedStacks.set(value, stack);
}

return {
value: value,
source: null,
Expand Down Expand Up @@ -16332,7 +16352,7 @@ if (__DEV__) {
}

error.digest = digest;
capturedValue = createCapturedValue(error, digest, stack);
capturedValue = createCapturedValueFromError(error, digest, stack);
}

return retrySuspenseComponentWithoutHydrating(
Expand Down Expand Up @@ -16457,7 +16477,7 @@ if (__DEV__) {
pushPrimaryTreeSuspenseHandler(workInProgress);
workInProgress.flags &= ~ForceClientRender;

var _capturedValue = createCapturedValue(
var _capturedValue = createCapturedValueFromError(
new Error(
"There was an error while hydrating this Suspense boundary. " +
"Switched to client rendering."
Expand Down
75 changes: 40 additions & 35 deletions compiled/facebook-www/ReactART-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -1748,6 +1748,18 @@ function describeFiber(fiber) {
return "";
}
}
function getStackByFiberInDevAndProd(workInProgress) {
try {
var info = "";
do
(info += describeFiber(workInProgress)),
(workInProgress = workInProgress.return);
while (workInProgress);
return info;
} catch (x) {
return "\nError generating stack: " + x.message + "\n" + x.stack;
}
}
var SuspenseException = Error(formatProdErrorMessage(460)),
SuspenseyCommitException = Error(formatProdErrorMessage(474)),
noopSuspenseyCommitThenable = { then: function () {} };
Expand Down Expand Up @@ -3978,25 +3990,18 @@ function mountClassInstance(workInProgress, ctor, newProps, renderLanes) {
"function" === typeof instance.componentDidMount &&
(workInProgress.flags |= 4194308);
}
var CapturedStacks = new WeakMap();
function createCapturedValueAtFiber(value, source) {
try {
var info = "",
node = source;
do (info += describeFiber(node)), (node = node.return);
while (node);
var JSCompiler_inline_result = info;
} catch (x) {
JSCompiler_inline_result =
"\nError generating stack: " + x.message + "\n" + x.stack;
}
return {
value: value,
source: source,
stack: JSCompiler_inline_result,
digest: null
};
}
function createCapturedValue(value, digest, stack) {
if ("object" === typeof value && null !== value) {
var stack = CapturedStacks.get(value);
"string" !== typeof stack &&
((stack = getStackByFiberInDevAndProd(source)),
CapturedStacks.set(value, stack));
} else stack = getStackByFiberInDevAndProd(source);
return { value: value, source: source, stack: stack, digest: null };
}
function createCapturedValueFromError(value, digest, stack) {
"string" === typeof stack && CapturedStacks.set(value, stack);
return {
value: value,
source: null,
Expand Down Expand Up @@ -5178,7 +5183,7 @@ function updateDehydratedSuspenseComponent(
return (
pushPrimaryTreeSuspenseHandler(workInProgress),
(workInProgress.flags &= -257),
(didPrimaryChildrenDefer = createCapturedValue(
(didPrimaryChildrenDefer = createCapturedValueFromError(
Error(formatProdErrorMessage(422))
)),
retrySuspenseComponentWithoutHydrating(
Expand Down Expand Up @@ -5240,7 +5245,7 @@ function updateDehydratedSuspenseComponent(
(didPrimaryChildrenDefer = shim$2().digest),
(suspenseState = Error(formatProdErrorMessage(419))),
(suspenseState.digest = didPrimaryChildrenDefer),
(didPrimaryChildrenDefer = createCapturedValue(
(didPrimaryChildrenDefer = createCapturedValueFromError(
suspenseState,
didPrimaryChildrenDefer,
void 0
Expand Down Expand Up @@ -10582,19 +10587,19 @@ var slice = Array.prototype.slice,
};
return Text;
})(React.Component),
devToolsConfig$jscomp$inline_1154 = {
devToolsConfig$jscomp$inline_1148 = {
findFiberByHostInstance: function () {
return null;
},
bundleType: 0,
version: "18.3.0-www-classic-84c34a04",
version: "18.3.0-www-classic-7ccd01b3",
rendererPackageName: "react-art"
};
var internals$jscomp$inline_1323 = {
bundleType: devToolsConfig$jscomp$inline_1154.bundleType,
version: devToolsConfig$jscomp$inline_1154.version,
rendererPackageName: devToolsConfig$jscomp$inline_1154.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1154.rendererConfig,
var internals$jscomp$inline_1317 = {
bundleType: devToolsConfig$jscomp$inline_1148.bundleType,
version: devToolsConfig$jscomp$inline_1148.version,
rendererPackageName: devToolsConfig$jscomp$inline_1148.rendererPackageName,
rendererConfig: devToolsConfig$jscomp$inline_1148.rendererConfig,
overrideHookState: null,
overrideHookStateDeletePath: null,
overrideHookStateRenamePath: null,
Expand All @@ -10611,26 +10616,26 @@ var internals$jscomp$inline_1323 = {
return null === fiber ? null : fiber.stateNode;
},
findFiberByHostInstance:
devToolsConfig$jscomp$inline_1154.findFiberByHostInstance ||
devToolsConfig$jscomp$inline_1148.findFiberByHostInstance ||
emptyFindFiberByHostInstance,
findHostInstancesForRefresh: null,
scheduleRefresh: null,
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-www-classic-84c34a04"
reconcilerVersion: "18.3.0-www-classic-7ccd01b3"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1324 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1318 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1324.isDisabled &&
hook$jscomp$inline_1324.supportsFiber
!hook$jscomp$inline_1318.isDisabled &&
hook$jscomp$inline_1318.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1324.inject(
internals$jscomp$inline_1323
(rendererID = hook$jscomp$inline_1318.inject(
internals$jscomp$inline_1317
)),
(injectedHook = hook$jscomp$inline_1324);
(injectedHook = hook$jscomp$inline_1318);
} catch (err) {}
}
var Path = Mode$1.Path;
Expand Down
Loading

0 comments on commit 651e755

Please sign in to comment.