Skip to content

Commit c3dc0e0

Browse files
committed
src: add CollectExceptionInfo & errors.SystemError
Preparing for the migration of existing UVException and ErrnoExceptions from the native layer, add new `errors.SystemError` to internal/errors and new `env->CollectExceptionInfo()` / `env->CollectUVExceptionInfo()` methods. PR-URL: #16567 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent f1f0eb2 commit c3dc0e0

File tree

7 files changed

+720
-329
lines changed

7 files changed

+720
-329
lines changed

doc/api/errors.md

+26-2
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ checks or `abort()` calls in the C++ layer.
458458

459459
## System Errors
460460

461-
System errors are generated when exceptions occur within the program's
461+
System errors are generated when exceptions occur within the Node.js
462462
runtime environment. Typically, these are operational errors that occur
463463
when an application violates an operating system constraint such as attempting
464464
to read a file that does not exist or when the user does not have sufficient
@@ -471,7 +471,24 @@ of error codes and their meanings is available by running `man 2 intro` or
471471
In Node.js, system errors are represented as augmented `Error` objects with
472472
added properties.
473473

474-
### Class: System Error
474+
### Class: SystemError
475+
476+
### error.info
477+
478+
`SystemError` instances may have an additional `info` property whose
479+
value is an object with additional details about the error conditions.
480+
481+
The following properties are provided:
482+
483+
* `code` {string} The string error code
484+
* `errno` {number} The system-provided error number
485+
* `message` {string} A system-provided human readable description of the error
486+
* `syscall` {string} The name of the system call that triggered the error
487+
* `path` {Buffer} When reporting a file system error, the `path` will identify
488+
the file path.
489+
* `dest` {Buffer} When reporting a file system error, the `dest` will identify
490+
the file path destination (if any).
491+
475492

476493
#### error.code
477494

@@ -1379,6 +1396,13 @@ instance.setEncoding('utf8');
13791396
Used when an attempt is made to call [`stream.write()`][] after
13801397
`stream.end()` has been called.
13811398

1399+
<a id="ERR_SYSTEM_ERROR"></a>
1400+
### ERR_SYSTEM_ERROR
1401+
1402+
The `ERR_SYSTEM_ERROR` code is used when an unspecified or non-specific system
1403+
error has occurred within the Node.js process. The error object will have an
1404+
`err.info` object property with additional details.
1405+
13821406
<a id="ERR_TLS_CERT_ALTNAME_INVALID"></a>
13831407
### ERR_TLS_CERT_ALTNAME_INVALID
13841408

lib/internal/errors.js

+90
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// message may change, the code should not.
1111

1212
const kCode = Symbol('code');
13+
const kInfo = Symbol('info');
1314
const messages = new Map();
1415

1516
const { kMaxLength } = process.binding('buffer');
@@ -58,6 +59,68 @@ function makeNodeError(Base) {
5859
};
5960
}
6061

