Skip to content

Commit 28ea89f

Browse files
committed
n-api: add code parameter to error helpers
In support of the effort to add error codes to all errors generated by Node.js, add an optional code parameter to the helper functions used to throw/create errors in N-API. Fixes: #13933
1 parent 598a128 commit 28ea89f

File tree

9 files changed

+303
-32
lines changed

9 files changed

+303
-32
lines changed

doc/api/n-api.md

+46-3
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,31 @@ code needs to create an Error object: [`napi_create_error`][],
323323
where result is the napi_value that refers to the newly created
324324
JavaScript Error object.
325325
326+
The Node.js project is adding error codes to all of the errors
327+
generated internally. The goal is for applications to use these
328+
error codes for all error checking. The associated error messages
329+
will remain, but will only be meant to be used for logging and
330+
display with the expectation that the message can change without
331+
SemVer applying. In order to support this model with N-API, both
332+
in internal functionality and for module specific functionality
333+
(as its good practice), the `throw_` and `create_` functions
334+
take an optional code parameter which is the string for the code
335+
to be added to the error object. If the optional parameter is NULL
336+
then no code will be associated with the error. If a code is provided,
337+
the name associated with the error is also updated to be:
338+
339+
```
340+
originalName [code]
341+
```
342+
343+
where originalName is the original name associated with the error
344+
and code is the code that was provided. For example if the code
345+
is 'ERR_ERROR_1' and a TypeError is being created the name will be:
346+
347+
```
348+
TypeError [ERR_ERROR_1]
349+
```
350+
326351
#### napi_throw
327352
<!-- YAML
328353
added: v8.0.0
@@ -343,9 +368,12 @@ This API throws the JavaScript Error provided.
343368
added: v8.0.0
344369
-->
345370
```C
346-
NODE_EXTERN napi_status napi_throw_error(napi_env env, const char* msg);
371+
NODE_EXTERN napi_status napi_throw_error(napi_env env,
372+
const char* code,
373+
const char* msg);
347374
```
348375
- `[in] env`: The environment that the API is invoked under.
376+
- `[in] code`: Optional error code to be set on the error.
349377
- `[in] msg`: C string representing the text to be associated with
350378
the error.
351379
@@ -358,9 +386,12 @@ This API throws a JavaScript Error with the text provided.
358386
added: v8.0.0
359387
-->
360388
```C
361-
NODE_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg);
389+
NODE_EXTERN napi_status napi_throw_type_error(napi_env env,
390+
const char* code,
391+
const char* msg);
362392
```
363393
- `[in] env`: The environment that the API is invoked under.
394+
- `[in] code`: Optional error code to be set on the error.
364395
- `[in] msg`: C string representing the text to be associated with
365396
the error.
366397

@@ -373,9 +404,12 @@ This API throws a JavaScript TypeError with the text provided.
373404
added: v8.0.0
374405
-->
375406
```C
376-
NODE_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg);
407+
NODE_EXTERN napi_status napi_throw_range_error(napi_env env,
408+
const char* code,
409+
const char* msg);
377410
```
378411
- `[in] env`: The environment that the API is invoked under.
412+
- `[in] code`: Optional error code to be set on the error.
379413
- `[in] msg`: C string representing the text to be associated with
380414
the error.
381415
@@ -409,10 +443,13 @@ added: v8.0.0
409443
-->
410444
```C
411445
NODE_EXTERN napi_status napi_create_error(napi_env env,
446+
napi_value code,
412447
napi_value msg,
413448
napi_value* result);
414449
```
415450
- `[in] env`: The environment that the API is invoked under.
451+
- `[in] code`: Optional `napi_value` with the string for the error code to
452+
be associated with the error.
416453
- `[in] msg`: napi_value that references a JavaScript String to be
417454
used as the message for the Error.
418455
- `[out] result`: `napi_value` representing the error created.
@@ -427,10 +464,13 @@ added: v8.0.0
427464
-->
428465
```C
429466
NODE_EXTERN napi_status napi_create_type_error(napi_env env,
467+
napi_value code,
430468
napi_value msg,
431469
napi_value* result);
432470
```
433471
- `[in] env`: The environment that the API is invoked under.
472+
- `[in] code`: Optional `napi_value` with the string for the error code to
473+
be associated with the error.
434474
- `[in] msg`: napi_value that references a JavaScript String to be
435475
used as the message for the Error.
436476
- `[out] result`: `napi_value` representing the error created.
@@ -446,10 +486,13 @@ added: v8.0.0
446486
-->
447487
```C
448488
NODE_EXTERN napi_status napi_create_range_error(napi_env env,
489+
napi_value code,
449490
const char* msg,
450491
napi_value* result);
451492
```
452493
- `[in] env`: The environment that the API is invoked under.
494+
- `[in] code`: Optional `napi_value` with the string for the error code to
495+
be associated with the error.
453496
- `[in] msg`: napi_value that references a JavaScript String to be
454497
used as the message for the Error.
455498
- `[out] result`: `napi_value` representing the error created.

