Skip to content

Commit 57be12e

Browse files
mhdawsonMylesBorins
authored andcommittedApr 16, 2018
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 <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
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

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

‎src/node_api.cc

+106-14
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

+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
} \

‎test/addons-napi/test_async/test_async.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void Execute(napi_env env, void* data) {
2929
carrier* c = static_cast<carrier*>(data);
3030

3131
if (c != &the_carrier) {
32-
napi_throw_type_error(env, "Wrong data parameter to Execute.");
32+
napi_throw_type_error(env, nullptr, "Wrong data parameter to Execute.");
3333
return;
3434
}
3535

@@ -40,12 +40,12 @@ void Complete(napi_env env, napi_status status, void* data) {
4040
carrier* c = static_cast<carrier*>(data);
4141

4242
if (c != &the_carrier) {
43-
napi_throw_type_error(env, "Wrong data parameter to Complete.");
43+
napi_throw_type_error(env, nullptr, "Wrong data parameter to Complete.");
4444
return;
4545
}
4646

4747
if (status != napi_ok) {
48-
napi_throw_type_error(env, "Execute callback failed.");
48+
napi_throw_type_error(env, nullptr, "Execute callback failed.");
4949
return;
5050
}
5151

‎test/addons-napi/test_error/test.js

+62
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ assert.throws(() => {
7272
test_error.throwTypeError();
7373
}, /^TypeError: type error$/);
7474

75+
assert.throws(
76+
() => test_error.throwErrorCode(),
77+
common.expectsError({
78+
code: 'ERR_TEST_CODE',
79+
message: 'Error [error]'
80+
})
81+
);
82+
83+
assert.throws(
84+
() => test_error.throwRangeErrorCode(),
85+
common.expectsError({
86+
code: 'ERR_TEST_CODE',
87+
message: 'RangeError [range error]'
88+
})
89+
);
90+
91+
assert.throws(
92+
() => test_error.throwTypeErrorCode(),
93+
common.expectsError({
94+
code: 'ERR_TEST_CODE',
95+
message: 'TypeError [type error]'
96+
})
97+
);
98+
7599
let error = test_error.createError();
76100
assert.ok(error instanceof Error, 'expected error to be an instance of Error');
77101
assert.strictEqual(error.message, 'error', 'expected message to be "error"');
@@ -89,3 +113,41 @@ assert.ok(error instanceof TypeError,
89113
assert.strictEqual(error.message,
90114
'type error',
91115
'expected message to be "type error"');
116+
117+
error = test_error.createErrorCode();
118+
assert.ok(error instanceof Error, 'expected error to be an instance of Error');
119+
assert.strictEqual(error.code,
120+
'ERR_TEST_CODE',
121+
'expected code to be "ERR_TEST_CODE"');
122+
assert.strictEqual(error.message,
123+
'Error [error]',
124+
'expected message to be "Error [error]"');
125+
assert.strictEqual(error.name,
126+
'Error [ERR_TEST_CODE]',
127+
'expected name to be "Error [ERR_TEST_CODE]"');
128+
129+
error = test_error.createRangeErrorCode();
130+
assert.ok(error instanceof RangeError,
131+
'expected error to be an instance of RangeError');
132+
assert.strictEqual(error.message,
133+
'RangeError [range error]',
134+
'expected message to be "RangeError [range error]"');
135+
assert.strictEqual(error.code,
136+
'ERR_TEST_CODE',
137+
'expected code to be "ERR_TEST_CODE"');
138+
assert.strictEqual(error.name,
139+
'RangeError [ERR_TEST_CODE]',
140+
'expected name to be "RangeError[ERR_TEST_CODE]"');
141+
142+
error = test_error.createTypeErrorCode();
143+
assert.ok(error instanceof TypeError,
144+
'expected error to be an instance of TypeError');
145+
assert.strictEqual(error.message,
146+
'TypeError [type error]',
147+
'expected message to be "TypeError [type error]"');
148+
assert.strictEqual(error.code,
149+
'ERR_TEST_CODE',
150+
'expected code to be "ERR_TEST_CODE"');
151+
assert.strictEqual(error.name,
152+
'TypeError [ERR_TEST_CODE]',
153+
'expected name to be "TypeError[ERR_TEST_CODE]"');

‎test/addons-napi/test_error/test_error.cc

+69-7
Original file line numberDiff line numberDiff line change
@@ -19,47 +19,103 @@ napi_value throwExistingError(napi_env env, napi_callback_info info) {
1919
napi_value message;
2020
napi_value error;
2121
NAPI_CALL(env, napi_create_string_utf8(env, "existing error", -1, &message));
22-
NAPI_CALL(env, napi_create_error(env, message, &error));
22+
NAPI_CALL(env, napi_create_error(env, nullptr, message, &error));
2323
NAPI_CALL(env, napi_throw(env, error));
2424
return nullptr;
2525
}
2626

2727
napi_value throwError(napi_env env, napi_callback_info info) {
28-
NAPI_CALL(env, napi_throw_error(env, "error"));
28+
NAPI_CALL(env, napi_throw_error(env, nullptr, "error"));
2929
return nullptr;
3030
}
3131

3232
napi_value throwRangeError(napi_env env, napi_callback_info info) {
33-
NAPI_CALL(env, napi_throw_range_error(env, "range error"));
33+
NAPI_CALL(env, napi_throw_range_error(env, nullptr, "range error"));
3434
return nullptr;
3535
}
3636

3737
napi_value throwTypeError(napi_env env, napi_callback_info info) {
38-
NAPI_CALL(env, napi_throw_type_error(env, "type error"));
38+
NAPI_CALL(env, napi_throw_type_error(env, nullptr, "type error"));
3939
return nullptr;
4040
}
4141

42+
napi_value throwErrorCode(napi_env env, napi_callback_info info) {
43+
NAPI_CALL(env, napi_throw_error(env, "ERR_TEST_CODE", "Error [error]"));
44+
return nullptr;
45+
}
46+
47+
napi_value throwRangeErrorCode(napi_env env, napi_callback_info info) {
48+
NAPI_CALL(env, napi_throw_range_error(env,
49+
"ERR_TEST_CODE",
50+
"RangeError [range error]"));
51+
return nullptr;
52+
}
53+
54+
napi_value throwTypeErrorCode(napi_env env, napi_callback_info info) {
55+
NAPI_CALL(env, napi_throw_type_error(env,
56+
"ERR_TEST_CODE",
57+
"TypeError [type error]"));
58+
return nullptr;
59+
}
60+
61+
4262
napi_value createError(napi_env env, napi_callback_info info) {
4363
napi_value result;
4464
napi_value message;
4565
NAPI_CALL(env, napi_create_string_utf8(env, "error", -1, &message));
46-
NAPI_CALL(env, napi_create_error(env, message, &result));
66+
NAPI_CALL(env, napi_create_error(env, nullptr, message, &result));
4767
return result;
4868
}
4969

5070
napi_value createRangeError(napi_env env, napi_callback_info info) {
5171
napi_value result;
5272
napi_value message;
5373
NAPI_CALL(env, napi_create_string_utf8(env, "range error", -1, &message));
54-
NAPI_CALL(env, napi_create_range_error(env, message, &result));
74+
NAPI_CALL(env, napi_create_range_error(env, nullptr, message, &result));
5575
return result;
5676
}
5777

5878
napi_value createTypeError(napi_env env, napi_callback_info info) {
5979
napi_value result;
6080
napi_value message;
6181
NAPI_CALL(env, napi_create_string_utf8(env, "type error", -1, &message));
62-
NAPI_CALL(env, napi_create_type_error(env, message, &result));
82+
NAPI_CALL(env, napi_create_type_error(env, nullptr, message, &result));
83+
return result;
84+
}
85+
86+
napi_value createErrorCode(napi_env env, napi_callback_info info) {
87+
napi_value result;
88+
napi_value message;
89+
napi_value code;
90+
NAPI_CALL(env, napi_create_string_utf8(env, "Error [error]", -1, &message));
91+
NAPI_CALL(env, napi_create_string_utf8(env, "ERR_TEST_CODE", -1, &code));
92+
NAPI_CALL(env, napi_create_error(env, code, message, &result));
93+
return result;
94+
}
95+
96+
napi_value createRangeErrorCode(napi_env env, napi_callback_info info) {
97+
napi_value result;
98+
napi_value message;
99+
napi_value code;
100+
NAPI_CALL(env, napi_create_string_utf8(env,
101+
"RangeError [range error]",
102+
-1,
103+
&message));
104+
NAPI_CALL(env, napi_create_string_utf8(env, "ERR_TEST_CODE", -1, &code));
105+
NAPI_CALL(env, napi_create_range_error(env, code, message, &result));
106+
return result;
107+
}
108+
109+
napi_value createTypeErrorCode(napi_env env, napi_callback_info info) {
110+
napi_value result;
111+
napi_value message;
112+
napi_value code;
113+
NAPI_CALL(env, napi_create_string_utf8(env,
114+
"TypeError [type error]",
115+
-1,
116+
&message));
117+
NAPI_CALL(env, napi_create_string_utf8(env, "ERR_TEST_CODE", -1, &code));
118+
NAPI_CALL(env, napi_create_type_error(env, code, message, &result));
63119
return result;
64120
}
65121

@@ -70,9 +126,15 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
70126
DECLARE_NAPI_PROPERTY("throwError", throwError),
71127
DECLARE_NAPI_PROPERTY("throwRangeError", throwRangeError),
72128
DECLARE_NAPI_PROPERTY("throwTypeError", throwTypeError),
129+
DECLARE_NAPI_PROPERTY("throwErrorCode", throwErrorCode),
130+
DECLARE_NAPI_PROPERTY("throwRangeErrorCode", throwRangeErrorCode),
131+
DECLARE_NAPI_PROPERTY("throwTypeErrorCode", throwTypeErrorCode),
73132
DECLARE_NAPI_PROPERTY("createError", createError),
74133
DECLARE_NAPI_PROPERTY("createRangeError", createRangeError),
75134
DECLARE_NAPI_PROPERTY("createTypeError", createTypeError),
135+
DECLARE_NAPI_PROPERTY("createErrorCode", createErrorCode),
136+
DECLARE_NAPI_PROPERTY("createRangeErrorCode", createRangeErrorCode),
137+
DECLARE_NAPI_PROPERTY("createTypeErrorCode", createTypeErrorCode),
76138
};
77139

78140
NAPI_CALL_RETURN_VOID(env, napi_define_properties(

‎test/addons-napi/test_typedarray/test_typedarray.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ napi_value Multiply(napi_env env, napi_callback_info info) {
6565
output_doubles[i] = input_doubles[i] * multiplier;
6666
}
6767
} else {
68-
napi_throw_error(env, "Typed array was of a type not expected by test.");
68+
napi_throw_error(env, NULL, "Typed array was of a type not expected by test.");
6969
return NULL;
7070
}
7171

‎test/common/index.js

+53
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,59 @@ Object.defineProperty(exports, 'hasIntl', {
626626
}
627627
});
628628

629+
// Useful for testing expected internal/error objects
630+
exports.expectsError = function expectsError(fn, settings, exact) {
631+
if (typeof fn !== 'function') {
632+
exact = settings;
633+
settings = fn;
634+
fn = undefined;
635+
}
636+
function innerFn(error) {
637+
assert.strictEqual(error.code, settings.code);
638+
if ('type' in settings) {
639+
const type = settings.type;
640+
if (type !== Error && !Error.isPrototypeOf(type)) {
641+
throw new TypeError('`settings.type` must inherit from `Error`');
642+
}
643+
assert(error instanceof type,
644+
`${error.name} is not instance of ${type.name}`);
645+
let typeName = error.constructor.name;
646+
if (typeName === 'NodeError' && type.name !== 'NodeError') {
647+
typeName = Object.getPrototypeOf(error.constructor).name;
648+
}
649+
assert.strictEqual(typeName, type.name);
650+
}
651+
if ('message' in settings) {
652+
const message = settings.message;
653+
if (typeof message === 'string') {
654+
assert.strictEqual(error.message, message);
655+
} else {
656+
assert(message.test(error.message),
657+
`${error.message} does not match ${message}`);
658+
}
659+
}
660+
if ('name' in settings) {
661+
assert.strictEqual(error.name, settings.name);
662+
}
663+
if (error.constructor.name === 'AssertionError') {
664+
['generatedMessage', 'actual', 'expected', 'operator'].forEach((key) => {
665+
if (key in settings) {
666+
const actual = error[key];
667+
const expected = settings[key];
668+
assert.strictEqual(actual, expected,
669+
`${key}: expected ${expected}, not ${actual}`);
670+
}
671+
});
672+
}
673+
return true;
674+
}
675+
if (fn) {
676+
assert.throws(fn, innerFn);
677+
return;
678+
}
679+
return exports.mustCall(innerFn, exact);
680+
};
681+
629682
// Crash the process on unhandled rejections.
630683
exports.crashOnUnhandledRejection = function() {
631684
process.on('unhandledRejection',

0 commit comments

Comments
 (0)
Please sign in to comment.