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
fix: use Node.js isolate setup logic in bindings #24579
Conversation
c5518bc
to
cebd4b6
Compare
Couple of question,
AFAIK in the browser process we setup the callbacks in https://github.com/electron/electron/blob/master/patches/node/feat_add_flags_for_low-level_hooks_and_exceptions.patch as
|
@deepak1556 you're totally right - we can remove a lot of that low-level flag logic now, as it's just being redundantly set/called twice 😅 I've also moved the flags into process-specific locations. With regard to:
I removed the invocation in the patch, so it's now just disabled for the renderer process where Blink does it.
Yes - we can remove this now as the embedder interface handles it. |
I think we should avoid letting node control blinks isolate, can't we fix the |
tl;dr the comparable surface area of difference is small enough, and the net benefit great enough of using embedding functionality provided to us, that I think choosing to leverage this functionality is a better choice than to call the vast majority of it all ourselves in the exact same way Node.js would :) A more in-depth breakdown: The embedder interface this change leverages, isolate->AddMessageListenerWithErrorLevel( errors::PerIsolateMessageListenerg);
isolate->SetAbortOnUncaughtExceptionCallback(abort_callback);
isolate->SetFatalErrorHandler(fatal_error_cb);
isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb);
isolate->SetMicrotasksPolicy(s.policy);
isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); Renderer Isolate Setup Prior to Change isolate->SetMicrotasksPolicy(s.policy);
isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
isolate->AddMessageListenerWithErrorLevel(errors::PerIsolateMessageListeners); Renderer Isolate Setup After Change isolate->SetMicrotasksPolicy(s.policy);
isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
isolate->AddMessageListenerWithErrorLevel( errors::PerIsolateMessageListeners);
isolate->SetAbortOnUncaughtExceptionCallback(abort_callback);
isolate->SetFatalErrorHandler(fatal_error_cb); That leaves the only difference consequent to this PR in the Node.js-Blink isolate relationship as:
isolate->SetAbortOnUncaughtExceptionCallback(abort_callback);
isolate->SetFatalErrorHandler(fatal_error_cb);
We choose to call
void FatalErrorCallback(const char* location, const char* message) {
LOG(ERROR) << "Fatal error in V8: " << location << " " << message;
ElectronBindings::Crash();
}
Which compares with the one we’d now be invoking with Node.js:
void OnFatalError(const char* location, const char* message) {
if (location) {
FPrintF(stderr, "FATAL ERROR: %s %s\n", location, message);
} else {
FPrintF(stderr, "FATAL ERROR: %s\n", message);
}
Isolate* isolate = Isolate::GetCurrent();
Environment* env = Environment::GetCurrent(isolate);
bool report_on_fatalerror;
{
Mutex::ScopedLock lock(node::per_process::cli_options_mutex);
report_on_fatalerror = per_process::cli_options->report_on_fatalerror;
}
if (report_on_fatalerror) {
report::TriggerNodeReport(
isolate, env, message, "FatalError", "", Local<Object>());
}
fflush(stderr);
ABORT();
} Stripping out cli functionality we already prevent in the renderer process: void OnFatalError(const char* location, const char* message) {
if (location) {
FPrintF(stderr, "FATAL ERROR: %s %s\n", location, message);
} else {
FPrintF(stderr, "FATAL ERROR: %s\n", message);
}
fflush(stderr);
ABORT();
} This introduces no side effects and is a comparable output, I would say. Lastly, we’re left with: Blink currently never calls this. This callback is used explicitly by embedders to help V8 determine if it should abort when it throws and no internal handler is predicted to catch the exception. Node.js currently chooses to do this if: The only other case resulting in an abort in Node.js’ callback is when the Environment itself is stopping. As such, I personally feel comfortable allowing this setup functionality to run, but I would definitely be open to perhaps upstreaming a toggle to prevent calling |
oops accidental approval, the fatal error handler set in bindings has not been removed.
09182a1
to
d2bf92b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving given the wonderful explanation, but I would like to err on the side of not backporting this as I'm still quite fearful 🤔
Release Notes Persisted
|
* fix: use Node.js isolate setup logic in bindings * Flags should be more process-specific * Remove redundant isolate function setting * Remove old SetFatalErrorHandler call
* fix: use Node.js isolate setup logic in bindings * Flags should be more process-specific * Remove redundant isolate function setting * Remove old SetFatalErrorHandler call
Description of Change
Closes #24577.
This fixes an issue whereby any code in Node.js that invokes various assert functionality in the browser or renderer processes will throw with
TypeError: call.getFileName is not a function
. The reason this happens is that Node.js overrides the Promise rejection callback used by the Isolate, so theWeakMap
used in their assert logic is never populated.To remedy this, the best long-term solution, I feel, is to better align our isolate setup functionality with that available to us via the embedder-focused
SetIsolateUpForNode
. We already approximated a lot of this functionality, so this allows us to remove that. It does, however create a small new issue wherein we do not want to use the promise rejection callback that Node.js uses in the renderer process, because it does not send PromiseRejectionEvents to the global script context. This causes code like:to fail since the
'unhandledrejection'
rejection event would never be fired. We need thus to use the one Blink already provides, and to augmentIsolateSettings
accordingly.I've upstreamed this at nodejs/node#34387.
cc @nornagon @zcbenz @MarshallOfSound
Checklist
npm test
passesRelease Notes
Notes: Fixed an issue where many uses of the Node.js
assert
module would throw in both the browser and renderer processes.