Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src,lib: fix assert when Error.prepareStackTrace not overridden #49212

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

codebytere
Copy link
Member

@codebytere codebytere commented Aug 17, 2023

Refs #23926

Fixes an issue caused when embedders choose to set up the v8::Isolate without a prepareStackTrace callback. In this event, several assert methods stop working because stack trace formatting relies on a SafeWeakMap that's only populated in the overridden prepareStackTrace callback.

Fix this by exposing override status under getEmbedderOptions and falling back to the default implementation of Error.prepareStackTrace if it's not been overridden.

@codebytere codebytere added the embedding Issues and PRs related to embedding Node.js in another project. label Aug 17, 2023
@codebytere codebytere requested a review from devsnek August 17, 2023 09:23
@nodejs-github-bot nodejs-github-bot added assert Issues and PRs related to the assert subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Aug 17, 2023
@codebytere codebytere force-pushed the fix-handle-no-override-preparestacktrace branch from 40a5d30 to 661bc35 Compare August 17, 2023 10:34
@codebytere codebytere force-pushed the fix-handle-no-override-preparestacktrace branch from 661bc35 to 151654e Compare August 17, 2023 10:36
FIXED_ONE_BYTE_STRING(
env->isolate(), "hasPrepareStackTraceCallback"),
Boolean::New(isolate,
!env->prepare_stack_trace_callback().IsEmpty()))
Copy link
Member

@joyeecheung joyeecheung Aug 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is overridden when the realm is bootstrapped though, unless SetIsolateUpForNode() is somehow called after CreateEnvironment() this would not have worked?

setPrepareStackTraceCallback(prepareStackTrace);

I wonder if this flag should've been moved into EnvironmentFlags instead (and if that's too breaking, keep SHOULD_NOT_SET_PREPARE_STACK_TRACE_CALLBACK a dummy flag there), and the GetEmbedderOptions() could query that flag stored into the Environment instead. At bootstrap time, we could just skip setPrepareStackTraceCallback if that flag is true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joyeecheung ah, we do call CreateEnvironment() before SetIsolateUpForNode() - I can switch that around.

overrideStackTrace.set(err, (_, stack) => stack);
call = err.stack[0];
} else {
const tmpPrepare = Error.prepareStackTrace;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm actually I think both blocks are assuming env->prepare_stack_trace_callback() would be called here? Error.prepareStackTrace doesn't even do anything if env->prepare_stack_trace_callback() is not configured (it's a polyfill for that hook).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work: https://github.com/v8/v8/blob/main/src/execution/messages.cc#L325-L335 What we polyfill in nodejs is that Error.prepareStackTrace in the main realm will affect stack traces in any other realm.

Copy link
Member

@joyeecheung joyeecheung Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah didn't realize that V8 has not removed it yet..in that case I wonder if instead of using the WeakMap, we can just switch to storing a v8::Private::ForApi() to the error to indicate that the prepare stack trace callback should use the function stored with that symbol to override the stack trace? Then the embedder can also communicate with the Node.js internals using v8::Private::ForApi() without requiring access to the overrideStackTrace map - overrideStackTrace isn't only used here, even, and WeakMap semantics means using v8::Private::ForApi() would just be equivalent. That would also avoid the Error.prepareStackTrace configurability/writability problem.

call = err.stack[0];
} else {
const tmpPrepare = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => stack;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would throw if Error.prepareStackTrace is not writable, not sure if that's desirable.

@@ -23,8 +23,6 @@ rules:
message: Use an error exported by the internal/errors module.
- selector: CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']
message: Please use `require('internal/errors').hideStackFrames()` instead.
- selector: AssignmentExpression:matches([left.name='prepareStackTrace'], [left.property.name='prepareStackTrace'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of removing this, you can add // eslint-disable-next-line no-restricted-syntax

overrideStackTrace.set(err, (_, stack) => stack);
call = err.stack[0];
} else {
const tmpPrepare = Error.prepareStackTrace;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work: https://github.com/v8/v8/blob/main/src/execution/messages.cc#L325-L335 What we polyfill in nodejs is that Error.prepareStackTrace in the main realm will affect stack traces in any other realm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assert Issues and PRs related to the assert subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. embedding Issues and PRs related to embedding Node.js in another project. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants