Skip to content

Commit 03a190f

Browse files
committed
src: add error code helpers to env
This commit adds env->THROW_ERR_* and env->ERR_* helpers in the C++ land to quickly assign error codes to existing C++ errors. It also converts several errors in the C++ land that can be assigned either obvious existing error codes, or error codes close to existing ones. The plan is to first assign more error codes before v10, then either migrate the errors into JS land or improve the error messages during v10 without having to go semver-major since they already have error codes assigned. The following new error codes are added: - ERR_INVALID_CONSTRUCTOR_CALL - ERR_SCRIPT_EXECUTION_TIMEOUT (related to ERR_SCRIPT_EXECUTION_INTERRUPTED)
1 parent 83d44be commit 03a190f

File tree

13 files changed

+150
-79
lines changed

13 files changed

+150
-79
lines changed

doc/api/errors.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,12 @@ A callback function was required but was not been provided to a Node.js API.
10901090

10911091
Invalid characters were detected in headers.
10921092

1093+
<a id="ERR_INVALID_CONSTRUCTOR_CALL"></a>
1094+
### ERR_INVALID_CONSTRUCTOR_CALL
1095+
1096+
A function that should not be invoked as a construcor was invoked with `new`
1097+
or a constructor was invoked without `new`.
1098+
10931099
<a id="ERR_INVALID_CURSOR_POS"></a>
10941100
### ERR_INVALID_CURSOR_POS
10951101

@@ -1343,6 +1349,11 @@ An attempt was made to `require()` an [ES6 module][].
13431349
Script execution was interrupted by `SIGINT` (For example, when Ctrl+C was
13441350
pressed).
13451351

1352+
<a id="ERR_SCRIPT_EXECUTION_TIMEOUT"></a>
1353+
### ERR_SCRIPT_EXECUTION_TIMEOUT
1354+
1355+
Script execution timed out, possibly due to bugs in the script being executed.
1356+
13461357
<a id="ERR_SERVER_ALREADY_LISTEN"></a>
13471358
### ERR_SERVER_ALREADY_LISTEN
13481359

src/env-inl.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,23 @@ inline void Environment::SetTemplateMethod(v8::Local<v8::FunctionTemplate> that,
657657
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
658658
#undef V
659659

660+
#define V(code, type) \
661+
inline v8::Local<v8::Value> Environment::code(const char* message) const { \
662+
v8::Local<v8::String> js_code = OneByteString(isolate(), #code); \
663+
v8::Local<v8::String> js_msg = OneByteString(isolate(), message); \
664+
v8::Local<v8::Object> e = \
665+
v8::Exception::type(js_msg)->ToObject( \
666+
isolate()->GetCurrentContext()).ToLocalChecked(); \
667+
e->Set(code_string(), js_code); \
668+
return e; \
669+
} \
670+
\
671+
inline void Environment::THROW_ ## code(const char* message) const { \
672+
isolate()->ThrowException(code(message)); \
673+
}
674+
ENVIRONMENT_ERROR_HELPERS(V)
675+
#undef V
676+
660677
} // namespace node
661678

662679
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

src/env.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,21 @@ struct PackageConfig {
343343
V(url_constructor_function, v8::Function) \
344344
V(write_wrap_template, v8::ObjectTemplate)
345345

346+
#define ENVIRONMENT_ERROR_HELPERS(V) \
347+
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
348+
V(ERR_INDEX_OUT_OF_RANGE, RangeError) \
349+
V(ERR_INSPECTOR_ALREADY_CONNECTED, Error) \
350+
V(ERR_INVALID_ARG_VALUE, Error) \
351+
V(ERR_INVALID_ARG_TYPE, TypeError) \
352+
V(ERR_INVALID_CALLBACK, TypeError) \
353+
V(ERR_INVALID_CONSTRUCTOR_CALL, TypeError) \
354+
V(ERR_INVALID_THIS, TypeError) \
355+
V(ERR_MISSING_ARGS, TypeError) \
356+
V(ERR_MISSING_MODULE, Error) \
357+
V(ERR_OUT_OF_RANGE, RangeError) \
358+
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
359+
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error)
360+
346361
class Environment;
347362

348363
class IsolateData {
@@ -716,6 +731,12 @@ class Environment {
716731
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
717732
#undef V
718733

734+
#define V(code, _) \
735+
inline v8::Local<v8::Value> code(const char* message) const; \
736+
inline void THROW_ ## code(const char* message) const;
737+
ENVIRONMENT_ERROR_HELPERS(V)
738+
#undef V
739+
719740
#if HAVE_INSPECTOR
720741
inline inspector::Agent* inspector_agent() const {
721742
return inspector_agent_.get();

src/module_wrap.cc

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,20 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
8181
Isolate* isolate = args.GetIsolate();
8282

8383
if (!args.IsConstructCall()) {
84-
env->ThrowError("constructor must be called using new");
84+
env->THROW_ERR_INVALID_CONSTRUCTOR_CALL(
85+
"constructor must be called using new");
8586
return;
8687
}
8788

8889
if (!args[0]->IsString()) {
89-
env->ThrowError("first argument is not a string");
90+
env->THROW_ERR_INVALID_ARG_TYPE("first argument is not a string");
9091
return;
9192
}
9293

9394
Local<String> source_text = args[0].As<String>();
9495

9596
if (!args[1]->IsString()) {
96-
env->ThrowError("second argument is not a string");
97+
env->THROW_ERR_INVALID_ARG_TYPE("second argument is not a string");
9798
return;
9899
}
99100

@@ -162,7 +163,7 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
162163
Environment* env = Environment::GetCurrent(args);
163164
Isolate* isolate = args.GetIsolate();
164165
if (!args[0]->IsFunction()) {
165-
env->ThrowError("first argument is not a function");
166+
env->THROW_ERR_INVALID_ARG_TYPE("first argument is not a function");
166167
return;
167168
}
168169

@@ -283,9 +284,10 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
283284
// which this timeout is nested, so check whether one of the watchdogs
284285
// from this invocation is responsible for termination.
285286
if (timed_out) {
286-
env->ThrowError("Script execution timed out.");
287+
env->THROW_ERR_SCRIPT_EXECUTION_TIMEOUT("Script execution timed out.");
287288
} else if (received_signal) {
288-
env->ThrowError("Script execution interrupted.");
289+
env->THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(
290+
"Script execution interrupted.");
289291
}
290292
env->isolate()->CancelTerminateExecution();
291293
}
@@ -666,37 +668,39 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
666668
Environment* env = Environment::GetCurrent(args);
667669

668670
if (args.IsConstructCall()) {
669-
env->ThrowError("resolve() must not be called as a constructor");
671+
env->THROW_ERR_INVALID_CONSTRUCTOR_CALL(
672+
"resolve() must not be called as a constructor");
670673
return;
671674
}
672675
if (args.Length() != 2) {
673-
env->ThrowError("resolve must have exactly 2 arguments (string, string)");
676+
env->THROW_ERR_INVALID_ARG_TYPE(
677+
"resolve must have exactly 2 arguments (string, string)");
674678
return;
675679
}
676680

677681
if (!args[0]->IsString()) {
678-
env->ThrowError("first argument is not a string");
682+
env->THROW_ERR_INVALID_ARG_TYPE("first argument is not a string");
679683
return;
680684
}
681685
Utf8Value specifier_utf8(env->isolate(), args[0]);
682686
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
683687

684688
if (!args[1]->IsString()) {
685-
env->ThrowError("second argument is not a string");
689+
env->THROW_ERR_INVALID_ARG_TYPE("second argument is not a string");
686690
return;
687691
}
688692
Utf8Value url_utf8(env->isolate(), args[1]);
689693
URL url(*url_utf8, url_utf8.length());
690694

691695
if (url.flags() & URL_FLAGS_FAILED) {
692-
env->ThrowError("second argument is not a URL string");
696+
env->THROW_ERR_INVALID_ARG_TYPE("second argument is not a URL string");
693697
return;
694698
}
695699

696700
Maybe<URL> result = node::loader::Resolve(env, specifier_std, url);
697701
if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) {
698702
std::string msg = "Cannot find module " + specifier_std;
699-
env->ThrowError(msg.c_str());
703+
env->THROW_ERR_MISSING_MODULE(msg.c_str());
700704
return;
701705
}
702706

@@ -749,7 +753,7 @@ void ModuleWrap::SetImportModuleDynamicallyCallback(
749753
Environment* env = Environment::GetCurrent(args);
750754
HandleScope handle_scope(iso);
751755
if (!args[0]->IsFunction()) {
752-
env->ThrowError("first argument is not a function");
756+
env->THROW_ERR_INVALID_ARG_TYPE("first argument is not a function");
753757
return;
754758
}
755759

@@ -782,7 +786,7 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback(
782786
Environment* env = Environment::GetCurrent(args);
783787
Isolate* isolate = env->isolate();
784788
if (!args[0]->IsFunction()) {
785-
env->ThrowError("first argument is not a function");
789+
env->THROW_ERR_INVALID_ARG_TYPE("first argument is not a function");
786790
return;
787791
}
788792

src/node.cc

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,7 +1577,7 @@ static void Chdir(const FunctionCallbackInfo<Value>& args) {
15771577
Environment* env = Environment::GetCurrent(args);
15781578

15791579
if (args.Length() != 1 || !args[0]->IsString()) {
1580-
return env->ThrowTypeError("Bad argument.");
1580+
return env->THROW_ERR_INVALID_ARG_TYPE("Bad argument.");
15811581
}
15821582

15831583
node::Utf8Value path(args.GetIsolate(), args[0]);
@@ -1619,7 +1619,8 @@ static void Umask(const FunctionCallbackInfo<Value>& args) {
16191619
old = umask(0);
16201620
umask(static_cast<mode_t>(old));
16211621
} else if (!args[0]->IsInt32() && !args[0]->IsString()) {
1622-
return env->ThrowTypeError("argument must be an integer or octal string.");
1622+
return env->THROW_ERR_INVALID_ARG_TYPE(
1623+
"argument must be an integer or octal string.");
16231624
} else {
16241625
int oct;
16251626
if (args[0]->IsInt32()) {
@@ -1632,7 +1633,7 @@ static void Umask(const FunctionCallbackInfo<Value>& args) {
16321633
for (size_t i = 0; i < str.length(); i++) {
16331634
char c = (*str)[i];
16341635
if (c > '7' || c < '0') {
1635-
return env->ThrowTypeError("invalid octal string");
1636+
return env->THROW_ERR_INVALID_ARG_VALUE("invalid octal string");
16361637
}
16371638
oct *= 8;
16381639
oct += c - '0';
@@ -1776,7 +1777,8 @@ static void SetGid(const FunctionCallbackInfo<Value>& args) {
17761777
Environment* env = Environment::GetCurrent(args);
17771778

17781779
if (!args[0]->IsUint32() && !args[0]->IsString()) {
1779-
return env->ThrowTypeError("setgid argument must be a number or a string");
1780+
return env->THROW_ERR_INVALID_ARG_TYPE(
1781+
"setgid argument must be a number or a string");
17801782
}
17811783

17821784
gid_t gid = gid_by_name(env->isolate(), args[0]);
@@ -1795,7 +1797,8 @@ static void SetEGid(const FunctionCallbackInfo<Value>& args) {
17951797
Environment* env = Environment::GetCurrent(args);
17961798

17971799
if (!args[0]->IsUint32() && !args[0]->IsString()) {
1798-
return env->ThrowTypeError("setegid argument must be a number or string");
1800+
return env->THROW_ERR_INVALID_ARG_TYPE(
1801+
"setegid argument must be a number or string");
17991802
}
18001803

18011804
gid_t gid = gid_by_name(env->isolate(), args[0]);
@@ -1814,7 +1817,8 @@ static void SetUid(const FunctionCallbackInfo<Value>& args) {
18141817
Environment* env = Environment::GetCurrent(args);
18151818

18161819
if (!args[0]->IsUint32() && !args[0]->IsString()) {
1817-
return env->ThrowTypeError("setuid argument must be a number or a string");
1820+
return env->THROW_ERR_INVALID_ARG_TYPE(
1821+
"setuid argument must be a number or a string");
18181822
}
18191823

18201824
uid_t uid = uid_by_name(env->isolate(), args[0]);
@@ -1833,7 +1837,8 @@ static void SetEUid(const FunctionCallbackInfo<Value>& args) {
18331837
Environment* env = Environment::GetCurrent(args);
18341838

18351839
if (!args[0]->IsUint32() && !args[0]->IsString()) {
1836-
return env->ThrowTypeError("seteuid argument must be a number or string");
1840+
return env->THROW_ERR_INVALID_ARG_TYPE(
1841+
"seteuid argument must be a number or string");
18371842
}
18381843

18391844
uid_t uid = uid_by_name(env->isolate(), args[0]);
@@ -1890,7 +1895,7 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
18901895
Environment* env = Environment::GetCurrent(args);
18911896

18921897
if (!args[0]->IsArray()) {
1893-
return env->ThrowTypeError("argument 1 must be an array");
1898+
return env->THROW_ERR_INVALID_ARG_TYPE("argument 1 must be an array");
18941899
}
18951900

18961901
Local<Array> groups_list = args[0].As<Array>();
@@ -1921,11 +1926,13 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
19211926
Environment* env = Environment::GetCurrent(args);
19221927

19231928
if (!args[0]->IsUint32() && !args[0]->IsString()) {
1924-
return env->ThrowTypeError("argument 1 must be a number or a string");
1929+
return env->THROW_ERR_INVALID_ARG_TYPE(
1930+
"argument 1 must be a number or a string");
19251931
}
19261932

19271933
if (!args[1]->IsUint32() && !args[1]->IsString()) {
1928-
return env->ThrowTypeError("argument 2 must be a number or a string");
1934+
return env->THROW_ERR_INVALID_ARG_TYPE(
1935+
"argument 2 must be a number or a string");
19291936
}
19301937

19311938
node::Utf8Value arg0(env->isolate(), args[0]);
@@ -2040,7 +2047,7 @@ static void Kill(const FunctionCallbackInfo<Value>& args) {
20402047
Environment* env = Environment::GetCurrent(args);
20412048

20422049
if (args.Length() != 2) {
2043-
return env->ThrowError("Bad argument.");
2050+
return env->THROW_ERR_MISSING_ARGS("Bad argument.");
20442051
}
20452052

20462053
int pid = args[0]->Int32Value();
@@ -2238,13 +2245,13 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
22382245
CHECK_EQ(modpending, nullptr);
22392246

22402247
if (args.Length() < 2) {
2241-
env->ThrowError("process.dlopen needs at least 2 arguments.");
2248+
env->THROW_ERR_MISSING_ARGS("process.dlopen needs at least 2 arguments.");
22422249
return;
22432250
}
22442251

22452252
int32_t flags = DLib::kDefaultFlags;
22462253
if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) {
2247-
return env->ThrowTypeError("flag argument must be an integer.");
2254+
return env->THROW_ERR_INVALID_ARG_TYPE("flag argument must be an integer.");
22482255
}
22492256

22502257
Local<Object> module;

src/node_buffer.cc

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636

3737
#define MIN(a, b) ((a) < (b) ? (a) : (b))
3838

39-
#define THROW_AND_RETURN_IF_OOB(r) \
40-
do { \
41-
if (!(r)) return env->ThrowRangeError("Index out of range"); \
39+
#define THROW_AND_RETURN_IF_OOB(r) \
40+
do { \
41+
if (!(r)) return env->THROW_ERR_INDEX_OUT_OF_RANGE("Index out of range"); \
4242
} while (0)
4343

4444
#define SLICE_START_END(start_arg, end_arg, end_max) \
@@ -544,7 +544,7 @@ void Copy(const FunctionCallbackInfo<Value> &args) {
544544
return args.GetReturnValue().Set(0);
545545

546546
if (source_start > ts_obj_length)
547-
return env->ThrowRangeError("Index out of range");
547+
return env->THROW_ERR_INDEX_OUT_OF_RANGE("Index out of range");
548548

549549
if (source_end - source_start > target_length - target_start)
550550
source_end = source_start + target_length - target_start;
@@ -657,7 +657,7 @@ void StringWrite(const FunctionCallbackInfo<Value>& args) {
657657
SPREAD_BUFFER_ARG(args.This(), ts_obj);
658658

659659
if (!args[0]->IsString())
660-
return env->ThrowTypeError("Argument must be a string");
660+
return env->THROW_ERR_INVALID_ARG_TYPE("Argument must be a string");
661661

662662
Local<String> str = args[0]->ToString(env->context()).ToLocalChecked();
663663

@@ -666,7 +666,8 @@ void StringWrite(const FunctionCallbackInfo<Value>& args) {
666666

667667
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &offset));
668668
if (offset > ts_obj_length)
669-
return env->ThrowRangeError("Offset is out of bounds");
669+
return env->THROW_ERR_BUFFER_OUT_OF_BOUNDS(
670+
"\"offset\" is outside of buffer bounds");
670671

671672
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], ts_obj_length - offset,
672673
&max_length));
@@ -728,9 +729,9 @@ void CompareOffset(const FunctionCallbackInfo<Value> &args) {
728729
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[5], ts_obj_length, &source_end));
729730

730731
if (source_start > ts_obj_length)
731-
return env->ThrowRangeError("Index out of range");
732+
return env->THROW_ERR_INDEX_OUT_OF_RANGE("Index out of range");
732733
if (target_start > target_length)
733-
return env->ThrowRangeError("Index out of range");
734+
return env->THROW_ERR_INDEX_OUT_OF_RANGE("Index out of range");
734735

735736
CHECK_LE(source_start, source_end);
736737
CHECK_LE(target_start, target_end);

0 commit comments

Comments
 (0)