lib/internal/errors.js

+2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe');
161161
E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks');
162162
E('ERR_MISSING_ARGS', missingArgs);
163163
E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times');
164+
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function');
165+
E('ERR_NAPI_CONS_PROTOTYPE_OBJECT', 'Constructor.prototype must be an object');
164166
E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support');
165167
E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s');
166168
E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound');

src/node_api.cc

+106-14
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "uv.h"
1919
#include "node_api.h"
2020
#include "node_internals.h"
21+
#include "util.h"
2122

2223
#define NAPI_VERSION 1
2324

@@ -1522,7 +1523,61 @@ napi_status napi_create_symbol(napi_env env,
15221523
return GET_RETURN_STATUS(env);
15231524
}
15241525

1526+
static napi_status set_error_code(napi_env env,
1527+
v8::Local<v8::Value> error,
1528+
napi_value code,
1529+
const char* code_cstring) {
1530+
if ((code != nullptr) || (code_cstring != nullptr)) {
1531+
v8::Isolate* isolate = env->isolate;
1532+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
1533+
v8::Local<v8::Object> err_object = error.As<v8::Object>();
1534+
1535+
v8::Local<v8::Value> code_value = v8impl::V8LocalValueFromJsValue(code);
1536+
if (code != nullptr) {
1537+
code_value = v8impl::V8LocalValueFromJsValue(code);
1538+
RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected);
1539+
} else {
1540+
CHECK_NEW_FROM_UTF8(env, code_value, code_cstring);
1541+
}
1542+
1543+
v8::Local<v8::Name> code_key;
1544+
CHECK_NEW_FROM_UTF8(env, code_key, "code");
1545+
1546+
v8::Maybe<bool> set_maybe = err_object->Set(context, code_key, code_value);
1547+
RETURN_STATUS_IF_FALSE(env,
1548+
set_maybe.FromMaybe(false),
1549+
napi_generic_failure);
1550+
1551+
// now update the name to be "name [code]" where name is the
1552+
// original name and code is the code associated with the Error
1553+
v8::Local<v8::String> name_string;
1554+
CHECK_NEW_FROM_UTF8(env, name_string, "");
1555+
v8::Local<v8::Name> name_key;
1556+
CHECK_NEW_FROM_UTF8(env, name_key, "name");
1557+
1558+
auto maybe_name = err_object->Get(context, name_key);
1559+
if (!maybe_name.IsEmpty()) {
1560+
v8::Local<v8::Value> name = maybe_name.ToLocalChecked();
1561+
if (name->IsString()) {
1562+
name_string = v8::String::Concat(name_string, name.As<v8::String>());
1563+
}
1564+
}
1565+
name_string = v8::String::Concat(name_string,
1566+
FIXED_ONE_BYTE_STRING(isolate, " ["));
1567+
name_string = v8::String::Concat(name_string, code_value.As<v8::String>());
1568+
name_string = v8::String::Concat(name_string,
1569+
FIXED_ONE_BYTE_STRING(isolate, "]"));
1570+
1571+
set_maybe = err_object->Set(context, name_key, name_string);
1572+
RETURN_STATUS_IF_FALSE(env,
1573+
set_maybe.FromMaybe(false),
1574+
napi_generic_failure);
1575+
}
1576+
return napi_ok;
1577+
}
1578+
15251579
napi_status napi_create_error(napi_env env,
1580+
napi_value code,
15261581
napi_value msg,
15271582
napi_value* result) {
15281583
NAPI_PREAMBLE(env);
@@ -1532,13 +1587,18 @@ napi_status napi_create_error(napi_env env,
15321587
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15331588
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15341589

1535-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::Error(
1536-
message_value.As<v8::String>()));
1590+
v8::Local<v8::Value> error_obj =
1591+
v8::Exception::Error(message_value.As<v8::String>());
1592+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1593+
if (status != napi_ok) return status;
1594+
1595+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15371596

15381597
return GET_RETURN_STATUS(env);
15391598
}
15401599