62+
// A specialized Error that includes an additional info property with
63+
// additional information about the error condition. The code key will
64+
// be extracted from the context object or the ERR_SYSTEM_ERROR default
65+
// will be used.
66+
class SystemError extends makeNodeError(Error) {
67+
constructor(context) {
68+
context = context || {};
69+
let code = 'ERR_SYSTEM_ERROR';
70+
if (messages.has(context.code))
71+
code = context.code;
72+
super(code,
73+
context.code,
74+
context.syscall,
75+
context.path,
76+
context.dest,
77+
context.message);
78+
Object.defineProperty(this, kInfo, {
79+
configurable: false,
80+
enumerable: false,
81+
value: context
82+
});
83+
}
84+
85+
get info() {
86+
return this[kInfo];
87+
}
88+
89+
get errno() {
90+
return this[kInfo].errno;
91+
}
92+
93+
set errno(val) {
94+
this[kInfo].errno = val;
95+
}
96+
97+
get syscall() {
98+
return this[kInfo].syscall;
99+
}
100+
101+
set syscall(val) {
102+
this[kInfo].syscall = val;
103+
}
104+
105+
get path() {
106+
return this[kInfo].path !== undefined ?
107+
this[kInfo].path.toString() : undefined;
108+
}
109+
110+
set path(val) {
111+
this[kInfo].path = val ? Buffer.from(val.toString()) : undefined;
112+
}
113+
114+
get dest() {
115+
return this[kInfo].path !== undefined ?
116+
this[kInfo].dest.toString() : undefined;
117+
}
118+
119+
set dest(val) {
120+
this[kInfo].dest = val ? Buffer.from(val.toString()) : undefined;
121+
}
122+
}
123+
61124
class AssertionError extends Error {
62125
constructor(options) {
63126
if (typeof options !== 'object' || options === null) {
@@ -128,6 +191,7 @@ module.exports = exports = {
128191
RangeError: makeNodeError(RangeError),
129192
URIError: makeNodeError(URIError),
130193
AssertionError,
194+
SystemError,
131195
E // This is exported only to facilitate testing.
132196
};
133197

@@ -144,6 +208,9 @@ module.exports = exports = {
144208
// Any error code added here should also be added to the documentation
145209
//
146210
// Note: Please try to keep these in alphabetical order
211+
//
212+
// Note: Node.js specific errors must begin with the prefix ERR_
213+
147214
E('ERR_ARG_NOT_ITERABLE', '%s must be iterable');
148215
E('ERR_ASSERTION', '%s');
149216
E('ERR_ASYNC_CALLBACK', (name) => `${name} must be a function`);
@@ -334,6 +401,7 @@ E('ERR_STREAM_READ_NOT_IMPLEMENTED', '_read() is not implemented');
334401
E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event');
335402
E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode');
336403
E('ERR_STREAM_WRITE_AFTER_END', 'write after end');
404+
E('ERR_SYSTEM_ERROR', sysError('A system error occurred'));
337405
E('ERR_TLS_CERT_ALTNAME_INVALID',
338406
'Hostname/IP does not match certificate\'s altnames: %s');
339407
E('ERR_TLS_DH_PARAM_SIZE', (size) =>
@@ -371,6 +439,28 @@ E('ERR_VALUE_OUT_OF_RANGE', (start, end, value) => {
371439
E('ERR_ZLIB_BINDING_CLOSED', 'zlib binding closed');
372440
E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed');
373441

442+
function sysError(defaultMessage) {
443+
return function(code,
444+
syscall,
445+
path,
446+
dest,
447+
message = defaultMessage) {
448+
if (code !== undefined)
449+
message += `: ${code}`;
450+
if (syscall !== undefined) {
451+
if (code === undefined)
452+
message += ':';
453+
message += ` [${syscall}]`;
454+
}
455+
if (path !== undefined) {
456+
message += `: ${path}`;
457+
if (dest !== undefined)
458+
message += ` => ${dest}`;
459+
}
460+
return message;
461+
};
462+
}
463+
374464
function invalidArgType(name, expected, actual) {
375465
internalAssert(name, 'name is required');
376466

src/env.cc

+78
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "node_internals.h"
22
#include "async-wrap.h"
33
#include "v8-profiler.h"
4+
#include "node_buffer.h"
45

56
#if defined(_MSC_VER)
67
#define getpid GetCurrentProcessId
@@ -228,4 +229,81 @@ void Environment::EnvPromiseHook(v8::PromiseHookType type,
228229
}
229230
}
230231

232+
void CollectExceptionInfo(Environment* env,
233+
v8::Local<v8::Object> obj,
234+
int errorno,
235+
const char* err_string,
236+
const char* syscall,
237+
const char* message,
238+
const char* path,
239+
const char* dest) {
240+
obj->Set(env->errno_string(), v8::Integer::New(env->isolate(), errorno));
241+
242+
obj->Set(env->context(), env->code_string(),
243+
OneByteString(env->isolate(), err_string)).FromJust();
244+
245+
if (message != nullptr) {
246+
obj->Set(env->context(), env->message_string(),
247+
OneByteString(env->isolate(), message)).FromJust();
248+
}
249+
250+
v8::Local<v8::Value> path_buffer;
251+
if (path != nullptr) {
252+
path_buffer =
253+
Buffer::Copy(env->isolate(), path, strlen(path)).ToLocalChecked();
254+
obj->Set(env->context(), env->path_string(), path_buffer).FromJust();
255+
}
256+
257+
v8::Local<v8::Value> dest_buffer;
258+
if (dest != nullptr) {
259+
dest_buffer =
260+
Buffer::Copy(env->isolate(), dest, strlen(dest)).ToLocalChecked();
261+
obj->Set(env->context(), env->dest_string(), dest_buffer).FromJust();
262+
}
263+
264+
if (syscall != nullptr) {
265+
obj->Set(env->context(), env->syscall_string(),
266+
OneByteString(env->isolate(), syscall));
267+
}
268+
}
269+
270+
void Environment::CollectExceptionInfo(v8::Local<v8::Value> object,
271+
int errorno,
272+
const char* syscall,
273+
const char* message,
274+
const char* path) {
275+
if (!object->IsObject() || errorno == 0)
276+
return;
277+
278+
v8::Local<v8::Object> obj = object.As<v8::Object>();
279+
const char* err_string = node::errno_string(errorno);
280+
281+
if (message == nullptr || message[0] == '\0') {
282+
message = strerror(errorno);
283+
}
284+
285+
node::CollectExceptionInfo(this, obj, errorno, err_string,
286+
syscall, message, path, nullptr);
287+
}
288+
289+
void Environment::CollectUVExceptionInfo(v8::Local<v8::Value> object,
290+
int errorno,
291+
const char* syscall,
292+
const char* message,
293+
const char* path,
294+
const char* dest) {
295+
if (!object->IsObject() || errorno == 0)
296+
return;
297+
298+
v8::Local<v8::Object> obj = object.As<v8::Object>();
299+
const char* err_string = uv_err_name(errorno);
300+
301+
if (message == nullptr || message[0] == '\0') {
302+
message = uv_strerror(errorno);
303+
}
304+
305+
node::CollectExceptionInfo(this, obj, errorno, err_string,
306+
syscall, message, path, dest);
307+
}
308+
231309
} // namespace node

src/env.h

+13
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,19 @@ class Environment {
617617
inline performance::performance_state* performance_state();
618618
inline std::map<std::string, uint64_t>* performance_marks();
619619

620+
void CollectExceptionInfo(v8::Local<v8::Value> context,
621+
int errorno,
622+
const char* syscall = nullptr,
623+
const char* message = nullptr,
624+
const char* path = nullptr);
625+
626+
void CollectUVExceptionInfo(v8::Local<v8::Value> context,
627+
int errorno,
628+
const char* syscall = nullptr,
629+
const char* message = nullptr,
630+
const char* path = nullptr,
631+
const char* dest = nullptr);
632+
620633
inline void ThrowError(const char* errmsg);
621634
inline void ThrowTypeError(const char* errmsg);
622635
inline void ThrowRangeError(const char* errmsg);

0 commit comments

Comments
 (0)