Skip to content

Commit 57be12e

Browse files
mhdawsonMylesBorins
authored andcommitted
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. Backport-PR-URL: #19447 PR-URL: #13988 Fixes: #13933 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 4827421 commit 57be12e

File tree

9 files changed

+354
-32
lines changed

9 files changed

+354
-32
lines changed

doc/api/n-api.md

Lines changed: 46 additions & 3 deletions
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.

src/node_api.cc

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "node_internals.h"
2222
#include "env-inl.h"
2323
#include "node_api_backport.h"
24+
#include "util.h"
2425

2526
#define NAPI_VERSION 1
2627

@@ -1525,7 +1526,61 @@ napi_status napi_create_symbol(napi_env env,
15251526
return GET_RETURN_STATUS(env);
15261527
}
15271528

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

1538-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::Error(
1539-
message_value.As<v8::String>()));
1593+
v8::Local<v8::Value> error_obj =
1594+
v8::Exception::Error(message_value.As<v8::String>());
1595+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1596+
if (status != napi_ok) return status;
1597+
1598+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15401599

15411600
return GET_RETURN_STATUS(env);
15421601
}
15431602

15441603
napi_status napi_create_type_error(napi_env env,
1604+
napi_value code,
15451605
napi_value msg,
15461606
napi_value* result) {
15471607
NAPI_PREAMBLE(env);
@@ -1551,13 +1611,18 @@ napi_status napi_create_type_error(napi_env env,
15511611
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15521612
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15531613

1554-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::TypeError(
1555-
message_value.As<v8::String>()));
1614+
v8::Local<v8::Value> error_obj =
1615+
v8::Exception::TypeError(message_value.As<v8::String>());
1616+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1617+
if (status != napi_ok) return status;
1618+
1619+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15561620

15571621
return GET_RETURN_STATUS(env);
15581622
}
15591623

15601624
napi_status napi_create_range_error(napi_env env,
1625+
napi_value code,
15611626
napi_value msg,
15621627
napi_value* result) {
15631628
NAPI_PREAMBLE(env);
@@ -1567,8 +1632,12 @@ napi_status napi_create_range_error(napi_env env,
15671632
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15681633
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15691634

1570-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::RangeError(
1571-
message_value.As<v8::String>()));
1635+
v8::Local<v8::Value> error_obj =
1636+
v8::Exception::RangeError(message_value.As<v8::String>());
1637+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1638+
if (status != napi_ok) return status;
1639+
1640+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15721641

15731642
return GET_RETURN_STATUS(env);
15741643
}
@@ -1741,40 +1810,58 @@ napi_status napi_throw(napi_env env, napi_value error) {
17411810
return napi_clear_last_error(env);
17421811
}
17431812

1744-
napi_status napi_throw_error(napi_env env, const char* msg) {
1813+
napi_status napi_throw_error(napi_env env,
1814+
const char* code,
1815+
const char* msg) {
17451816
NAPI_PREAMBLE(env);
17461817

17471818
v8::Isolate* isolate = env->isolate;
17481819
v8::Local<v8::String> str;
17491820
CHECK_NEW_FROM_UTF8(env, str, msg);
17501821

1751-
isolate->ThrowException(v8::Exception::Error(str));
1822+
v8::Local<v8::Value> error_obj = v8::Exception::Error(str);
1823+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1824+
if (status != napi_ok) return status;
1825+
1826+
isolate->ThrowException(error_obj);
17521827
// any VM calls after this point and before returning
17531828
// to the javascript invoker will fail
17541829
return napi_clear_last_error(env);
17551830
}
17561831

1757-
napi_status napi_throw_type_error(napi_env env, const char* msg) {
1832+
napi_status napi_throw_type_error(napi_env env,
1833+
const char* code,
1834+
const char* msg) {
17581835
NAPI_PREAMBLE(env);
17591836

17601837
v8::Isolate* isolate = env->isolate;
17611838
v8::Local<v8::String> str;
17621839
CHECK_NEW_FROM_UTF8(env, str, msg);
17631840

1764-
isolate->ThrowException(v8::Exception::TypeError(str));
1841+
v8::Local<v8::Value> error_obj = v8::Exception::TypeError(str);
1842+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1843+
if (status != napi_ok) return status;
1844+
1845+
isolate->ThrowException(error_obj);
17651846
// any VM calls after this point and before returning
17661847
// to the javascript invoker will fail
17671848
return napi_clear_last_error(env);
17681849
}
17691850

1770-
napi_status napi_throw_range_error(napi_env env, const char* msg) {
1851+
napi_status napi_throw_range_error(napi_env env,
1852+
const char* code,
1853+
const char* msg) {
17711854
NAPI_PREAMBLE(env);
17721855

17731856
v8::Isolate* isolate = env->isolate;
17741857
v8::Local<v8::String> str;
17751858
CHECK_NEW_FROM_UTF8(env, str, msg);
17761859

1777-
isolate->ThrowException(v8::Exception::RangeError(str));
1860+
v8::Local<v8::Value> error_obj = v8::Exception::RangeError(str);
1861+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1862+
if (status != napi_ok) return status;
1863+
1864+
isolate->ThrowException(error_obj);
17781865
// any VM calls after this point and before returning
17791866
// to the javascript invoker will fail
17801867
return napi_clear_last_error(env);
@@ -2394,7 +2481,9 @@ napi_status napi_instanceof(napi_env env,
23942481
CHECK_TO_OBJECT(env, context, ctor, constructor);
23952482

23962483
if (!ctor->IsFunction()) {
2397-
napi_throw_type_error(env, "constructor must be a function");
2484+
napi_throw_type_error(env,
2485+
"ERR_NAPI_CONS_FUNCTION",
2486+
"Constructor must be a function");
23982487

23992488
return napi_set_last_error(env, napi_function_expected);
24002489
}
@@ -2462,7 +2551,10 @@ napi_status napi_instanceof(napi_env env,
24622551

24632552
v8::Local<v8::Value> prototype_property = maybe_prototype.ToLocalChecked();
24642553
if (!prototype_property->IsObject()) {
2465-
napi_throw_type_error(env, "constructor.prototype must be an object");
2554+
napi_throw_type_error(
2555+
env,
2556+
"ERR_NAPI_CONS_PROTOTYPE_OBJECT",
2557+
"Constructor.prototype must be an object");
24662558

24672559
return napi_set_last_error(env, napi_object_expected);
24682560
}

src/node_api.h

Lines changed: 12 additions & 3 deletions
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

Lines changed: 2 additions & 1 deletion
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)