Skip to content

Commit

Permalink
src: make more error handling improvements
Browse files Browse the repository at this point in the history
PR-URL: #57262
Reviewed-By: Daeyeon Jeong <[email protected]>
Reviewed-By: Yagiz Nizipli <[email protected]>
  • Loading branch information
jasnell committed Mar 4, 2025
1 parent 2cff256 commit 52ac448
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 85 deletions.
84 changes: 49 additions & 35 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ using v8::HeapProfiler;
using v8::HeapSpaceStatistics;
using v8::Integer;
using v8::Isolate;
using v8::JustVoid;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Nothing;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
Expand Down Expand Up @@ -1590,54 +1592,66 @@ Local<Value> Environment::GetNow() {
return Number::New(isolate(), static_cast<double>(now));
}

void CollectExceptionInfo(Environment* env,
Local<Object> obj,
int errorno,
const char* err_string,
const char* syscall,
const char* message,
const char* path,
const char* dest) {
obj->Set(env->context(),
env->errno_string(),
Integer::New(env->isolate(), errorno)).Check();

obj->Set(env->context(), env->code_string(),
OneByteString(env->isolate(), err_string)).Check();

if (message != nullptr) {
obj->Set(env->context(), env->message_string(),
OneByteString(env->isolate(), message)).Check();
Maybe<void> CollectExceptionInfo(Environment* env,
Local<Object> obj,
int errorno,
const char* err_string,
const char* syscall,
const char* message,
const char* path,
const char* dest) {
if (obj->Set(env->context(),
env->errno_string(),
Integer::New(env->isolate(), errorno))
.IsNothing() ||
obj->Set(env->context(),
env->code_string(),
OneByteString(env->isolate(), err_string))
.IsNothing() ||
(message != nullptr && obj->Set(env->context(),
env->message_string(),
OneByteString(env->isolate(), message))
.IsNothing())) {
return Nothing<void>();
}

Local<Value> path_buffer;
if (path != nullptr) {
path_buffer =
Buffer::Copy(env->isolate(), path, strlen(path)).ToLocalChecked();
obj->Set(env->context(), env->path_string(), path_buffer).Check();
if (!Buffer::Copy(env->isolate(), path, strlen(path))
.ToLocal(&path_buffer) ||
obj->Set(env->context(), env->path_string(), path_buffer).IsNothing()) {
return Nothing<void>();
}
}

Local<Value> dest_buffer;
if (dest != nullptr) {
dest_buffer =
Buffer::Copy(env->isolate(), dest, strlen(dest)).ToLocalChecked();
obj->Set(env->context(), env->dest_string(), dest_buffer).Check();
if (!Buffer::Copy(env->isolate(), dest, strlen(dest))
.ToLocal(&dest_buffer) ||
obj->Set(env->context(), env->dest_string(), dest_buffer).IsNothing()) {
return Nothing<void>();
}
}

if (syscall != nullptr) {
obj->Set(env->context(), env->syscall_string(),
OneByteString(env->isolate(), syscall)).Check();
if (obj->Set(env->context(),
env->syscall_string(),
OneByteString(env->isolate(), syscall))
.IsNothing()) {
return Nothing<void>();
}
}

return JustVoid();
}

void Environment::CollectUVExceptionInfo(Local<Value> object,
int errorno,
const char* syscall,
const char* message,
const char* path,
const char* dest) {
if (!object->IsObject() || errorno == 0)
return;
Maybe<void> Environment::CollectUVExceptionInfo(Local<Value> object,
int errorno,
const char* syscall,
const char* message,
const char* path,
const char* dest) {
if (!object->IsObject() || errorno == 0) return JustVoid();

Local<Object> obj = object.As<Object>();
const char* err_string = uv_err_name(errorno);
Expand All @@ -1646,7 +1660,7 @@ void Environment::CollectUVExceptionInfo(Local<Value> object,
message = uv_strerror(errorno);
}

CollectExceptionInfo(
return CollectExceptionInfo(
this, obj, errorno, err_string, syscall, message, path, dest);
}

Expand Down
12 changes: 6 additions & 6 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -767,12 +767,12 @@ class Environment final : public MemoryRetainer {

inline performance::PerformanceState* performance_state();

void CollectUVExceptionInfo(v8::Local<v8::Value> context,
int errorno,
const char* syscall = nullptr,
const char* message = nullptr,
const char* path = nullptr,
const char* dest = nullptr);
v8::Maybe<void> CollectUVExceptionInfo(v8::Local<v8::Value> context,
int errorno,
const char* syscall = nullptr,
const char* message = nullptr,
const char* path = nullptr,
const char* dest = nullptr);

// If this flag is set, calls into JS (if they would be observable
// from userland) must be avoided. This flag does not indicate whether
Expand Down
14 changes: 8 additions & 6 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -685,8 +685,9 @@ void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {

Local<Object> module = Object::New(env->isolate());
Local<Object> exports = Object::New(env->isolate());
Local<String> exports_prop = env->exports_string();
module->Set(env->context(), exports_prop, exports).Check();
if (module->Set(env->context(), env->exports_string(), exports).IsNothing()) {
return;
}

if (mod->nm_context_register_func != nullptr) {
mod->nm_context_register_func(
Expand All @@ -698,10 +699,11 @@ void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {
env, "Linked binding has no declared entry point.");
}

auto effective_exports =
module->Get(env->context(), exports_prop).ToLocalChecked();

args.GetReturnValue().Set(effective_exports);
Local<Value> effective_exports;
if (module->Get(env->context(), env->exports_string())
.ToLocal(&effective_exports)) {
args.GetReturnValue().Set(effective_exports);
}
}

// Call built-in bindings' _register_<module name> function to
Expand Down
9 changes: 6 additions & 3 deletions src/node_errors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1215,9 +1215,12 @@ void TriggerUncaughtException(Isolate* isolate,
// monkey-patchable.
Local<Object> process_object = env->process_object();
Local<String> fatal_exception_string = env->fatal_exception_string();
Local<Value> fatal_exception_function =
process_object->Get(env->context(),
fatal_exception_string).ToLocalChecked();
Local<Value> fatal_exception_function;
if (!process_object->Get(env->context(), fatal_exception_string)
.ToLocal(&fatal_exception_function)) {
// V8 will have scheduled a superseding error to throw
return;
}
// If the exception happens before process._fatalException is attached
// during bootstrap, or if the user has patched it incorrectly, exit
// the current Node.js instance.
Expand Down
42 changes: 23 additions & 19 deletions src/node_os.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ static void GetHostname(const FunctionCallbackInfo<Value>& args) {

if (r != 0) {
CHECK_GE(args.Length(), 1);
env->CollectUVExceptionInfo(args[args.Length() - 1], r,
"uv_os_gethostname");
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(
args[args.Length() - 1], r, "uv_os_gethostname"));
return;
}

Local<Value> ret;
Expand All @@ -85,8 +85,9 @@ static void GetOSInformation(const FunctionCallbackInfo<Value>& args) {

if (err != 0) {
CHECK_GE(args.Length(), 1);
env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_uname");
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(
args[args.Length() - 1], err, "uv_os_uname"));
return;
}

// [sysname, version, release, machine]
Expand Down Expand Up @@ -159,8 +160,8 @@ static void GetUptime(const FunctionCallbackInfo<Value>& args) {
double uptime;
int err = uv_uptime(&uptime);
if (err != 0) {
env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_uptime");
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_uptime"));
return;
}

args.GetReturnValue().Set(uptime);
Expand Down Expand Up @@ -189,14 +190,13 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {

int err = uv_interface_addresses(&interfaces, &count);

if (err == UV_ENOSYS)
return args.GetReturnValue().SetUndefined();
if (err == UV_ENOSYS) return;

if (err) {
CHECK_GE(args.Length(), 1);
env->CollectUVExceptionInfo(args[args.Length() - 1], errno,
"uv_interface_addresses");
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(
args[args.Length() - 1], errno, "uv_interface_addresses"));
return;
}

Local<Value> no_scope_id = Integer::New(isolate, -1);
Expand Down Expand Up @@ -267,8 +267,9 @@ static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {

if (err) {
CHECK_GE(args.Length(), 1);
env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir");
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(
args[args.Length() - 1], err, "uv_os_homedir"));
return;
}

Local<String> home;
Expand Down Expand Up @@ -299,9 +300,9 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {

if (const int err = uv_os_get_passwd(&pwd)) {
CHECK_GE(args.Length(), 2);
env->CollectUVExceptionInfo(args[args.Length() - 1], err,
"uv_os_get_passwd");
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(
args[args.Length() - 1], err, "uv_os_get_passwd"));
return;
}

auto free_passwd = OnScopeLeave([&] { uv_os_free_passwd(&pwd); });
Expand Down Expand Up @@ -371,7 +372,10 @@ static void SetPriority(const FunctionCallbackInfo<Value>& args) {

if (err) {
CHECK(args[2]->IsObject());
env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority");
if (env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority")
.IsNothing()) {
return;
}
}

args.GetReturnValue().Set(err);
Expand All @@ -390,7 +394,7 @@ static void GetPriority(const FunctionCallbackInfo<Value>& args) {

if (err) {
CHECK(args[1]->IsObject());
env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority");
USE(env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority"));
return;
}

Expand Down
44 changes: 34 additions & 10 deletions src/node_wasi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,37 @@ void WASI::New(const FunctionCallbackInfo<Value>& args) {

Local<Array> stdio = args[3].As<Array>();
CHECK_EQ(stdio->Length(), 3);
options.in = stdio->Get(context, 0).ToLocalChecked()->
Int32Value(context).FromJust();
options.out = stdio->Get(context, 1).ToLocalChecked()->
Int32Value(context).FromJust();
options.err = stdio->Get(context, 2).ToLocalChecked()->
Int32Value(context).FromJust();

Local<Value> val;
int32_t tmp;
if (!stdio->Get(context, 0).ToLocal(&val) ||
!val->Int32Value(context).To(&tmp)) {
return;
}
options.in = tmp;

if (!stdio->Get(context, 1).ToLocal(&val) ||
!val->Int32Value(context).To(&tmp)) {
return;
}
options.out = tmp;

if (!stdio->Get(context, 2).ToLocal(&val) ||
!val->Int32Value(context).To(&tmp)) {
return;
}
options.err = tmp;

options.fd_table_size = 3;
options.argc = argc;
options.argv =
const_cast<const char**>(argc == 0 ? nullptr : new char*[argc]);

for (uint32_t i = 0; i < argc; i++) {
auto arg = argv->Get(context, i).ToLocalChecked();
Local<Value> arg;
if (!argv->Get(context, i).ToLocal(&arg)) {
return;
}
CHECK(arg->IsString());
node::Utf8Value str(env->isolate(), arg);
options.argv[i] = strdup(*str);
Expand All @@ -158,7 +175,10 @@ void WASI::New(const FunctionCallbackInfo<Value>& args) {
const uint32_t envc = env_pairs->Length();
options.envp = const_cast<const char**>(new char*[envc + 1]);
for (uint32_t i = 0; i < envc; i++) {
auto pair = env_pairs->Get(context, i).ToLocalChecked();
Local<Value> pair;
if (!env_pairs->Get(context, i).ToLocal(&pair)) {
return;
}
CHECK(pair->IsString());
node::Utf8Value str(env->isolate(), pair);
options.envp[i] = strdup(*str);
Expand All @@ -172,8 +192,12 @@ void WASI::New(const FunctionCallbackInfo<Value>& args) {
options.preopens = Calloc<uvwasi_preopen_t>(options.preopenc);
int index = 0;
for (uint32_t i = 0; i < preopens->Length(); i += 2) {
auto mapped = preopens->Get(context, i).ToLocalChecked();
auto real = preopens->Get(context, i + 1).ToLocalChecked();
Local<Value> mapped;
Local<Value> real;
if (!preopens->Get(context, i).ToLocal(&mapped) ||
!preopens->Get(context, i + 1).ToLocal(&real)) {
return;
}
CHECK(mapped->IsString());
CHECK(real->IsString());
node::Utf8Value mapped_path(env->isolate(), mapped);
Expand Down
3 changes: 1 addition & 2 deletions src/tty_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ void TTYWrap::New(const FunctionCallbackInfo<Value>& args) {
int err = 0;
new TTYWrap(env, args.This(), fd, &err);
if (err != 0) {
env->CollectUVExceptionInfo(args[1], err, "uv_tty_init");
args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(args[1], err, "uv_tty_init"));
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/udp_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,8 @@ void UDPWrap::BufferSize(const FunctionCallbackInfo<Value>& args) {
"uv_send_buffer_size";

if (!args[0]->IsInt32()) {
env->CollectUVExceptionInfo(args[2], UV_EINVAL, uv_func_name);
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(args[2], UV_EINVAL, uv_func_name));
return;
}

uv_handle_t* handle = reinterpret_cast<uv_handle_t*>(&wrap->handle_);
Expand All @@ -398,8 +398,8 @@ void UDPWrap::BufferSize(const FunctionCallbackInfo<Value>& args) {
err = uv_send_buffer_size(handle, &size);

if (err != 0) {
env->CollectUVExceptionInfo(args[2], err, uv_func_name);
return args.GetReturnValue().SetUndefined();
USE(env->CollectUVExceptionInfo(args[2], err, uv_func_name));
return;
}

args.GetReturnValue().Set(size);
Expand Down

0 comments on commit 52ac448

Please sign in to comment.