diff --git a/napi-inl.h b/napi-inl.h index 22cbccdc7..0dcbb0be1 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -1452,10 +1452,10 @@ inline Error Error::New(napi_env env) { case napi_string_expected: case napi_boolean_expected: case napi_number_expected: - status = napi_create_type_error(env, message, &error); + status = napi_create_type_error(env, nullptr, message, &error); break; default: - status = napi_create_error(env, message, &error); + status = napi_create_error(env, nullptr, message, &error); break; } assert(status == napi_ok); @@ -1556,7 +1556,7 @@ inline TError Error::New(napi_env env, NAPI_THROW_IF_FAILED(env, status, TError()); napi_value error; - status = create_error(env, str, &error); + status = create_error(env, nullptr, str, &error); NAPI_THROW_IF_FAILED(env, status, TError()); return TError(env, error); @@ -2467,7 +2467,7 @@ inline napi_value ObjectWrap::ConstructorCallbackWrapper( if (status != napi_ok) return nullptr; if (!isConstructCall) { - napi_throw_type_error(env, "Class constructors cannot be invoked without 'new'"); + napi_throw_type_error(env, nullptr, "Class constructors cannot be invoked without 'new'"); return nullptr; } diff --git a/napi.h b/napi.h index 9862771e1..c835e5e0f 100644 --- a/napi.h +++ b/napi.h @@ -1085,7 +1085,7 @@ namespace Napi { protected: /// !cond INTERNAL - typedef napi_status (*create_error_fn)(napi_env envb, napi_value msg, napi_value* result); + typedef napi_status (*create_error_fn)(napi_env envb, napi_value code, napi_value msg, napi_value* result); template static TError New(napi_env env, diff --git a/src/node_api.cc b/src/node_api.cc index 81f0e3e54..6ae0d439c 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1523,7 +1523,61 @@ napi_status napi_create_symbol(napi_env env, return GET_RETURN_STATUS(env); } +static napi_status set_error_code(napi_env env, + v8::Local error, + napi_value code, + const char* code_cstring) { + if ((code != nullptr) || (code_cstring != nullptr)) { + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local err_object = error.As(); + + v8::Local code_value = v8impl::V8LocalValueFromJsValue(code); + if (code != nullptr) { + code_value = v8impl::V8LocalValueFromJsValue(code); + RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected); + } else { + CHECK_NEW_FROM_UTF8(env, code_value, code_cstring); + } + + v8::Local code_key; + CHECK_NEW_FROM_UTF8(env, code_key, "code"); + + v8::Maybe set_maybe = err_object->Set(context, code_key, code_value); + RETURN_STATUS_IF_FALSE(env, + set_maybe.FromMaybe(false), + napi_generic_failure); + + // now update the name to be "name [code]" where name is the + // original name and code is the code associated with the Error + v8::Local name_string; + CHECK_NEW_FROM_UTF8(env, name_string, ""); + v8::Local name_key; + CHECK_NEW_FROM_UTF8(env, name_key, "name"); + + auto maybe_name = err_object->Get(context, name_key); + if (!maybe_name.IsEmpty()) { + v8::Local name = maybe_name.ToLocalChecked(); + if (name->IsString()) { + name_string = v8::String::Concat(name_string, name.As()); + } + } + name_string = v8::String::Concat(name_string, + FIXED_ONE_BYTE_STRING(isolate, " [")); + name_string = v8::String::Concat(name_string, code_value.As()); + name_string = v8::String::Concat(name_string, + FIXED_ONE_BYTE_STRING(isolate, "]")); + + set_maybe = err_object->Set(context, name_key, name_string); + RETURN_STATUS_IF_FALSE(env, + set_maybe.FromMaybe(false), + napi_generic_failure); + } + return napi_ok; +} + napi_status napi_create_error(napi_env env, + napi_value code, napi_value msg, napi_value* result) { NAPI_PREAMBLE(env); @@ -1533,13 +1587,18 @@ napi_status napi_create_error(napi_env env, v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - *result = v8impl::JsValueFromV8LocalValue(v8::Exception::Error( - message_value.As())); + v8::Local error_obj = + v8::Exception::Error(message_value.As()); + napi_status status = set_error_code(env, error_obj, code, nullptr); + if (status != napi_ok) return status; + + *result = v8impl::JsValueFromV8LocalValue(error_obj); return GET_RETURN_STATUS(env); } napi_status napi_create_type_error(napi_env env, + napi_value code, napi_value msg, napi_value* result) { NAPI_PREAMBLE(env); @@ -1549,13 +1608,18 @@ napi_status napi_create_type_error(napi_env env, v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - *result = v8impl::JsValueFromV8LocalValue(v8::Exception::TypeError( - message_value.As())); + v8::Local error_obj = + v8::Exception::TypeError(message_value.As()); + napi_status status = set_error_code(env, error_obj, code, nullptr); + if (status != napi_ok) return status; + + *result = v8impl::JsValueFromV8LocalValue(error_obj); return GET_RETURN_STATUS(env); } napi_status napi_create_range_error(napi_env env, + napi_value code, napi_value msg, napi_value* result) { NAPI_PREAMBLE(env); @@ -1565,8 +1629,12 @@ napi_status napi_create_range_error(napi_env env, v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - *result = v8impl::JsValueFromV8LocalValue(v8::Exception::RangeError( - message_value.As())); + v8::Local error_obj = + v8::Exception::RangeError(message_value.As()); + napi_status status = set_error_code(env, error_obj, code, nullptr); + if (status != napi_ok) return status; + + *result = v8impl::JsValueFromV8LocalValue(error_obj); return GET_RETURN_STATUS(env); } @@ -1739,40 +1807,58 @@ napi_status napi_throw(napi_env env, napi_value error) { return napi_clear_last_error(env); } -napi_status napi_throw_error(napi_env env, const char* msg) { +napi_status napi_throw_error(napi_env env, + const char* code, + const char* msg) { NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); - isolate->ThrowException(v8::Exception::Error(str)); + v8::Local error_obj = v8::Exception::Error(str); + napi_status status = set_error_code(env, error_obj, nullptr, code); + if (status != napi_ok) return status; + + isolate->ThrowException(error_obj); // any VM calls after this point and before returning // to the javascript invoker will fail return napi_clear_last_error(env); } -napi_status napi_throw_type_error(napi_env env, const char* msg) { +napi_status napi_throw_type_error(napi_env env, + const char* code, + const char* msg) { NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); - isolate->ThrowException(v8::Exception::TypeError(str)); + v8::Local error_obj = v8::Exception::TypeError(str); + napi_status status = set_error_code(env, error_obj, nullptr, code); + if (status != napi_ok) return status; + + isolate->ThrowException(error_obj); // any VM calls after this point and before returning // to the javascript invoker will fail return napi_clear_last_error(env); } -napi_status napi_throw_range_error(napi_env env, const char* msg) { +napi_status napi_throw_range_error(napi_env env, + const char* code, + const char* msg) { NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); - isolate->ThrowException(v8::Exception::RangeError(str)); + v8::Local error_obj = v8::Exception::RangeError(str); + napi_status status = set_error_code(env, error_obj, nullptr, code); + if (status != napi_ok) return status; + + isolate->ThrowException(error_obj); // any VM calls after this point and before returning // to the javascript invoker will fail return napi_clear_last_error(env); @@ -2392,7 +2478,9 @@ napi_status napi_instanceof(napi_env env, CHECK_TO_OBJECT(env, context, ctor, constructor); if (!ctor->IsFunction()) { - napi_throw_type_error(env, "constructor must be a function"); + napi_throw_type_error(env, + "ERR_NAPI_CONS_FUNCTION", + "Constructor must be a function"); return napi_set_last_error(env, napi_function_expected); } @@ -2460,7 +2548,10 @@ napi_status napi_instanceof(napi_env env, v8::Local prototype_property = maybe_prototype.ToLocalChecked(); if (!prototype_property->IsObject()) { - napi_throw_type_error(env, "constructor.prototype must be an object"); + napi_throw_type_error( + env, + "ERR_NAPI_CONS_PROTOTYPE_OBJECT", + "Constructor.prototype must be an object"); return napi_set_last_error(env, napi_object_expected); } diff --git a/src/node_api.h b/src/node_api.h index e346b762d..af98789d4 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -142,12 +142,15 @@ NAPI_EXTERN napi_status napi_create_function(napi_env env, void* data, napi_value* result); NAPI_EXTERN napi_status napi_create_error(napi_env env, + napi_value code, napi_value msg, napi_value* result); NAPI_EXTERN napi_status napi_create_type_error(napi_env env, + napi_value code, napi_value msg, napi_value* result); NAPI_EXTERN napi_status napi_create_range_error(napi_env env, + napi_value code, napi_value msg, napi_value* result); @@ -404,9 +407,15 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env, // Methods to support error handling NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); -NAPI_EXTERN napi_status napi_throw_error(napi_env env, const char* msg); -NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg); -NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg); +NAPI_EXTERN napi_status napi_throw_error(napi_env env, + const char* code, + const char* msg); +NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, + const char* code, + const char* msg); +NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, + const char* code, + const char* msg); NAPI_EXTERN napi_status napi_is_error(napi_env env, napi_value value, bool* result);