15411600
napi_status napi_create_type_error(napi_env env,
1601+
napi_value code,
15421602
napi_value msg,
15431603
napi_value* result) {
15441604
NAPI_PREAMBLE(env);
@@ -1548,13 +1608,18 @@ napi_status napi_create_type_error(napi_env env,
15481608
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15491609
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15501610

1551-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::TypeError(
1552-
message_value.As<v8::String>()));
1611+
v8::Local<v8::Value> error_obj =
1612+
v8::Exception::TypeError(message_value.As<v8::String>());
1613+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1614+
if (status != napi_ok) return status;
1615+
1616+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15531617

15541618
return GET_RETURN_STATUS(env);
15551619
}
15561620

15571621
napi_status napi_create_range_error(napi_env env,
1622+
napi_value code,
15581623
napi_value msg,
15591624
napi_value* result) {
15601625
NAPI_PREAMBLE(env);
@@ -1564,8 +1629,12 @@ napi_status napi_create_range_error(napi_env env,
15641629
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15651630
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15661631

1567-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::RangeError(
1568-
message_value.As<v8::String>()));
1632+
v8::Local<v8::Value> error_obj =
1633+
v8::Exception::RangeError(message_value.As<v8::String>());
1634+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1635+
if (status != napi_ok) return status;
1636+
1637+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15691638

15701639
return GET_RETURN_STATUS(env);
15711640
}
@@ -1738,40 +1807,58 @@ napi_status napi_throw(napi_env env, napi_value error) {
17381807
return napi_clear_last_error(env);
17391808
}
17401809

