From c3dc0e0d75cc5a03de154f3586e1062df1e719e4 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 27 Oct 2017 16:25:14 -0700 Subject: [PATCH] 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: https://github.com/nodejs/node/pull/16567 Reviewed-By: Joyee Cheung Reviewed-By: Michael Dawson --- doc/api/errors.md | 28 +- lib/internal/errors.js | 90 +++++++ src/env.cc | 78 ++++++ src/env.h | 13 + src/node.cc | 327 ----------------------- src/node_internals.h | 326 ++++++++++++++++++++++ test/parallel/test-errors-systemerror.js | 187 +++++++++++++ 7 files changed, 720 insertions(+), 329 deletions(-) create mode 100644 test/parallel/test-errors-systemerror.js diff --git a/doc/api/errors.md b/doc/api/errors.md index 23817ef3d1924f..e7b629781e6d37 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -458,7 +458,7 @@ checks or `abort()` calls in the C++ layer. ## System Errors -System errors are generated when exceptions occur within the program's +System errors are generated when exceptions occur within the Node.js runtime environment. Typically, these are operational errors that occur when an application violates an operating system constraint such as attempting 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 In Node.js, system errors are represented as augmented `Error` objects with added properties. -### Class: System Error +### Class: SystemError + +### error.info + +`SystemError` instances may have an additional `info` property whose +value is an object with additional details about the error conditions. + +The following properties are provided: + +* `code` {string} The string error code +* `errno` {number} The system-provided error number +* `message` {string} A system-provided human readable description of the error +* `syscall` {string} The name of the system call that triggered the error +* `path` {Buffer} When reporting a file system error, the `path` will identify + the file path. +* `dest` {Buffer} When reporting a file system error, the `dest` will identify + the file path destination (if any). + #### error.code @@ -1379,6 +1396,13 @@ instance.setEncoding('utf8'); Used when an attempt is made to call [`stream.write()`][] after `stream.end()` has been called. + +### ERR_SYSTEM_ERROR + +The `ERR_SYSTEM_ERROR` code is used when an unspecified or non-specific system +error has occurred within the Node.js process. The error object will have an +`err.info` object property with additional details. + ### ERR_TLS_CERT_ALTNAME_INVALID diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 7148279672c2cc..d6a77b88757283 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -10,6 +10,7 @@ // message may change, the code should not. const kCode = Symbol('code'); +const kInfo = Symbol('info'); const messages = new Map(); const { kMaxLength } = process.binding('buffer'); @@ -58,6 +59,68 @@ function makeNodeError(Base) { }; } +// A specialized Error that includes an additional info property with +// additional information about the error condition. The code key will +// be extracted from the context object or the ERR_SYSTEM_ERROR default +// will be used. +class SystemError extends makeNodeError(Error) { + constructor(context) { + context = context || {}; + let code = 'ERR_SYSTEM_ERROR'; + if (messages.has(context.code)) + code = context.code; + super(code, + context.code, + context.syscall, + context.path, + context.dest, + context.message); + Object.defineProperty(this, kInfo, { + configurable: false, + enumerable: false, + value: context + }); + } + + get info() { + return this[kInfo]; + } + + get errno() { + return this[kInfo].errno; + } + + set errno(val) { + this[kInfo].errno = val; + } + + get syscall() { + return this[kInfo].syscall; + } + + set syscall(val) { + this[kInfo].syscall = val; + } + + get path() { + return this[kInfo].path !== undefined ? + this[kInfo].path.toString() : undefined; + } + + set path(val) { + this[kInfo].path = val ? Buffer.from(val.toString()) : undefined; + } + + get dest() { + return this[kInfo].path !== undefined ? + this[kInfo].dest.toString() : undefined; + } + + set dest(val) { + this[kInfo].dest = val ? Buffer.from(val.toString()) : undefined; + } +} + class AssertionError extends Error { constructor(options) { if (typeof options !== 'object' || options === null) { @@ -128,6 +191,7 @@ module.exports = exports = { RangeError: makeNodeError(RangeError), URIError: makeNodeError(URIError), AssertionError, + SystemError, E // This is exported only to facilitate testing. }; @@ -144,6 +208,9 @@ module.exports = exports = { // Any error code added here should also be added to the documentation // // Note: Please try to keep these in alphabetical order +// +// Note: Node.js specific errors must begin with the prefix ERR_ + E('ERR_ARG_NOT_ITERABLE', '%s must be iterable'); E('ERR_ASSERTION', '%s'); E('ERR_ASYNC_CALLBACK', (name) => `${name} must be a function`); @@ -334,6 +401,7 @@ E('ERR_STREAM_READ_NOT_IMPLEMENTED', '_read() is not implemented'); E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode'); E('ERR_STREAM_WRITE_AFTER_END', 'write after end'); +E('ERR_SYSTEM_ERROR', sysError('A system error occurred')); E('ERR_TLS_CERT_ALTNAME_INVALID', 'Hostname/IP does not match certificate\'s altnames: %s'); E('ERR_TLS_DH_PARAM_SIZE', (size) => @@ -371,6 +439,28 @@ E('ERR_VALUE_OUT_OF_RANGE', (start, end, value) => { E('ERR_ZLIB_BINDING_CLOSED', 'zlib binding closed'); E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed'); +function sysError(defaultMessage) { + return function(code, + syscall, + path, + dest, + message = defaultMessage) { + if (code !== undefined) + message += `: ${code}`; + if (syscall !== undefined) { + if (code === undefined) + message += ':'; + message += ` [${syscall}]`; + } + if (path !== undefined) { + message += `: ${path}`; + if (dest !== undefined) + message += ` => ${dest}`; + } + return message; + }; +} + function invalidArgType(name, expected, actual) { internalAssert(name, 'name is required'); diff --git a/src/env.cc b/src/env.cc index a0d82986c9e231..9bed08cdb09175 100644 --- a/src/env.cc +++ b/src/env.cc @@ -1,6 +1,7 @@ #include "node_internals.h" #include "async-wrap.h" #include "v8-profiler.h" +#include "node_buffer.h" #if defined(_MSC_VER) #define getpid GetCurrentProcessId @@ -228,4 +229,81 @@ void Environment::EnvPromiseHook(v8::PromiseHookType type, } } +void CollectExceptionInfo(Environment* env, + v8::Local obj, + int errorno, + const char* err_string, + const char* syscall, + const char* message, + const char* path, + const char* dest) { + obj->Set(env->errno_string(), v8::Integer::New(env->isolate(), errorno)); + + obj->Set(env->context(), env->code_string(), + OneByteString(env->isolate(), err_string)).FromJust(); + + if (message != nullptr) { + obj->Set(env->context(), env->message_string(), + OneByteString(env->isolate(), message)).FromJust(); + } + + v8::Local path_buffer; + if (path != nullptr) { + path_buffer = + Buffer::Copy(env->isolate(), path, strlen(path)).ToLocalChecked(); + obj->Set(env->context(), env->path_string(), path_buffer).FromJust(); + } + + v8::Local dest_buffer; + if (dest != nullptr) { + dest_buffer = + Buffer::Copy(env->isolate(), dest, strlen(dest)).ToLocalChecked(); + obj->Set(env->context(), env->dest_string(), dest_buffer).FromJust(); + } + + if (syscall != nullptr) { + obj->Set(env->context(), env->syscall_string(), + OneByteString(env->isolate(), syscall)); + } +} + +void Environment::CollectExceptionInfo(v8::Local object, + int errorno, + const char* syscall, + const char* message, + const char* path) { + if (!object->IsObject() || errorno == 0) + return; + + v8::Local obj = object.As(); + const char* err_string = node::errno_string(errorno); + + if (message == nullptr || message[0] == '\0') { + message = strerror(errorno); + } + + node::CollectExceptionInfo(this, obj, errorno, err_string, + syscall, message, path, nullptr); +} + +void Environment::CollectUVExceptionInfo(v8::Local object, + int errorno, + const char* syscall, + const char* message, + const char* path, + const char* dest) { + if (!object->IsObject() || errorno == 0) + return; + + v8::Local obj = object.As(); + const char* err_string = uv_err_name(errorno); + + if (message == nullptr || message[0] == '\0') { + message = uv_strerror(errorno); + } + + node::CollectExceptionInfo(this, obj, errorno, err_string, + syscall, message, path, dest); +} + } // namespace node diff --git a/src/env.h b/src/env.h index c8bb168df4d845..787cc7eb19d064 100644 --- a/src/env.h +++ b/src/env.h @@ -617,6 +617,19 @@ class Environment { inline performance::performance_state* performance_state(); inline std::map* performance_marks(); + void CollectExceptionInfo(v8::Local context, + int errorno, + const char* syscall = nullptr, + const char* message = nullptr, + const char* path = nullptr); + + void CollectUVExceptionInfo(v8::Local context, + int errorno, + const char* syscall = nullptr, + const char* message = nullptr, + const char* path = nullptr, + const char* dest = nullptr); + inline void ThrowError(const char* errmsg); inline void ThrowTypeError(const char* errmsg); inline void ThrowRangeError(const char* errmsg); diff --git a/src/node.cc b/src/node.cc index 3e88f36e1353cf..d304ca24c895cd 100644 --- a/src/node.cc +++ b/src/node.cc @@ -390,333 +390,6 @@ static void IdleImmediateDummy(uv_idle_t* handle) { // TODO(bnoordhuis) Maybe make libuv accept nullptr idle callbacks. } - -static inline const char *errno_string(int errorno) { -#define ERRNO_CASE(e) case e: return #e; - switch (errorno) { -#ifdef EACCES - ERRNO_CASE(EACCES); -#endif - -#ifdef EADDRINUSE - ERRNO_CASE(EADDRINUSE); -#endif - -#ifdef EADDRNOTAVAIL - ERRNO_CASE(EADDRNOTAVAIL); -#endif - -#ifdef EAFNOSUPPORT - ERRNO_CASE(EAFNOSUPPORT); -#endif - -#ifdef EAGAIN - ERRNO_CASE(EAGAIN); -#endif - -#ifdef EWOULDBLOCK -# if EAGAIN != EWOULDBLOCK - ERRNO_CASE(EWOULDBLOCK); -# endif -#endif - -#ifdef EALREADY - ERRNO_CASE(EALREADY); -#endif - -#ifdef EBADF - ERRNO_CASE(EBADF); -#endif - -#ifdef EBADMSG - ERRNO_CASE(EBADMSG); -#endif - -#ifdef EBUSY - ERRNO_CASE(EBUSY); -#endif - -#ifdef ECANCELED - ERRNO_CASE(ECANCELED); -#endif - -#ifdef ECHILD - ERRNO_CASE(ECHILD); -#endif - -#ifdef ECONNABORTED - ERRNO_CASE(ECONNABORTED); -#endif - -#ifdef ECONNREFUSED - ERRNO_CASE(ECONNREFUSED); -#endif - -#ifdef ECONNRESET - ERRNO_CASE(ECONNRESET); -#endif - -#ifdef EDEADLK - ERRNO_CASE(EDEADLK); -#endif - -#ifdef EDESTADDRREQ - ERRNO_CASE(EDESTADDRREQ); -#endif - -#ifdef EDOM - ERRNO_CASE(EDOM); -#endif - -#ifdef EDQUOT - ERRNO_CASE(EDQUOT); -#endif - -#ifdef EEXIST - ERRNO_CASE(EEXIST); -#endif - -#ifdef EFAULT - ERRNO_CASE(EFAULT); -#endif - -#ifdef EFBIG - ERRNO_CASE(EFBIG); -#endif - -#ifdef EHOSTUNREACH - ERRNO_CASE(EHOSTUNREACH); -#endif - -#ifdef EIDRM - ERRNO_CASE(EIDRM); -#endif - -#ifdef EILSEQ - ERRNO_CASE(EILSEQ); -#endif - -#ifdef EINPROGRESS - ERRNO_CASE(EINPROGRESS); -#endif - -#ifdef EINTR - ERRNO_CASE(EINTR); -#endif - -#ifdef EINVAL - ERRNO_CASE(EINVAL); -#endif - -#ifdef EIO - ERRNO_CASE(EIO); -#endif - -#ifdef EISCONN - ERRNO_CASE(EISCONN); -#endif - -#ifdef EISDIR - ERRNO_CASE(EISDIR); -#endif - -#ifdef ELOOP - ERRNO_CASE(ELOOP); -#endif - -#ifdef EMFILE - ERRNO_CASE(EMFILE); -#endif - -#ifdef EMLINK - ERRNO_CASE(EMLINK); -#endif - -#ifdef EMSGSIZE - ERRNO_CASE(EMSGSIZE); -#endif - -#ifdef EMULTIHOP - ERRNO_CASE(EMULTIHOP); -#endif - -#ifdef ENAMETOOLONG - ERRNO_CASE(ENAMETOOLONG); -#endif - -#ifdef ENETDOWN - ERRNO_CASE(ENETDOWN); -#endif - -#ifdef ENETRESET - ERRNO_CASE(ENETRESET); -#endif - -#ifdef ENETUNREACH - ERRNO_CASE(ENETUNREACH); -#endif - -#ifdef ENFILE - ERRNO_CASE(ENFILE); -#endif - -#ifdef ENOBUFS - ERRNO_CASE(ENOBUFS); -#endif - -#ifdef ENODATA - ERRNO_CASE(ENODATA); -#endif - -#ifdef ENODEV - ERRNO_CASE(ENODEV); -#endif - -#ifdef ENOENT - ERRNO_CASE(ENOENT); -#endif - -#ifdef ENOEXEC - ERRNO_CASE(ENOEXEC); -#endif - -#ifdef ENOLINK - ERRNO_CASE(ENOLINK); -#endif - -#ifdef ENOLCK -# if ENOLINK != ENOLCK - ERRNO_CASE(ENOLCK); -# endif -#endif - -#ifdef ENOMEM - ERRNO_CASE(ENOMEM); -#endif - -#ifdef ENOMSG - ERRNO_CASE(ENOMSG); -#endif - -#ifdef ENOPROTOOPT - ERRNO_CASE(ENOPROTOOPT); -#endif - -#ifdef ENOSPC - ERRNO_CASE(ENOSPC); -#endif - -#ifdef ENOSR - ERRNO_CASE(ENOSR); -#endif - -#ifdef ENOSTR - ERRNO_CASE(ENOSTR); -#endif - -#ifdef ENOSYS - ERRNO_CASE(ENOSYS); -#endif - -#ifdef ENOTCONN - ERRNO_CASE(ENOTCONN); -#endif - -#ifdef ENOTDIR - ERRNO_CASE(ENOTDIR); -#endif - -#ifdef ENOTEMPTY -# if ENOTEMPTY != EEXIST - ERRNO_CASE(ENOTEMPTY); -# endif -#endif - -#ifdef ENOTSOCK - ERRNO_CASE(ENOTSOCK); -#endif - -#ifdef ENOTSUP - ERRNO_CASE(ENOTSUP); -#else -# ifdef EOPNOTSUPP - ERRNO_CASE(EOPNOTSUPP); -# endif -#endif - -#ifdef ENOTTY - ERRNO_CASE(ENOTTY); -#endif - -#ifdef ENXIO - ERRNO_CASE(ENXIO); -#endif - - -#ifdef EOVERFLOW - ERRNO_CASE(EOVERFLOW); -#endif - -#ifdef EPERM - ERRNO_CASE(EPERM); -#endif - -#ifdef EPIPE - ERRNO_CASE(EPIPE); -#endif - -#ifdef EPROTO - ERRNO_CASE(EPROTO); -#endif - -#ifdef EPROTONOSUPPORT - ERRNO_CASE(EPROTONOSUPPORT); -#endif - -#ifdef EPROTOTYPE - ERRNO_CASE(EPROTOTYPE); -#endif - -#ifdef ERANGE - ERRNO_CASE(ERANGE); -#endif - -#ifdef EROFS - ERRNO_CASE(EROFS); -#endif - -#ifdef ESPIPE - ERRNO_CASE(ESPIPE); -#endif - -#ifdef ESRCH - ERRNO_CASE(ESRCH); -#endif - -#ifdef ESTALE - ERRNO_CASE(ESTALE); -#endif - -#ifdef ETIME - ERRNO_CASE(ETIME); -#endif - -#ifdef ETIMEDOUT - ERRNO_CASE(ETIMEDOUT); -#endif - -#ifdef ETXTBSY - ERRNO_CASE(ETXTBSY); -#endif - -#ifdef EXDEV - ERRNO_CASE(EXDEV); -#endif - - default: return ""; - } -} - const char *signo_string(int signo) { #define SIGNO_CASE(e) case e: return #e; switch (signo) { diff --git a/src/node_internals.h b/src/node_internals.h index 5d2f996cc8c1c5..579221b85c3478 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -324,6 +324,332 @@ class InternalCallbackScope { bool closed_ = false; }; +static inline const char *errno_string(int errorno) { +#define ERRNO_CASE(e) case e: return #e; + switch (errorno) { +#ifdef EACCES + ERRNO_CASE(EACCES); +#endif + +#ifdef EADDRINUSE + ERRNO_CASE(EADDRINUSE); +#endif + +#ifdef EADDRNOTAVAIL + ERRNO_CASE(EADDRNOTAVAIL); +#endif + +#ifdef EAFNOSUPPORT + ERRNO_CASE(EAFNOSUPPORT); +#endif + +#ifdef EAGAIN + ERRNO_CASE(EAGAIN); +#endif + +#ifdef EWOULDBLOCK +# if EAGAIN != EWOULDBLOCK + ERRNO_CASE(EWOULDBLOCK); +# endif +#endif + +#ifdef EALREADY + ERRNO_CASE(EALREADY); +#endif + +#ifdef EBADF + ERRNO_CASE(EBADF); +#endif + +#ifdef EBADMSG + ERRNO_CASE(EBADMSG); +#endif + +#ifdef EBUSY + ERRNO_CASE(EBUSY); +#endif + +#ifdef ECANCELED + ERRNO_CASE(ECANCELED); +#endif + +#ifdef ECHILD + ERRNO_CASE(ECHILD); +#endif + +#ifdef ECONNABORTED + ERRNO_CASE(ECONNABORTED); +#endif + +#ifdef ECONNREFUSED + ERRNO_CASE(ECONNREFUSED); +#endif + +#ifdef ECONNRESET + ERRNO_CASE(ECONNRESET); +#endif + +#ifdef EDEADLK + ERRNO_CASE(EDEADLK); +#endif + +#ifdef EDESTADDRREQ + ERRNO_CASE(EDESTADDRREQ); +#endif + +#ifdef EDOM + ERRNO_CASE(EDOM); +#endif + +#ifdef EDQUOT + ERRNO_CASE(EDQUOT); +#endif + +#ifdef EEXIST + ERRNO_CASE(EEXIST); +#endif + +#ifdef EFAULT + ERRNO_CASE(EFAULT); +#endif + +#ifdef EFBIG + ERRNO_CASE(EFBIG); +#endif + +#ifdef EHOSTUNREACH + ERRNO_CASE(EHOSTUNREACH); +#endif + +#ifdef EIDRM + ERRNO_CASE(EIDRM); +#endif + +#ifdef EILSEQ + ERRNO_CASE(EILSEQ); +#endif + +#ifdef EINPROGRESS + ERRNO_CASE(EINPROGRESS); +#endif + +#ifdef EINTR + ERRNO_CASE(EINTR); +#endif + +#ifdef EINVAL + ERRNO_CASE(EINVAL); +#endif + +#ifdef EIO + ERRNO_CASE(EIO); +#endif + +#ifdef EISCONN + ERRNO_CASE(EISCONN); +#endif + +#ifdef EISDIR + ERRNO_CASE(EISDIR); +#endif + +#ifdef ELOOP + ERRNO_CASE(ELOOP); +#endif + +#ifdef EMFILE + ERRNO_CASE(EMFILE); +#endif + +#ifdef EMLINK + ERRNO_CASE(EMLINK); +#endif + +#ifdef EMSGSIZE + ERRNO_CASE(EMSGSIZE); +#endif + +#ifdef EMULTIHOP + ERRNO_CASE(EMULTIHOP); +#endif + +#ifdef ENAMETOOLONG + ERRNO_CASE(ENAMETOOLONG); +#endif + +#ifdef ENETDOWN + ERRNO_CASE(ENETDOWN); +#endif + +#ifdef ENETRESET + ERRNO_CASE(ENETRESET); +#endif + +#ifdef ENETUNREACH + ERRNO_CASE(ENETUNREACH); +#endif + +#ifdef ENFILE + ERRNO_CASE(ENFILE); +#endif + +#ifdef ENOBUFS + ERRNO_CASE(ENOBUFS); +#endif + +#ifdef ENODATA + ERRNO_CASE(ENODATA); +#endif + +#ifdef ENODEV + ERRNO_CASE(ENODEV); +#endif + +#ifdef ENOENT + ERRNO_CASE(ENOENT); +#endif + +#ifdef ENOEXEC + ERRNO_CASE(ENOEXEC); +#endif + +#ifdef ENOLINK + ERRNO_CASE(ENOLINK); +#endif + +#ifdef ENOLCK +# if ENOLINK != ENOLCK + ERRNO_CASE(ENOLCK); +# endif +#endif + +#ifdef ENOMEM + ERRNO_CASE(ENOMEM); +#endif + +#ifdef ENOMSG + ERRNO_CASE(ENOMSG); +#endif + +#ifdef ENOPROTOOPT + ERRNO_CASE(ENOPROTOOPT); +#endif + +#ifdef ENOSPC + ERRNO_CASE(ENOSPC); +#endif + +#ifdef ENOSR + ERRNO_CASE(ENOSR); +#endif + +#ifdef ENOSTR + ERRNO_CASE(ENOSTR); +#endif + +#ifdef ENOSYS + ERRNO_CASE(ENOSYS); +#endif + +#ifdef ENOTCONN + ERRNO_CASE(ENOTCONN); +#endif + +#ifdef ENOTDIR + ERRNO_CASE(ENOTDIR); +#endif + +#ifdef ENOTEMPTY +# if ENOTEMPTY != EEXIST + ERRNO_CASE(ENOTEMPTY); +# endif +#endif + +#ifdef ENOTSOCK + ERRNO_CASE(ENOTSOCK); +#endif + +#ifdef ENOTSUP + ERRNO_CASE(ENOTSUP); +#else +# ifdef EOPNOTSUPP + ERRNO_CASE(EOPNOTSUPP); +# endif +#endif + +#ifdef ENOTTY + ERRNO_CASE(ENOTTY); +#endif + +#ifdef ENXIO + ERRNO_CASE(ENXIO); +#endif + + +#ifdef EOVERFLOW + ERRNO_CASE(EOVERFLOW); +#endif + +#ifdef EPERM + ERRNO_CASE(EPERM); +#endif + +#ifdef EPIPE + ERRNO_CASE(EPIPE); +#endif + +#ifdef EPROTO + ERRNO_CASE(EPROTO); +#endif + +#ifdef EPROTONOSUPPORT + ERRNO_CASE(EPROTONOSUPPORT); +#endif + +#ifdef EPROTOTYPE + ERRNO_CASE(EPROTOTYPE); +#endif + +#ifdef ERANGE + ERRNO_CASE(ERANGE); +#endif + +#ifdef EROFS + ERRNO_CASE(EROFS); +#endif + +#ifdef ESPIPE + ERRNO_CASE(ESPIPE); +#endif + +#ifdef ESRCH + ERRNO_CASE(ESRCH); +#endif + +#ifdef ESTALE + ERRNO_CASE(ESTALE); +#endif + +#ifdef ETIME + ERRNO_CASE(ETIME); +#endif + +#ifdef ETIMEDOUT + ERRNO_CASE(ETIMEDOUT); +#endif + +#ifdef ETXTBSY + ERRNO_CASE(ETXTBSY); +#endif + +#ifdef EXDEV + ERRNO_CASE(EXDEV); +#endif + + default: return ""; + } +} + #define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_INTERNAL) \ diff --git a/test/parallel/test-errors-systemerror.js b/test/parallel/test-errors-systemerror.js new file mode 100644 index 00000000000000..45ac7341752512 --- /dev/null +++ b/test/parallel/test-errors-systemerror.js @@ -0,0 +1,187 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const errors = require('internal/errors'); + +common.expectsError( + () => { throw new errors.SystemError(); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred' + } +); + +common.expectsError( + () => { throw new errors.SystemError({}); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred' + } +); + +common.expectsError( + () => { throw new errors.SystemError(null); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred' + } +); + +common.expectsError( + () => { throw new errors.SystemError({ code: 'ERR' }); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred: ERR' + } +); + +{ + const ctx = { + code: 'ERR', + syscall: 'foo' + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred: ERR [foo]' + } + ); +} + +{ + const ctx = { + code: 'ERR', + syscall: 'foo', + path: Buffer.from('a') + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred: ERR [foo]: a' + } + ); +} + +{ + const ctx = { + code: 'ERR', + syscall: 'foo', + path: Buffer.from('a'), + dest: Buffer.from('b') + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred: ERR [foo]: a => b' + } + ); +} + +{ + const ctx = { + syscall: 'foo', + path: Buffer.from('a'), + dest: Buffer.from('b') + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred: [foo]: a => b' + } + ); +} + +{ + const ctx = { + path: Buffer.from('a'), + dest: Buffer.from('b') + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'A system error occurred: a => b' + } + ); +} + +{ + const ctx = { + code: 'ERR', + message: 'something happened', + syscall: 'foo', + path: Buffer.from('a'), + dest: Buffer.from('b') + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_SYSTEM_ERROR', + type: errors.SystemError, + message: 'something happened: ERR [foo]: a => b' + } + ); +} + +{ + const ctx = { + code: 'ERR_ASSERTION' + }; + common.expectsError( + () => { throw new errors.SystemError(ctx); }, + { + code: 'ERR_ASSERTION', + type: errors.SystemError + } + ); +} + +{ + const ctx = { + code: 'ERR', + errno: 123, + message: 'something happened', + syscall: 'foo', + path: Buffer.from('a'), + dest: Buffer.from('b') + }; + const err = new errors.SystemError(ctx); + assert.strictEqual(err.info, ctx); + assert.strictEqual(err.code, 'ERR_SYSTEM_ERROR'); + err.code = 'test'; + assert.strictEqual(err.code, 'test'); + + // Test legacy properties. These shouldn't be used anymore + // but let us make sure they still work + assert.strictEqual(err.errno, 123); + assert.strictEqual(err.syscall, 'foo'); + assert.strictEqual(err.path, 'a'); + assert.strictEqual(err.dest, 'b'); + + // Make sure it's mutable + err.code = 'test'; + err.errno = 321; + err.syscall = 'test'; + err.path = 'path'; + err.dest = 'path'; + + assert.strictEqual(err.errno, 321); + assert.strictEqual(err.syscall, 'test'); + assert.strictEqual(err.path, 'path'); + assert.strictEqual(err.dest, 'path'); +}