Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
module: improve detection try catch
  • Loading branch information
GeoffreyBooth committed Mar 21, 2024
commit c7813dfab731dcf11b54386d9f79315e978a4e02
12 changes: 8 additions & 4 deletions lib/internal/modules/run_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
hasUncaughtExceptionCaptureCallback,
} = require('internal/process/execution');
const {
tryCatchRethrow,
triggerUncaughtException,
} = internalBinding('errors');
const {
Expand Down Expand Up @@ -167,11 +168,14 @@ function executeUserEntryPoint(main = process.argv[1]) {
const cjsLoader = require('internal/modules/cjs/loader');
const { Module } = cjsLoader;
if (getOptionValue('--experimental-detect-module')) {
try {
tryCatchRethrow(() => {
// Module._load is the monkey-patchable CJS module loader.
Module._load(main, null, true);
} catch (error) {
}, (error) => {
const source = cjsLoader.entryPointSource;
if (!source) {
return true; // Rethrow original exception.
}
const { shouldRetryAsESM } = require('internal/modules/helpers');
retryAsESM = shouldRetryAsESM(error.message, source);
// In case the entry point is a large file, such as a bundle,
Expand All @@ -180,9 +184,9 @@ function executeUserEntryPoint(main = process.argv[1]) {
if (!retryAsESM) {
const { enrichCJSError } = require('internal/modules/esm/translators');
enrichCJSError(error, source, resolvedMain);
throw error;
return true; // Rethrow original exception.
}
}
});
} else { // `--experimental-detect-module` is not passed
Module._load(main, null, true);
}
Expand Down
35 changes: 35 additions & 0 deletions src/node_errors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ using v8::String;
using v8::Undefined;
using v8::Value;

// Takes two JS callbacks, and calls the first one in a TryCatch scope.
// If the first one throws, call the second one with the exception.
// If the second one returns true, rethrow the original exception.
// This is to keep the location of the original throw in JS.
static void TryCatchRethrow(const FunctionCallbackInfo<Value>& args) {
CHECK(args.Length() == 2);
CHECK(args[0]->IsFunction());
CHECK(args[1]->IsFunction());

Local<Function> try_fn = args[0].As<Function>();
Local<Function> catch_fn = args[1].As<Function>();

Isolate* isolate = args.GetIsolate();
Environment* env = Environment::GetCurrent(isolate);
Local<Context> context = isolate->GetCurrentContext();

TryCatchScope try_catch_scope(env);
USE(try_fn->Call(context, Undefined(isolate), 0, nullptr));

if (try_catch_scope.HasCaught()) {
Local<Value> exception = try_catch_scope.Exception();
Local<Value> argv[] = {exception};
Local<Value> result;
if (!catch_fn->Call(context, Undefined(isolate), 1, argv)
.ToLocal(&result)) {
return;
}
if (result->IsTrue()) {
try_catch_scope.ReThrow();
}
}
}

bool IsExceptionDecorated(Environment* env, Local<Value> er) {
if (!er.IsEmpty() && er->IsObject()) {
Local<Object> err_obj = er.As<Object>();
Expand Down Expand Up @@ -1089,6 +1122,7 @@ static void TriggerUncaughtException(const FunctionCallbackInfo<Value>& args) {
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(TryCatchRethrow);
registry->Register(SetPrepareStackTraceCallback);
registry->Register(SetGetSourceMapErrorSource);
registry->Register(SetSourceMapsEnabled);
Expand All @@ -1102,6 +1136,7 @@ void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
SetMethod(context, target, "tryCatchRethrow", TryCatchRethrow);
SetMethod(context,
target,
"setPrepareStackTraceCallback",
Expand Down