1741-
napi_status napi_throw_error(napi_env env, const char* msg) {
1810+
napi_status napi_throw_error(napi_env env,
1811+
const char* code,
1812+
const char* msg) {
17421813
NAPI_PREAMBLE(env);
17431814

17441815
v8::Isolate* isolate = env->isolate;
17451816
v8::Local<v8::String> str;
17461817
CHECK_NEW_FROM_UTF8(env, str, msg);
17471818

1748-
isolate->ThrowException(v8::Exception::Error(str));
1819+
v8::Local<v8::Value> error_obj = v8::Exception::Error(str);
1820+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1821+
if (status != napi_ok) return status;
1822+
1823+
isolate->ThrowException(error_obj);
17491824
// any VM calls after this point and before returning
17501825
// to the javascript invoker will fail
17511826
return napi_clear_last_error(env);
17521827
}
17531828

1754-
napi_status napi_throw_type_error(napi_env env, const char* msg) {
1829+
napi_status napi_throw_type_error(napi_env env,
1830+
const char* code,
1831+
const char* msg) {
17551832
NAPI_PREAMBLE(env);
17561833

17571834
v8::Isolate* isolate = env->isolate;
17581835
v8::Local<v8::String> str;
17591836
CHECK_NEW_FROM_UTF8(env, str, msg);
17601837

1761-
isolate->ThrowException(v8::Exception::TypeError(str));
1838+
v8::Local<v8::Value> error_obj = v8::Exception::TypeError(str);
1839+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1840+
if (status != napi_ok) return status;
1841+
1842+
isolate->ThrowException(error_obj);
17621843
// any VM calls after this point and before returning
17631844
// to the javascript invoker will fail
17641845
return napi_clear_last_error(env);
17651846
}
17661847

1767-
napi_status napi_throw_range_error(napi_env env, const char* msg) {
1848+
napi_status napi_throw_range_error(napi_env env,
1849+
const char* code,
1850+
const char* msg) {
17681851
NAPI_PREAMBLE(env);
17691852

17701853
v8::Isolate* isolate = env->isolate;
17711854
v8::Local<v8::String> str;
17721855
CHECK_NEW_FROM_UTF8(env, str, msg);
17731856

1774-
isolate->ThrowException(v8::Exception::RangeError(str));
1857+
v8::Local<v8::Value> error_obj = v8::Exception::RangeError(str);
1858+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1859+
if (status != napi_ok) return status;
1860+
1861+
isolate->ThrowException(error_obj);
17751862
// any VM calls after this point and before returning
17761863
// to the javascript invoker will fail
17771864
return napi_clear_last_error(env);
@@ -2391,7 +2478,9 @@ napi_status napi_instanceof(napi_env env,
23912478
CHECK_TO_OBJECT(env, context, ctor, constructor);
23922479

23932480
if (!ctor->IsFunction()) {
2394-
napi_throw_type_error(env, "constructor must be a function");
2481+
napi_throw_type_error(env,
2482+
"ERR_NAPI_CONS_FUNCTION",
2483+
"Constructor must be a function");
23952484

23962485
return napi_set_last_error(env, napi_function_expected);
23972486
}
@@ -2459,7 +2548,10 @@ napi_status napi_instanceof(napi_env env,
24592548

24602549
v8::Local<v8::Value> prototype_property = maybe_prototype.ToLocalChecked();
24612550
if (!prototype_property->IsObject()) {
2462-
napi_throw_type_error(env, "constructor.prototype must be an object");
2551+
napi_throw_type_error(
2552+
env,
2553+
"ERR_NAPI_CONS_PROTOTYPE_OBJECT",
2554+
"Constructor.prototype must be an object");
24632555

24642556
return napi_set_last_error(env, napi_object_expected);
24652557
}

src/node_api.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,15 @@ NAPI_EXTERN napi_status napi_create_function(napi_env env,
142142
void* data,
143143
napi_value* result);
144144
NAPI_EXTERN napi_status napi_create_error(napi_env env,
145+
napi_value code,
145146
napi_value msg,
146147
napi_value* result);
147148
NAPI_EXTERN napi_status napi_create_type_error(napi_env env,
149+
napi_value code,
148150
napi_value msg,
149151
napi_value* result);
150152
NAPI_EXTERN napi_status napi_create_range_error(napi_env env,
153+
napi_value code,
151154
napi_value msg,
152155
napi_value* result);
153156

@@ -404,9 +407,15 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env,
404407

405408
// Methods to support error handling
406409
NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error);
407-
NAPI_EXTERN napi_status napi_throw_error(napi_env env, const char* msg);
408-
NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg);
409-
NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg);
410+
NAPI_EXTERN napi_status napi_throw_error(napi_env env,
411+
const char* code,
412+
const char* msg);
413+
NAPI_EXTERN napi_status napi_throw_type_error(napi_env env,
414+
const char* code,
415+
const char* msg);
416+
NAPI_EXTERN napi_status napi_throw_range_error(napi_env env,
417+
const char* code,
418+
const char* msg);
410419
NAPI_EXTERN napi_status napi_is_error(napi_env env,
411420
napi_value value,
412421
bool* result);

test/addons-napi/common.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
const char* error_message = error_info->error_message != NULL ? \
1313
error_info->error_message : \
1414
"empty error message"; \
15-
napi_throw_error((env), error_message); \
15+
napi_throw_error((env), NULL, error_message); \
1616
} \
1717
} while (0)
1818

@@ -21,6 +21,7 @@
2121
if (!(assertion)) { \
2222
napi_throw_error( \
2323
(env), \
24+
NULL, \
2425
"assertion (" #assertion ") failed: " message); \
2526
return ret_val; \
2627
} \

0 commit comments

Comments
 (0)