Skip to content

Commit f2efdc8

Browse files
mhdawsonaddaleax
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: nodejs#14453 Backport-Reviewed-By: James M Snell <jasnell@gmail.com> PR-URL: nodejs#13988 Fixes: nodejs#13933 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 1813467 commit f2efdc8

File tree

9 files changed

+305
-32
lines changed

9 files changed

+305
-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

+4
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`);
143143
E('ERR_UNKNOWN_SIGNAL', (signal) => `Unknown signal: ${signal}`);
144144
E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type');
145145
E('ERR_UNKNOWN_STREAM_TYPE', 'Unknown stream file type');
146+
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function');
147+
E('ERR_NAPI_CONS_PROTOTYPE_OBJECT', 'Constructor.prototype must be an object');
148+
E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support');
149+
E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s');
146150
E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound');
147151
E('ERR_SOCKET_BAD_TYPE',
148152
'Bad socket type specified. Valid types are: udp4, udp6');

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

@@ -1527,7 +1528,61 @@ napi_status napi_create_symbol(napi_env env,
15271528
return GET_RETURN_STATUS(env);
15281529
}
15291530

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

1540-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::Error(
1541-
message_value.As<v8::String>()));
1595+
v8::Local<v8::Value> error_obj =
1596+
v8::Exception::Error(message_value.As<v8::String>());
1597+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1598+
if (status != napi_ok) return status;
1599+
1600+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15421601

15431602
return GET_RETURN_STATUS(env);
15441603
}
15451604

15461605
napi_status napi_create_type_error(napi_env env,
1606+
napi_value code,
15471607
napi_value msg,
15481608
napi_value* result) {
15491609
NAPI_PREAMBLE(env);
@@ -1553,13 +1613,18 @@ napi_status napi_create_type_error(napi_env env,
15531613
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15541614
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15551615

1556-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::TypeError(
1557-
message_value.As<v8::String>()));
1616+
v8::Local<v8::Value> error_obj =
1617+
v8::Exception::TypeError(message_value.As<v8::String>());
1618+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1619+
if (status != napi_ok) return status;
1620+
1621+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15581622

15591623
return GET_RETURN_STATUS(env);
15601624
}
15611625

15621626
napi_status napi_create_range_error(napi_env env,
1627+
napi_value code,
15631628
napi_value msg,
15641629
napi_value* result) {
15651630
NAPI_PREAMBLE(env);
@@ -1569,8 +1634,12 @@ napi_status napi_create_range_error(napi_env env,
15691634
v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
15701635
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
15711636

1572-
*result = v8impl::JsValueFromV8LocalValue(v8::Exception::RangeError(
1573-
message_value.As<v8::String>()));
1637+
v8::Local<v8::Value> error_obj =
1638+
v8::Exception::RangeError(message_value.As<v8::String>());
1639+
napi_status status = set_error_code(env, error_obj, code, nullptr);
1640+
if (status != napi_ok) return status;
1641+
1642+
*result = v8impl::JsValueFromV8LocalValue(error_obj);
15741643

15751644
return GET_RETURN_STATUS(env);
15761645
}
@@ -1743,40 +1812,58 @@ napi_status napi_throw(napi_env env, napi_value error) {
17431812
return napi_clear_last_error(env);
17441813
}
17451814

1746-
napi_status napi_throw_error(napi_env env, const char* msg) {
1815+
napi_status napi_throw_error(napi_env env,
1816+
const char* code,
1817+
const char* msg) {
17471818
NAPI_PREAMBLE(env);
17481819

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

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

1759-
napi_status napi_throw_type_error(napi_env env, const char* msg) {
1834+
napi_status napi_throw_type_error(napi_env env,
1835+
const char* code,
1836+
const char* msg) {
17601837
NAPI_PREAMBLE(env);
17611838

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

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

1772-
napi_status napi_throw_range_error(napi_env env, const char* msg) {
1853+
napi_status napi_throw_range_error(napi_env env,
1854+
const char* code,
1855+
const char* msg) {
17731856
NAPI_PREAMBLE(env);
17741857

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

1779-
isolate->ThrowException(v8::Exception::RangeError(str));
1862+
v8::Local<v8::Value> error_obj = v8::Exception::RangeError(str);
1863+
napi_status status = set_error_code(env, error_obj, nullptr, code);
1864+
if (status != napi_ok) return status;
1865+
1866+
isolate->ThrowException(error_obj);
17801867
// any VM calls after this point and before returning
17811868
// to the javascript invoker will fail
17821869
return napi_clear_last_error(env);
@@ -2415,7 +2502,9 @@ napi_status napi_instanceof(napi_env env,
24152502
CHECK_TO_OBJECT(env, context, ctor, constructor);
24162503

24172504
if (!ctor->IsFunction()) {
2418-
napi_throw_type_error(env, "constructor must be a function");
2505+
napi_throw_type_error(env,
2506+
"ERR_NAPI_CONS_FUNCTION",
2507+
"Constructor must be a function");
24192508

24202509
return napi_set_last_error(env, napi_function_expected);
24212510
}
@@ -2483,7 +2572,10 @@ napi_status napi_instanceof(napi_env env,
24832572

24842573
v8::Local<v8::Value> prototype_property = maybe_prototype.ToLocalChecked();
24852574
if (!prototype_property->IsObject()) {
2486-
napi_throw_type_error(env, "constructor.prototype must be an object");
2575+
napi_throw_type_error(
2576+
env,
2577+
"ERR_NAPI_CONS_PROTOTYPE_OBJECT",
2578+
"Constructor.prototype must be an object");
24872579

24882580
return napi_set_last_error(env, napi_object_expected);
24892581
}

src/node_api.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,15 @@ NAPI_EXTERN napi_status napi_create_function(napi_env env,
151151
void* data,
152152
napi_value* result);
153153
NAPI_EXTERN napi_status napi_create_error(napi_env env,
154+
napi_value code,
154155
napi_value msg,
155156
napi_value* result);
156157
NAPI_EXTERN napi_status napi_create_type_error(napi_env env,
158+
napi_value code,
157159
napi_value msg,
158160
napi_value* result);
159161
NAPI_EXTERN napi_status napi_create_range_error(napi_env env,
162+
napi_value code,
160163
napi_value msg,
161164
napi_value* result);
162165

@@ -413,9 +416,15 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env,
413416

414417
// Methods to support error handling
415418
NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error);
416-
NAPI_EXTERN napi_status napi_throw_error(napi_env env, const char* msg);
417-
NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg);
418-
NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg);
419+
NAPI_EXTERN napi_status napi_throw_error(napi_env env,
420+
const char* code,
421+
const char* msg);
422+
NAPI_EXTERN napi_status napi_throw_type_error(napi_env env,
423+
const char* code,
424+
const char* msg);
425+
NAPI_EXTERN napi_status napi_throw_range_error(napi_env env,
426+
const char* code,
427+
const char* msg);
419428
NAPI_EXTERN napi_status napi_is_error(napi_env env,
420429
napi_value value,
421430
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)