diff --git a/lib/_debugger.js b/lib/_debugger.js index e872d77ea55962..5a104e3840faa2 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -126,7 +126,8 @@ Protocol.prototype.execute = function(d) { break; default: - throw new Error('Unknown state'); + const errors = require('internal/errors'); + throw new errors.Error('UNKNOWNSTATE'); } }; diff --git a/lib/_http_client.js b/lib/_http_client.js index 68eb125e0e10e9..3faa98180a8d3a 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const util = require('util'); const net = require('net'); const url = require('url'); @@ -22,7 +23,7 @@ function ClientRequest(options, cb) { if (typeof options === 'string') { options = url.parse(options); if (!options.hostname) { - throw new Error('Unable to determine the domain name'); + throw new errors.Error('DNSUNKNOWNDOMAIN'); } } else { options = util._extend({}, options); @@ -50,10 +51,11 @@ function ClientRequest(options, cb) { // well, and b) possibly too restrictive for real-world usage. That's // why it only scans for spaces because those are guaranteed to create // an invalid request. - throw new TypeError('Request path contains unescaped characters'); + throw new errors.TypeError('HTTPINVALIDPATH'); } else if (protocol !== expectedProtocol) { - throw new Error('Protocol "' + protocol + '" not supported. ' + - 'Expected "' + expectedProtocol + '"'); + throw new errors.Error('HTTPUNSUPPORTEDPROTOCOL', + protocol, + expectedProtocol); } const defaultPort = options.defaultPort || @@ -70,7 +72,7 @@ function ClientRequest(options, cb) { var method = self.method = (options.method || 'GET').toUpperCase(); if (!common._checkIsHttpToken(method)) { - throw new TypeError('Method must be a valid HTTP token'); + throw new errors.TypeError('HTTPMETHODINVALID'); } self.path = options.path || '/'; if (cb) { @@ -247,9 +249,7 @@ function emitAbortNT(self) { function createHangUpError() { - var error = new Error('socket hang up'); - error.code = 'ECONNRESET'; - return error; + return new errors.Error('ECONNRESET'); } diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index e1e78e010cfd62..8c6f42e3f6b682 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const assert = require('assert').ok; const Stream = require('stream'); const timers = require('timers'); @@ -302,11 +303,10 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { function storeHeader(self, state, field, value) { if (!common._checkIsHttpToken(field)) { - throw new TypeError( - 'Header name must be a valid HTTP Token ["' + field + '"]'); + throw new errors.TypeError('HTTPINVALIDTOKEN', field); } if (common._checkInvalidHeaderChar(value) === true) { - throw new TypeError('The header content contains invalid characters'); + throw new errors.TypeError('HTTPINVALIDHEADER'); } state.messageHeader += field + ': ' + escapeHeaderValue(value) + CRLF; @@ -336,17 +336,15 @@ function storeHeader(self, state, field, value) { OutgoingMessage.prototype.setHeader = function(name, value) { if (!common._checkIsHttpToken(name)) - throw new TypeError( - 'Header name must be a valid HTTP Token ["' + name + '"]'); + throw new errors.TypeError('HTTPINVALIDTOKEN', name); if (typeof name !== 'string') - throw new TypeError('"name" should be a string in setHeader(name, value)'); + throw new errors.TypeError('INVALIDARG', 'name', 'string'); if (value === undefined) - throw new Error('"value" required in setHeader("' + name + '", value)'); + throw new errors.Error('REQUIREDARG', 'value'); if (this._header) - throw new Error('Can\'t set headers after they are sent.'); - if (common._checkInvalidHeaderChar(value) === true) { - throw new TypeError('The header content contains invalid characters'); - } + throw new errors.Error('HEADERSSENT'); + if (common._checkInvalidHeaderChar(value) === true) + throw new errors.TypeError('HTTPINVALIDHEADER'); if (this._headers === null) this._headers = {}; @@ -361,7 +359,7 @@ OutgoingMessage.prototype.setHeader = function(name, value) { OutgoingMessage.prototype.getHeader = function(name) { if (arguments.length < 1) { - throw new Error('"name" argument is required for getHeader(name)'); + throw new errors.Error('REQUIREDARG', 'name'); } if (!this._headers) return; @@ -440,7 +438,7 @@ OutgoingMessage.prototype.write = function(chunk, encoding, callback) { } if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) { - throw new TypeError('First argument must be a string or Buffer'); + throw new errors.TypeError('INVALIDARG', 'chunk', ['Buffer', 'string']); } @@ -515,11 +513,10 @@ OutgoingMessage.prototype.addTrailers = function(headers) { value = headers[key]; } if (!common._checkIsHttpToken(field)) { - throw new TypeError( - 'Trailer name must be a valid HTTP Token ["' + field + '"]'); + throw new errors.TypeError('HTTPINVALIDTOKEN', field); } if (common._checkInvalidHeaderChar(value) === true) { - throw new TypeError('The trailer content contains invalid characters'); + throw new errors.TypeError('HTTPINVALIDTRAILER'); } this._trailer += field + ': ' + escapeHeaderValue(value) + CRLF; } @@ -539,7 +536,7 @@ OutgoingMessage.prototype.end = function(data, encoding, callback) { } if (data && typeof data !== 'string' && !(data instanceof Buffer)) { - throw new TypeError('First argument must be a string or Buffer'); + throw new errors.TypeError('INVALIDARG', 'data', ['Buffer', 'string']); } if (this.finished) { diff --git a/lib/_http_server.js b/lib/_http_server.js index d0f0fbe5d5bb38..25a3384a2edf7d 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -188,8 +188,10 @@ ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { } statusCode |= 0; - if (statusCode < 100 || statusCode > 999) - throw new RangeError(`Invalid status code: ${statusCode}`); + if (statusCode < 100 || statusCode > 999) { + const errors = require('internal/errors'); + throw new errors.RangeError('HTTPINVALIDSTATUS', statusCode); + } var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' + this.statusMessage + CRLF; diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index afe3b3baf065c2..4eb90161702a0a 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -12,6 +12,25 @@ var StringDecoder; util.inherits(Readable, Stream); +// This does not use the internal/errors so that it can remain +// portable with the readable-streams module. +function typeError(code, args) { + const assert = require('assert'); + var msg; + switch (code) { + case 'EINVALIDCHUNK': + msg = 'Invalid non-string/buffer chunk'; + break; + default: + assert.fail(null, null, 'Unknown error code.'); + } + var error = new TypeError(msg); + Error.captureStackTrace(error, typeError); + error.code = error.errno = code; + error.name = 'TypeError[' + code + ']'; + return error; +} + const hasPrependListener = typeof EE.prototype.prependListener === 'function'; function prependListener(emitter, event, fn) { @@ -396,7 +415,7 @@ function chunkInvalid(state, chunk) { chunk !== null && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); + er = new typeError('EINVALIDCHUNK'); } return er; } diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 76c4972d405c81..bf6426f445a636 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -16,6 +16,32 @@ util.inherits(Writable, Stream); function nop() {} +// This does not use the internal/errors so that it can remain +// portable with the readable-streams module. +function typeError(code, args) { + const assert = require('assert'); + var msg; + switch (code) { + case 'EWRITENULL': + msg = 'May not write null values to stream.'; + break; + case 'EINVALIDCHUNK': + msg = 'Invalid non-string/buffer chunk'; + break; + case 'UNKNOWNENCODING': + assert(args && args[0]); + msg = 'Unknown encoding: ' + args[0]; + break; + default: + assert.fail(null, null, 'Unknown error code.'); + } + var error = new TypeError(msg); + Error.captureStackTrace(error, typeError); + error.code = error.errno = code; + error.name = 'TypeError[' + code + ']'; + return error; +} + function WriteReq(chunk, encoding, cb) { this.chunk = chunk; this.encoding = encoding; @@ -181,12 +207,12 @@ function validChunk(stream, state, chunk, cb) { // if we are not in object mode then throw // if it is not a buffer, string, or undefined. if (chunk === null) { - er = new TypeError('May not write null values to stream'); + er = new typeError('EWRITENULL'); } else if (!(chunk instanceof Buffer) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); + er = new typeError('EINVALIDCHUNK'); } if (er) { stream.emit('error', er); @@ -249,7 +275,7 @@ Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { if (typeof encoding === 'string') encoding = encoding.toLowerCase(); if (!Buffer.isEncoding(encoding)) - throw new TypeError('Unknown encoding: ' + encoding); + throw new typeError('UNKNOWNENCODING', [encoding]); this._writableState.defaultEncoding = encoding; return this; }; diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 029d9ac26fe6bd..4fb9021829438b 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -762,7 +762,8 @@ function Server(/* [options], listener */) { var timeout = options.handshakeTimeout || (120 * 1000); if (typeof timeout !== 'number') { - throw new TypeError('handshakeTimeout must be a number'); + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDOPT', 'handshakeTimeout', 'number'); } if (self.sessionTimeout) { diff --git a/lib/assert.js b/lib/assert.js index 8955aa8761d7c2..1ac9a6e403b317 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -326,7 +326,8 @@ function _throws(shouldThrow, block, expected, message) { var actual; if (typeof block !== 'function') { - throw new TypeError('"block" argument must be a function'); + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'block', 'function'); } if (typeof expected === 'string') { diff --git a/lib/buffer.js b/lib/buffer.js index 515f841fd36507..8dec15ab3bf7c7 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -9,9 +9,6 @@ exports.SlowBuffer = SlowBuffer; exports.INSPECT_MAX_BYTES = 50; exports.kMaxLength = binding.kMaxLength; -const kFromErrorMsg = 'First argument must be a string, Buffer, ' + - 'ArrayBuffer, Array, or array-like object.'; - Buffer.poolSize = 8 * 1024; var poolSize, poolOffset, allocPool; @@ -32,8 +29,10 @@ Buffer.prototype.swap16 = function swap16() { // do the swap in javascript. For larger buffers, // dropping down to the native code is faster. const len = this.length; - if (len % 2 !== 0) - throw new RangeError('Buffer size must be a multiple of 16-bits'); + if (len % 2 !== 0) { + const errors = require('internal/errors'); + throw new errors.RangeError('BUFFERSWAP16'); + } if (len < 512) { for (var i = 0; i < len; i += 2) swap(this, i, i + 1); @@ -47,8 +46,10 @@ Buffer.prototype.swap32 = function swap32() { // do the swap in javascript. For larger buffers, // dropping down to the native code is faster. const len = this.length; - if (len % 4 !== 0) - throw new RangeError('Buffer size must be a multiple of 32-bits'); + if (len % 4 !== 0) { + const errors = require('internal/errors'); + throw new errors.RangeError('BUFFERSWAP32'); + } if (len < 1024) { for (var i = 0; i < len; i += 4) { swap(this, i, i + 3); @@ -121,8 +122,10 @@ function Buffer(arg, encodingOrOffset, length) { * Buffer.from(arrayBuffer[, byteOffset[, length]]) **/ Buffer.from = function(value, encodingOrOffset, length) { - if (typeof value === 'number') - throw new TypeError('"value" argument must not be a number'); + if (typeof value === 'number') { + const errors = require('internal/errors'); + throw new errors.TypeError('ARGNOTNUM', 'value'); + } if (value instanceof ArrayBuffer) return fromArrayBuffer(value, encodingOrOffset, length); @@ -138,7 +141,8 @@ Object.setPrototypeOf(Buffer, Uint8Array); function assertSize(size) { if (typeof size !== 'number') { - const err = new TypeError('"size" argument must be a number'); + const errors = require('internal/errors'); + const err = new errors.TypeError('INVALIDARG', 'size', 'number'); // The following hides the 'assertSize' method from the // callstack. This is done simply to hide the internal // details of the implementation from bleeding out to users. @@ -222,8 +226,10 @@ function fromString(string, encoding) { if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8'; - if (!Buffer.isEncoding(encoding)) - throw new TypeError('"encoding" must be a valid string encoding'); + if (!Buffer.isEncoding(encoding)) { + const errors = require('internal/errors'); + throw new errors.TypeError('UNKNOWNENCODING', encoding); + } var length = byteLength(string, encoding); @@ -284,7 +290,10 @@ function fromObject(obj) { } } - throw new TypeError(kFromErrorMsg); + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 1, + ['Buffer', 'ArrayBuffer', 'Array', + 'array-like object']); } @@ -296,9 +305,13 @@ Buffer.isBuffer = function isBuffer(b) { Buffer.compare = function compare(a, b) { - if (!(a instanceof Buffer) || - !(b instanceof Buffer)) { - throw new TypeError('Arguments must be Buffers'); + if (!(a instanceof Buffer)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'a', 'Buffer'); + } + if (!(b instanceof Buffer)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'b', 'Buffer'); } if (a === b) { @@ -337,8 +350,10 @@ Buffer.isEncoding = function(encoding) { Buffer.concat = function(list, length) { var i; - if (!Array.isArray(list)) - throw new TypeError('"list" argument must be an Array of Buffers'); + if (!Array.isArray(list)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'list', 'Array of Buffers'); + } if (list.length === 0) return Buffer.alloc(0); @@ -355,8 +370,10 @@ Buffer.concat = function(list, length) { var pos = 0; for (i = 0; i < list.length; i++) { var buf = list[i]; - if (!Buffer.isBuffer(buf)) - throw new TypeError('"list" argument must be an Array of Buffers'); + if (!Buffer.isBuffer(buf)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'list', 'Array of Buffers'); + } buf.copy(buffer, pos); pos += buf.length; } @@ -508,8 +525,10 @@ function slowToString(encoding, start, end) { return this.ucs2Slice(start, end); default: - if (loweredCase) - throw new TypeError('Unknown encoding: ' + encoding); + if (loweredCase) { + const errors = require('internal/errors'); + throw new errors.TypeError('UNKNOWNENCODING', encoding); + } encoding = (encoding + '').toLowerCase(); loweredCase = true; } @@ -531,8 +550,10 @@ Buffer.prototype.toString = function() { Buffer.prototype.equals = function equals(b) { - if (!(b instanceof Buffer)) - throw new TypeError('Argument must be a Buffer'); + if (!(b instanceof Buffer)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 1, 'Buffer'); + } if (this === b) return true; @@ -559,8 +580,10 @@ Buffer.prototype.compare = function compare(target, thisStart, thisEnd) { - if (!(target instanceof Buffer)) - throw new TypeError('Argument must be a Buffer'); + if (!(target instanceof Buffer)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'target', 'Buffer'); + } if (start === undefined) start = 0; @@ -575,7 +598,8 @@ Buffer.prototype.compare = function compare(target, end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index'); + const errors = require('internal/errors'); + throw new errors.RangeError('INDEXOUTOFRANGE'); } if (thisStart >= thisEnd && start >= end) @@ -630,7 +654,9 @@ function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { return binding.indexOfNumber(buffer, val, byteOffset, dir); } - throw new TypeError('"val" argument must be string, number or Buffer'); + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'val', + ['Buffer', 'string', 'number']); } @@ -655,7 +681,8 @@ function slowIndexOf(buffer, val, byteOffset, encoding, dir) { default: if (loweredCase) { - throw new TypeError('Unknown encoding: ' + encoding); + const errors = require('internal/errors'); + throw new errors.TypeError('UNKNOWNENCODING', encoding); } encoding = ('' + encoding).toLowerCase(); @@ -706,10 +733,12 @@ Buffer.prototype.fill = function fill(val, start, end, encoding) { val = 0; } if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string'); + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'encoding', 'string'); } if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding); + const errors = require('internal/errors'); + throw new errors.TypeError('UNKNOWNENCODING', encoding); } } else if (typeof val === 'number') { @@ -717,8 +746,10 @@ Buffer.prototype.fill = function fill(val, start, end, encoding) { } // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || end > this.length) - throw new RangeError('Out of range index'); + if (start < 0 || end > this.length) { + const errors = require('internal/errors'); + throw new errors.RangeError('INDEXOUTOFRANGE'); + } if (end <= start) return this; @@ -768,8 +799,10 @@ Buffer.prototype.write = function(string, offset, length, encoding) { if (length === undefined || length > remaining) length = remaining; - if (string.length > 0 && (length < 0 || offset < 0)) - throw new RangeError('Attempt to write outside buffer bounds'); + if (string.length > 0 && (length < 0 || offset < 0)) { + const errors = require('internal/errors'); + throw new errors.RangeError('WRITEOUTOFBOUNDS'); + } if (!encoding) encoding = 'utf8'; @@ -801,8 +834,10 @@ Buffer.prototype.write = function(string, offset, length, encoding) { return this.ucs2Write(string, offset, length); default: - if (loweredCase) - throw new TypeError('Unknown encoding: ' + encoding); + if (loweredCase) { + const errors = require('internal/errors'); + throw new errors.TypeError('UNKNOWNENCODING', encoding); + } encoding = ('' + encoding).toLowerCase(); loweredCase = true; } @@ -826,8 +861,10 @@ Buffer.prototype.slice = function slice(start, end) { function checkOffset(offset, ext, length) { - if (offset + ext > length) - throw new RangeError('Index out of range'); + if (offset + ext > length) { + const errors = require('internal/errors'); + throw new errors.RangeError('INDEXOUTOFRANGE'); + } } @@ -1034,12 +1071,18 @@ Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { function checkInt(buffer, value, offset, ext, max, min) { - if (!(buffer instanceof Buffer)) - throw new TypeError('"buffer" argument must be a Buffer instance'); - if (value > max || value < min) - throw new TypeError('"value" argument is out of bounds'); - if (offset + ext > buffer.length) - throw new RangeError('Index out of range'); + if (!(buffer instanceof Buffer)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'buffer', 'Buffer'); + } + if (value > max || value < min) { + const errors = require('internal/errors'); + throw new errors.TypeError('OUTOFBOUNDSARG', 'value'); + } + if (offset + ext > buffer.length) { + const errors = require('internal/errors'); + throw new errors.RangeError('INDEXOUTOFRANGE'); + } } diff --git a/lib/child_process.js b/lib/child_process.js index 81fb8c1fcd0fe8..504b35aee2d460 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const util = require('util'); const internalUtil = require('internal/util'); const debug = util.debuglog('child_process'); @@ -24,7 +25,7 @@ exports.fork = function(modulePath /*, args, options*/) { args = arguments[1]; options = util._extend({}, arguments[2]); } else if (arguments[1] && typeof arguments[1] !== 'object') { - throw new TypeError('Incorrect value of args option'); + throw new errors.TypeError('INVALIDARG', 'args', 'Array'); } else { args = []; options = util._extend({}, arguments[1]); @@ -133,7 +134,7 @@ exports.execFile = function(file /*, args, options, callback*/) { } if (pos === 1 && arguments.length > 1) { - throw new TypeError('Incorrect value of args option'); + throw new errors.TypeError('INVALIDARG', 'args', 'Array'); } var child = spawn(file, args, { @@ -309,7 +310,7 @@ function normalizeSpawnArguments(file /*, args, options*/) { options = arguments[2]; } else if (arguments[1] !== undefined && (arguments[1] === null || typeof arguments[1] !== 'object')) { - throw new TypeError('Incorrect value of args option'); + throw new errors.TypeError('INVALIDARG', 'args', 'Array'); } else { args = []; options = arguments[1]; @@ -318,7 +319,7 @@ function normalizeSpawnArguments(file /*, args, options*/) { if (options === undefined) options = {}; else if (options === null || typeof options !== 'object') - throw new TypeError('"options" argument must be an object'); + throw new errors.TypeError('INVALIDARG', 'options', 'object'); // Make a shallow copy so we don't clobber the user's options object. options = Object.assign({}, options); @@ -419,15 +420,15 @@ function spawnSync(/*file, args, options*/) { var input = options.stdio[i] && options.stdio[i].input; if (input != null) { var pipe = options.stdio[i] = util._extend({}, options.stdio[i]); - if (Buffer.isBuffer(input)) + if (Buffer.isBuffer(input)) { pipe.input = input; - else if (typeof input === 'string') + } else if (typeof input === 'string') { pipe.input = Buffer.from(input, options.encoding); - else - throw new TypeError(util.format( - 'stdio[%d] should be Buffer or string not %s', - i, - typeof input)); + } else { + throw new errors.TypeError('INVALIDOPT', + `stdio[${i}]`, + ['Buffer', 'string']); + } } } diff --git a/lib/console.js b/lib/console.js index 2359cb36e011d0..1556d9cf084e08 100644 --- a/lib/console.js +++ b/lib/console.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const util = require('util'); function Console(stdout, stderr) { @@ -7,12 +8,12 @@ function Console(stdout, stderr) { return new Console(stdout, stderr); } if (!stdout || typeof stdout.write !== 'function') { - throw new TypeError('Console expects a writable stream instance'); + throw new errors.TypeError('CONSOLEWRITABLE'); } if (!stderr) { stderr = stdout; } else if (typeof stderr.write !== 'function') { - throw new TypeError('Console expects writable stream instances'); + throw new errors.TypeError('CONSOLEWRITABLE'); } var prop = { diff --git a/lib/crypto.js b/lib/crypto.js index 688ac34e4ad767..58f5467e919233 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -3,6 +3,7 @@ 'use strict'; +const errors = require('internal/errors'); const internalUtil = require('internal/util'); internalUtil.assertCrypto(exports); @@ -341,7 +342,8 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (!(sizeOrKey instanceof Buffer) && typeof sizeOrKey !== 'number' && typeof sizeOrKey !== 'string') - throw new TypeError('First argument should be number, string or Buffer'); + throw new errors.TypeError('INVALIDARG', 1, + ['Buffer', 'string', 'number']); if (keyEncoding) { if (typeof keyEncoding !== 'string' || @@ -483,7 +485,7 @@ DiffieHellman.prototype.setPrivateKey = function(key, encoding) { function ECDH(curve) { if (typeof curve !== 'string') - throw new TypeError('"curve" argument should be a string'); + throw new errors.TypeError('INVALIDARG', 'curve', 'string'); this._handle = new binding.ECDH(curve); } @@ -516,7 +518,7 @@ ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { else if (format === 'uncompressed') f = constants.POINT_CONVERSION_UNCOMPRESSED; else - throw new TypeError('Bad format: ' + format); + throw new errors.TypeError('INVALIDARGVALUE', 'format', format); } else { f = constants.POINT_CONVERSION_UNCOMPRESSED; } @@ -609,10 +611,10 @@ Certificate.prototype.exportChallenge = function(object, encoding) { exports.setEngine = function setEngine(id, flags) { if (typeof id !== 'string') - throw new TypeError('"id" argument should be a string'); + throw new errors.TypeError('INVALIDARG', 'id', 'string'); if (flags && typeof flags !== 'number') - throw new TypeError('"flags" argument should be a number, if present'); + throw new errors.TypeError('INVALIDARG', 'flags', 'number'); flags = flags >>> 0; // Use provided engine for everything by default diff --git a/lib/dgram.js b/lib/dgram.js index 4473282148fdff..cce448c3a1b2cf 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const assert = require('assert'); const Buffer = require('buffer').Buffer; const util = require('util'); @@ -254,7 +255,7 @@ function sliceBuffer(buffer, offset, length) { if (typeof buffer === 'string') buffer = Buffer.from(buffer); else if (!(buffer instanceof Buffer)) - throw new TypeError('First argument must be a buffer or string'); + throw new errors.TypeError('INVALIDARG', 'buffer', ['Buffer', 'string']); offset = offset >>> 0; length = length >>> 0; @@ -320,17 +321,18 @@ Socket.prototype.send = function(buffer, if (typeof buffer === 'string') { buffer = [ Buffer.from(buffer) ]; } else if (!(buffer instanceof Buffer)) { - throw new TypeError('First argument must be a buffer or a string'); + throw new errors.TypeError('INVALIDARG', 'buffer', + ['Buffer', 'string', 'Array']); } else { buffer = [ buffer ]; } } else if (!fixBuffer(buffer)) { - throw new TypeError('Buffer list arguments must be buffers or strings'); + throw new errors.TypeError('BUFFERLIST'); } port = port >>> 0; if (port === 0 || port > 65535) - throw new RangeError('Port should be > 0 and < 65536'); + throw new errors.RangeError('PORTRANGE'); // Normalize callback so it's either a function or undefined but not anything // else. @@ -441,7 +443,7 @@ Socket.prototype.setBroadcast = function(arg) { Socket.prototype.setTTL = function(arg) { if (typeof arg !== 'number') { - throw new TypeError('Argument must be a number'); + throw new errors.TypeError('INVALIDARG', 'arg', 'number'); } var err = this._handle.setTTL(arg); @@ -455,7 +457,7 @@ Socket.prototype.setTTL = function(arg) { Socket.prototype.setMulticastTTL = function(arg) { if (typeof arg !== 'number') { - throw new TypeError('Argument must be a number'); + throw new errors.TypeError('INVALIDARG', 'arg', 'number'); } var err = this._handle.setMulticastTTL(arg); diff --git a/lib/dns.js b/lib/dns.js index 8d1541718abf75..8b41f30e853c5f 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -1,6 +1,7 @@ 'use strict'; const util = require('util'); +const errors = require('internal/errors'); const cares = process.binding('cares_wrap'); const uv = process.binding('uv'); @@ -112,13 +113,12 @@ exports.lookup = function lookup(hostname, options, callback) { // Parse arguments if (hostname && typeof hostname !== 'string') { - throw new TypeError('Invalid arguments: ' + - 'hostname must be a string or falsey'); + throw new errors.TypeError('INVALIDARG', 'hostname', ['string', 'falsy']); } else if (typeof options === 'function') { callback = options; family = 0; } else if (typeof callback !== 'function') { - throw new TypeError('Invalid arguments: callback must be passed'); + throw new errors.TypeError('CALLBACKREQUIRED'); } else if (options !== null && typeof options === 'object') { hints = options.hints >>> 0; family = options.family >>> 0; @@ -128,14 +128,15 @@ exports.lookup = function lookup(hostname, options, callback) { hints !== exports.ADDRCONFIG && hints !== exports.V4MAPPED && hints !== (exports.ADDRCONFIG | exports.V4MAPPED)) { - throw new TypeError('Invalid argument: hints must use valid flags'); + throw new errors.TypeError('INVALIDOPTVALUE', 'hints'); } } else { family = options >>> 0; } - if (family !== 0 && family !== 4 && family !== 6) - throw new TypeError('Invalid argument: family must be 4 or 6'); + if (family !== 0 && family !== 4 && family !== 6) { + throw new errors.TypeError('INVALIDFAMILY'); + } callback = makeAsync(callback); @@ -189,10 +190,10 @@ exports.lookupService = function(host, port, callback) { throw new Error('Invalid arguments'); if (isIP(host) === 0) - throw new TypeError('"host" argument needs to be a valid IP address'); + throw new errors.TypeError('INVALIDARG', 'host', 'valid IP address'); if (port == null || !isLegalPort(port)) - throw new TypeError(`"port" should be >= 0 and < 65536, got "${port}"`); + throw new errors.TypeError('PORTRANGE'); port = +port; callback = makeAsync(callback); diff --git a/lib/events.js b/lib/events.js index f0b323e15c1751..8effd8678bd148 100644 --- a/lib/events.js +++ b/lib/events.js @@ -60,8 +60,10 @@ EventEmitter.init = function() { // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || isNaN(n)) - throw new TypeError('"n" argument must be a positive number'); + if (typeof n !== 'number' || n < 0 || isNaN(n)) { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'n', 'positive number'); + } this._maxListeners = n; return this; }; @@ -212,8 +214,10 @@ function _addListener(target, type, listener, prepend) { var events; var existing; - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'listener', 'function'); + } events = target._events; if (!events) { @@ -291,16 +295,20 @@ function _onceWrap(target, type, listener) { } EventEmitter.prototype.once = function once(type, listener) { - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'listener', 'function'); + } this.on(type, _onceWrap(this, type, listener)); return this; }; EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) { - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'listener', 'function'); + } this.prependListener(type, _onceWrap(this, type, listener)); return this; }; @@ -310,8 +318,10 @@ EventEmitter.prototype.removeListener = function removeListener(type, listener) { var list, events, position, i, originalListener; - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'listener', 'function'); + } events = this._events; if (!events) diff --git a/lib/fs.js b/lib/fs.js index e441746366e729..e0167d947c15c9 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -3,6 +3,7 @@ 'use strict'; +const errors = require('internal/errors'); const util = require('util'); const pathModule = require('path'); @@ -72,8 +73,7 @@ try { } function throwOptionsError(options) { - throw new TypeError('Expected options to be either an object or a string, ' + - 'but got ' + typeof options + ' instead'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); } function rethrow() { @@ -110,7 +110,7 @@ function makeCallback(cb) { } if (typeof cb !== 'function') { - throw new TypeError('"callback" argument must be a function'); + throw new errors.TypeError('CALLBACKREQUIRED'); } return function() { @@ -219,7 +219,7 @@ fs.access = function(path, mode, callback) { callback = mode; mode = fs.F_OK; } else if (typeof callback !== 'function') { - throw new TypeError('"callback" argument must be a function'); + throw new errors.TypeError('CALLBACKREQUIRED'); } if (!nullCheck(path, callback)) @@ -383,9 +383,7 @@ function readFileAfterStat(err, st) { } if (size > kMaxLength) { - err = new RangeError('File size is greater than possible Buffer: ' + - `0x${kMaxLength.toString(16)} bytes`); - return context.close(err); + return context.close(new errors.RangeError('FILETOOBIG', kMaxLength)); } context.buffer = Buffer.allocUnsafeSlow(size); @@ -926,7 +924,7 @@ fs.readdir = function(path, options, callback) { options = {encoding: options}; } if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); callback = makeCallback(callback); if (!nullCheck(path, callback)) return; @@ -940,7 +938,7 @@ fs.readdirSync = function(path, options) { if (typeof options === 'string') options = {encoding: options}; if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); nullCheck(path); return binding.readdir(pathModule._makeLong(path), options.encoding); }; @@ -990,7 +988,7 @@ fs.readlink = function(path, options, callback) { options = {encoding: options}; } if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); callback = makeCallback(callback); if (!nullCheck(path, callback)) return; var req = new FSReqWrap(); @@ -1003,7 +1001,7 @@ fs.readlinkSync = function(path, options) { if (typeof options === 'string') options = {encoding: options}; if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); nullCheck(path); return binding.readlink(pathModule._makeLong(path), options.encoding); }; @@ -1441,7 +1439,7 @@ fs.watch = function(filename, options, listener) { options = {encoding: options}; } if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); if (options.persistent === undefined) options.persistent = true; if (options.recursive === undefined) options.recursive = false; @@ -1563,7 +1561,7 @@ fs.realpathSync = function realpathSync(path, options) { else if (typeof options === 'string') options = {encoding: options}; else if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); nullCheck(path); return binding.realpath(pathModule._makeLong(path), options.encoding); }; @@ -1578,7 +1576,7 @@ fs.realpath = function realpath(path, options, callback) { } else if (typeof options === 'string') { options = {encoding: options}; } else if (typeof options !== 'object') { - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); } callback = makeCallback(callback); if (!nullCheck(path, callback)) @@ -1614,7 +1612,7 @@ function ReadStream(path, options) { else if (typeof options === 'string') options = { encoding: options }; else if (options === null || typeof options !== 'object') - throw new TypeError('"options" argument must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); // a little bit bigger buffer and water marks by default options = Object.create(options); @@ -1635,16 +1633,17 @@ function ReadStream(path, options) { if (this.start !== undefined) { if (typeof this.start !== 'number') { - throw new TypeError('"start" option must be a Number'); + throw new errors.TypeError('INVALIDOPT', 'start', 'number'); } if (this.end === undefined) { this.end = Infinity; } else if (typeof this.end !== 'number') { - throw new TypeError('"end" option must be a Number'); + throw new errors.TypeError('INVALIDOPT', 'end', 'number'); } if (this.start > this.end) { - throw new Error('"start" option must be <= "end" option'); + throw new errors.Error('INVALIDOPT', 'start', + 'number <= the "end" option'); } this.pos = this.start; @@ -1785,7 +1784,7 @@ function WriteStream(path, options) { else if (typeof options === 'string') options = { encoding: options }; else if (options === null || typeof options !== 'object') - throw new TypeError('"options" argument must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); options = Object.create(options); @@ -1803,10 +1802,10 @@ function WriteStream(path, options) { if (this.start !== undefined) { if (typeof this.start !== 'number') { - throw new TypeError('"start" option must be a Number'); + throw new errors.TypeError('INVALIDOPT', 'start', 'number'); } if (this.start < 0) { - throw new Error('"start" must be >= zero'); + throw new errors.Error('INVALIDOPT', 'start', 'number >= zero'); } this.pos = this.start; @@ -1996,7 +1995,7 @@ SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy; fs.mkdtemp = function(prefix, options, callback) { if (!prefix || typeof prefix !== 'string') - throw new TypeError('filename prefix is required'); + throw new errors.TypeError('REQUIREDARG', 'prefix'); options = options || {}; if (typeof options === 'function') { @@ -2006,7 +2005,7 @@ fs.mkdtemp = function(prefix, options, callback) { options = {encoding: options}; } if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); if (!nullCheck(prefix, callback)) { return; @@ -2020,13 +2019,13 @@ fs.mkdtemp = function(prefix, options, callback) { fs.mkdtempSync = function(prefix, options) { if (!prefix || typeof prefix !== 'string') - throw new TypeError('filename prefix is required'); + throw new errors.TypeError('REQUIREDARG', 'prefix'); options = options || {}; if (typeof options === 'string') options = {encoding: options}; if (typeof options !== 'object') - throw new TypeError('"options" must be a string or an object'); + throw new errors.TypeError('INVALIDARG', 'options', ['Object', 'string']); nullCheck(prefix); return binding.mkdtemp(prefix + 'XXXXXX', options.encoding); diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 7143ff2720d4cb..6931ca7b1e01f9 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -369,7 +369,13 @@ } if (!NativeModule.exists(id)) { - throw new Error(`No such native module ${id}`); + const err = new Error(`No such native module ${id}`); + // Cannot depend on internal/errors here, set manually. + // Note, these are not set as read only accessors whereas + // in internal/errors they are set to be read only. + err.name = 'Error[ENOMODULE]'; + err.code = err.errno = 'ENOMODULE'; + throw err; } process.moduleLoadList.push(`NativeModule ${id}`); diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index ff26e8b75ab58c..00f7423898a2d4 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const StringDecoder = require('string_decoder').StringDecoder; const Buffer = require('buffer').Buffer; const EventEmitter = require('events'); @@ -361,7 +362,7 @@ ChildProcess.prototype.kill = function(sig) { } if (signal === undefined) { - throw new Error('Unknown signal: ' + sig); + throw new errors.Error('UNKNOWNSIGNAL', sig); } if (this._handle) { @@ -512,7 +513,7 @@ function setupChannel(target, channel) { options = undefined; } else if (options !== undefined && (options === null || typeof options !== 'object')) { - throw new TypeError('"options" argument must be an object'); + throw new errors.TypeError('INVALIDARG', 'options', 'object'); } options = Object.assign({swallowErrors: false}, options); @@ -520,7 +521,7 @@ function setupChannel(target, channel) { if (this.connected) { return this._send(message, handle, options, callback); } - const ex = new Error('channel closed'); + const ex = new errors.Error('CHANNELCLOSED'); if (typeof callback === 'function') { process.nextTick(callback, ex); } else { @@ -533,7 +534,7 @@ function setupChannel(target, channel) { assert(this.connected || this._channel); if (message === undefined) - throw new TypeError('"message" argument cannot be undefined'); + throw new errors.TypeError('REQUIREDARG', 'message'); // Support legacy function signature if (typeof options === 'boolean') { @@ -560,7 +561,7 @@ function setupChannel(target, channel) { } else if (handle instanceof UDP) { message.type = 'dgram.Native'; } else { - throw new TypeError('This handle type can\'t be sent'); + throw new errors.TypeError('IPCBADHANDLE'); } // Queue-up message and handle if we haven't received ACK yet. @@ -657,7 +658,7 @@ function setupChannel(target, channel) { target.disconnect = function() { if (!this.connected) { - this.emit('error', new Error('IPC channel is already disconnected')); + this.emit('error', new errors.Error('IPCDISCONNECTED')); return; } @@ -730,11 +731,12 @@ function _validateStdio(stdio, sync) { case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break; case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break; case 'inherit': stdio = [0, 1, 2]; break; - default: throw new TypeError('Incorrect value of stdio option: ' + stdio); + default: throw new errors.TypeError('INVALIDOPTVALUE', 'stdio', stdio); } } else if (!Array.isArray(stdio)) { - throw new TypeError('Incorrect value of stdio option: ' + - util.inspect(stdio)); + throw new errors.TypeError('INVALIDOPTVALUE', + 'stdio', + util.inspect(stdio)); } // At least 3 stdio will be created @@ -778,9 +780,9 @@ function _validateStdio(stdio, sync) { // Cleanup previously created pipes cleanup(); if (!sync) - throw new Error('Child process can have only one IPC pipe'); + throw new errors.Error('IPCONEIPC'); else - throw new Error('You cannot use IPC with synchronous forks'); + throw new errors.Error('IPCNOSYNCFORK'); } ipc = new Pipe(true); @@ -815,14 +817,14 @@ function _validateStdio(stdio, sync) { } else if (stdio instanceof Buffer || typeof stdio === 'string') { if (!sync) { cleanup(); - throw new TypeError('Asynchronous forks do not support Buffer input: ' + - util.inspect(stdio)); + throw new errors.TypeError('IPCBUFFERASYNCFORK', util.inspect(stdio)); } } else { // Cleanup cleanup(); - throw new TypeError('Incorrect value for stdio stream: ' + - util.inspect(stdio)); + throw new errors.TypeError('INVALIDOPTVALUE', + 'stdio', + util.inspect(stdio)); } return acc; diff --git a/lib/internal/errors.js b/lib/internal/errors.js new file mode 100644 index 00000000000000..8a2ce91c075eb2 --- /dev/null +++ b/lib/internal/errors.js @@ -0,0 +1,282 @@ +'use strict'; + +// The whole point behind this internal module is to allow Node.js to no +// longer be forced to treat every error message change as a semver-major +// change. The NodeError classes here all expose a `code` property whose +// value identifies the error. + +// Note: the lib/_stream_readable.js and lib/_stream_writable.js files +// include their own alternatives to this because those need to be portable +// to the readable-stream standalone module. If the logic here changes at all, +// then those also need to be checked. + +const kCode = Symbol('code'); + +class NodeError extends Error { + constructor(key, ...args) { + super(message(key, args)); + this[kCode] = key; + Error.captureStackTrace(this, NodeError); + } + + get name() { + return `Error[${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } +} + +class NodeTypeError extends TypeError { + constructor(key, ...args) { + super(message(key, args)); + this[kCode] = key; + Error.captureStackTrace(this, NodeTypeError); + } + + get name() { + return `TypeError[${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } +} + +class NodeRangeError extends RangeError { + constructor(key, ... args) { + super(message(key, args)); + this[kCode] = key; + Error.captureStackTrace(this, NodeRangeError); + } + + get name() { + return `RangeError[${this[kCode]}]`; + } + + get code() { + return this[kCode]; + } +} + +exports.Error = NodeError; +exports.TypeError = NodeTypeError; +exports.RangeError = NodeRangeError; + +var assert, util; +function lazyAssert() { + if (!assert) + assert = require('assert'); + return assert; +} + +function lazyUtil() { + if (!util) + util = require('util'); + return util; +} + +function message(key, args) { + const assert = lazyAssert(); + const util = lazyUtil(); + const msg = Error[Symbol.for(key)]; + // We want to assert here because if this happens, + // Node.js is doing something incorrect. + assert(msg, `An invalid error message key was used: ${key}.`); + let fmt = util.format; + if (typeof msg === 'function') { + fmt = msg; + } else { + args.unshift(msg); + } + const res = fmt.apply(null, args); + // We want to assert here because if this happens, + // Node.js is doing something incorrect. + assert(res, 'An invalid formatted error message was returned.'); + return res; +} + +// Below is the catalog of messages +// Error keys should be all caps, value can +// be a string formatted for use with +// util.format, or a function. + +// Utility function for registering the error codes. +function E(sym, val) { + Error[Symbol.for(sym)] = val; +} + +E('ARGNOTNUM', argNotNum); +E('ASSERTIONERROR', (msg) => msg); +E('BUFFERLIST', + '"buffer" list argument must contain only Buffers or strings'); +E('BUFFERSWAP16', 'Buffer size must be a multiple of 16-bits'); +E('BUFFERSWAP32', 'Buffer size must be a multiple of 32-bits'); +E('BUFFERTOOLARGE', bufferTooBig); +E('CALLBACKREQUIRED', 'The required "callback" argument is not a function'); +E('CHANNELCLOSED', 'Channel closed'); +E('CLOSEDBEFOREREPLY', 'Slave closed before reply'); +E('CONSOLEWRITABLE', 'Console expects a writable stream instance'); +E('DNSUNKNOWNDOMAIN', 'Unable to determine the domain name'); +E('ECONNRESET', 'Socket hang up'); +E('FILETOOBIG', fileTooBig); +E('HEADERSSENT', + 'Unable to set additional headers after they are already sent'); +E('HTTPINVALIDHEADER', 'The header content contains invalid characters'); +E('HTTPINVALIDPATH', 'Request path contains unescaped characters'); +E('HTTPINVALIDSTATUS', (status) => `Invalid status code: ${status}`); +E('HTTPINVALIDTOKEN', (val) => `Invalid HTTP token: ${val}`); +E('HTTPINVALIDTRAILER', 'The trailer content contains invalid characters'); +E('HTTPMETHODINVALID', 'Method must be a valid HTTP token'); +E('HTTPUNSUPPORTEDPROTOCOL', unsupportedProtocol); +E('INDEXOUTOFRANGE', 'Index out of range'); +E('INVALIDARG', invalidArgument); +E('INVALIDARGVALUE', invalidArgumentValue); +E('INVALIDFAMILY', 'IP protocol family must be either 4 or 6'); +E('INVALIDOPT', invalidOption); +E('INVALIDOPTVALUE', invalidOptionValue); +E('INVALIDPID', 'Invalid pid'); +E('IPCBADHANDLE', 'Handles of this type cannot be sent'); +E('IPCBUFFERASYNCFORK', + (stdio) => `Asynchronous forks do not support Buffer input: ${stdio}`); +E('IPCDISCONNECTED', 'The IPC channel is already disconnected'); +E('IPCNOSYNCFORK', 'IPC cannot be used with synchronous forks'); +E('IPCONEIPC', 'Child process can have only one IPC pipe'); +E('NOCPUUSAGE', (err) => `Unable to obtain CPU Usage: ${err}`); +E('NOCRYPTO', 'Node.js is not compiled with openssl crypto support'); +E('NOTIMPLEMENTED', notImplemented); +E('OUTOFBOUNDSARG', (arg) => `"${arg}" argument is out of bounds`); +E('PORTRANGE', '"port" argument must be a number >= 0 and < 65536'); +E('PROTOTYPEREQUIRED', + 'The super constructor to "inherits" must have a prototype'); +E('REPLHISTORYPARSE', (path) => `Could not parse history data in ${path}`); +E('REQUIREDARG', requiredArgument); +E('STDERRCLOSE', 'process.stderr cannot be closed'); +E('STDOUTCLOSE', 'process.stdout cannot be closed'); +E('UNKNOWNENCODING', (enc) => `Unknown encoding: ${enc}`); +E('UNKNOWNSIGNAL', (sig) => `Unknown signal: ${sig}`); +E('UNKNOWNSTATE', 'Unknown state'); +E('WRITEOUTOFBOUNDS', 'Write out of bounds'); + +// Utility methods for the messages above + +function bufferTooBig(max) { + const assert = lazyAssert(); + assert(max !== undefined); + return 'Cannot create final Buffer. It would be larger ' + + `than 0x${max.toString(16)} bytes`; +} + +function fileTooBig(arg) { + const assert = lazyAssert(); + assert(arg !== undefined); + return 'File size is greater than possible Buffer: ' + + `0x${arg.toString(16)} bytes`; +} + +function unsupportedProtocol(protocol, expected) { + let msg = `Protocol "${protocol}" is not supported`; + if (expected) { + msg += `. Expected "${expected}"`; + } + return msg; +} + +function notImplemented(additional) { + let msg = 'Not implemented'; + if (additional) + msg += `: ${additional}`; + return msg; +} + +function invalidOptionValue(opt, val) { + const assert = lazyAssert(); + assert(opt); + let msg = `Invalid value for "${opt}" option`; + if (val) + msg += `: ${val}`; + return msg; +} + +function invalidArgumentValue(arg, val) { + const assert = lazyAssert(); + assert(arg); + let msg = `Invalid value for "${arg}" argument`; + if (val) + msg += `: ${val}`; + return msg; +} + +function argNotNum(arg) { + const assert = lazyAssert(); + assert(arg); + return `"${arg}" argument must not be a number`; +} + +function requiredArgument(arg) { + const assert = lazyAssert(); + assert(arg); + return `"${arg}" argument is required and cannot be undefined`; +} + +function invalidArgument(arg, expected) { + return invalidOptionOrArgument(true, arg, expected); +} + +function invalidOption(opt, expected) { + return invalidOptionOrArgument(false, opt, expected); +} + +function formatArg(arg) { + if (typeof arg === 'number') { + arg >>>= 0; + switch (arg) { + case 1: + arg = 'The first'; + break; + case 2: + arg = 'The second'; + break; + case 3: + arg = 'The third'; + break; + default: + arg = `The ${arg}th`; + } + } else { + arg = `"${arg}"`; + } + return arg; +} + +// This takes an arg name and one or more expected types to generate an +// appropriate error message indicating that the given argument must be +// of the expected type. For instance: +// "foo" argument must be a string +// "bar" argument must be an object +// "baz" argument must be one of: string, object +// first argument must be a string +// ... and so forth. +function invalidOptionOrArgument(isargument, arg, expected) { + const assert = lazyAssert(); + const util = lazyUtil(); + assert(arg && expected); + // Is this error message for an argument or an option? + const what = isargument ? 'argument' : 'option'; + if (typeof expected === 'string') { + // Check to see if we need "a" or "an". + const ch = expected.codePointAt(0) | 0x20; + const prefix = (ch === 0x61 || ch === 0x65 || + ch === 0x69 || ch === 0x6f || + ch === 0x75) ? 'an' : 'a'; + return `${formatArg(arg)} ${what} must be ${prefix} ${expected}`; + } else if (Array.isArray(expected)) { + // Is there only a single item in the array or multiple? + return expected.length === 1 ? + invalidArgument(arg, expected[0]) : + `${formatArg(arg)} ${what} must be one of: ${expected.join(', ')}`; + } else { + assert.fail(null, null, `Unexpected value: ${util.inspect(expected)}`); + } +} diff --git a/lib/internal/net.js b/lib/internal/net.js index d19bc4c219a796..9d93ca92d67575 100644 --- a/lib/internal/net.js +++ b/lib/internal/net.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); module.exports = { isLegalPort, assertPort }; // Check that the port number is not NaN when coerced to a number, @@ -14,5 +15,5 @@ function isLegalPort(port) { function assertPort(port) { if (typeof port !== 'undefined' && !isLegalPort(port)) - throw new RangeError('"port" argument must be >= 0 and < 65536'); + throw new errors.RangeError('PORTRANGE'); } diff --git a/lib/internal/process.js b/lib/internal/process.js index c435c2e8712a03..a97b5e681c0bbc 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -9,6 +9,13 @@ function lazyConstants() { return _lazyConstants; } +var _errors = null; +function lazyErrors() { + if (!_errors) + _errors = require('internal/errors'); + return _errors; +} + exports.setup_cpuUsage = setup_cpuUsage; exports.setup_hrtime = setup_hrtime; exports.setupConfig = setupConfig; @@ -19,7 +26,10 @@ exports.setupRawDebug = setupRawDebug; const assert = process.assert = function(x, msg) { - if (!x) throw new Error(msg || 'assertion error'); + if (!x) { + const errors = lazyErrors(); + throw new errors.Error('ASSERTIONERROR', msg || 'assertion error'); + } }; @@ -37,18 +47,21 @@ function setup_cpuUsage() { // If a previous value was passed in, ensure it has the correct shape. if (prevValue) { if (!previousValueIsValid(prevValue.user)) { - throw new TypeError('value of user property of argument is invalid'); + const errors = lazyErrors(); + throw errors.TypeError('INVALIDARGVALUE', 'prevValue.user'); } if (!previousValueIsValid(prevValue.system)) { - throw new TypeError('value of system property of argument is invalid'); + const errors = lazyErrors(); + throw errors.TypeError('INVALIDARGVALUE', 'prevValue.system'); } } // Call the native function to get the current values. const errmsg = _cpuUsage(cpuValues); if (errmsg) { - throw new Error('unable to obtain CPU usage: ' + errmsg); + const errors = lazyErrors(); + throw errors.Error('NOCPUUSAGE', errmsg); } // If a previous value was passed in, return diff of current from previous. @@ -88,7 +101,8 @@ function setup_hrtime() { return [nsec < 0 ? sec - 1 : sec, nsec < 0 ? nsec + 1e9 : nsec]; } - throw new TypeError('process.hrtime() only accepts an Array tuple'); + const errors = lazyErrors(); + throw errors.TypeError('INVALIDARG', 'prevValue', 'Array'); } return [ @@ -152,7 +166,8 @@ function setupKillAndExit() { var err; if (pid != (pid | 0)) { - throw new TypeError('invalid pid'); + const errors = lazyErrors(); + throw errors.TypeError('INVALIDPID'); } // preserve null signal @@ -164,7 +179,8 @@ function setupKillAndExit() { sig.slice(0, 3) === 'SIG') { err = process._kill(pid, lazyConstants()[sig]); } else { - throw new Error(`Unknown signal: ${sig}`); + const errors = lazyErrors(); + throw errors.Error('UNKNOWNSIGNAL', sig); } } diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index 529645aa8d65c4..4c8a17489a725a 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -138,8 +138,10 @@ function setupNextTick() { } function nextTick(callback) { - if (typeof callback !== 'function') - throw new TypeError('callback is not a function'); + if (typeof callback !== 'function') { + const errors = require('internal/errors'); + throw new errors.TypeError('CALLBACKREQUIRED'); + } // on the way out, don't bother. it won't get fired anyway. if (process._exiting) return; diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js index 55689069ff2cb5..9159288dc1a349 100644 --- a/lib/internal/process/stdio.js +++ b/lib/internal/process/stdio.js @@ -2,6 +2,13 @@ exports.setup = setupStdio; +var _errors = null; +function lazyErrors() { + if (!_errors) + _errors = require('internal/errors'); + return _errors; +} + function setupStdio() { var stdin, stdout, stderr; @@ -9,7 +16,8 @@ function setupStdio() { if (stdout) return stdout; stdout = createWritableStdioStream(1); stdout.destroy = stdout.destroySoon = function(er) { - er = er || new Error('process.stdout cannot be closed.'); + const errors = lazyErrors(); + er = er || new errors.Error('STDOUTCLOSE'); stdout.emit('error', er); }; if (stdout.isTTY) { @@ -24,7 +32,8 @@ function setupStdio() { if (stderr) return stderr; stderr = createWritableStdioStream(2); stderr.destroy = stderr.destroySoon = function(er) { - er = er || new Error('process.stderr cannot be closed.'); + const errors = lazyErrors(); + er = er || new errors.Error('STDERRCLOSE'); stderr.emit('error', er); }; if (stderr.isTTY) { @@ -83,7 +92,8 @@ function setupStdio() { default: // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stdin file type!'); + const errors = lazyErrors(); + throw new errors.Error('NOTIMPLEMENTED', 'Unknown stdin file type'); } // For supporting legacy API we put the FD here. @@ -149,7 +159,8 @@ function createWritableStdioStream(fd) { default: // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stream file type!'); + const errors = lazyErrors(); + throw new errors.Error('NOTIMPLEMENTED', 'Unknown stream file type'); } // For supporting legacy API we put the FD here. diff --git a/lib/internal/process/warning.js b/lib/internal/process/warning.js index 4087eb0d2847e7..61e6b8b960e84e 100644 --- a/lib/internal/process/warning.js +++ b/lib/internal/process/warning.js @@ -39,7 +39,8 @@ function setupProcessWarnings() { Error.captureStackTrace(warning, ctor || process.emitWarning); } if (!(warning instanceof Error)) { - throw new TypeError('\'warning\' must be an Error object or string.'); + const errors = require('internal/errors'); + throw new errors.TypeError('INVALIDARG', 'warning', ['Error', 'string']); } if (throwDeprecation && warning.name === 'DeprecationWarning') throw warning; diff --git a/lib/internal/repl.js b/lib/internal/repl.js index cea681f5837494..cc6eb25108f955 100644 --- a/lib/internal/repl.js +++ b/lib/internal/repl.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const Interface = require('readline').Interface; const REPL = require('repl'); const path = require('path'); @@ -152,13 +153,14 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) { if (oldReplJSONHistory) repl.history = JSON.parse(oldReplJSONHistory); if (!Array.isArray(repl.history)) { - throw new Error('Expected array, got ' + typeof repl.history); + throw new errors.Error('INVALIDARGVALUE', + 'repl.history', + typeof repl.history); } repl.history = repl.history.slice(0, repl.historySize); } catch (err) { if (err.code !== 'ENOENT') { - return ready( - new Error(`Could not parse history data in ${oldHistoryPath}.`)); + return ready(new errors.Error('REPLHISTORYPARSE', oldHistoryPath)); } } } diff --git a/lib/internal/socket_list.js b/lib/internal/socket_list.js index 0f4be6df239dd6..cf4f648e180c23 100644 --- a/lib/internal/socket_list.js +++ b/lib/internal/socket_list.js @@ -2,6 +2,7 @@ module.exports = {SocketListSend, SocketListReceive}; +const errors = require('internal/errors'); const EventEmitter = require('events'); const util = require('util'); @@ -22,7 +23,7 @@ SocketListSend.prototype._request = function(msg, cmd, callback) { function onclose() { self.slave.removeListener('internalMessage', onreply); - callback(new Error('Slave closed before reply')); + callback(new errors.Error('CLOSEDBEFOREREPLY')); } function onreply(msg) { diff --git a/lib/internal/util.js b/lib/internal/util.js index 56398ccf9dc2fe..70ad6808be33fe 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const binding = process.binding('util'); const prefix = `(${process.release.name}:${process.pid}) `; const noDeprecation = process.noDeprecation; @@ -88,5 +89,5 @@ exports.objectToString = function objectToString(o) { const noCrypto = !process.versions.openssl; exports.assertCrypto = function(exports) { if (noCrypto) - throw new Error('Node.js is not compiled with openssl crypto support'); + throw new errors.Error('NOCRYPTO'); }; diff --git a/lib/net.js b/lib/net.js index 509112f58f2077..e0ce8846cd28fb 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const EventEmitter = require('events'); const stream = require('stream'); const timers = require('timers'); @@ -32,7 +33,7 @@ function createHandle(fd) { var type = TTYWrap.guessHandleType(fd); if (type === 'PIPE') return new Pipe(); if (type === 'TCP') return new TCP(); - throw new TypeError('Unsupported fd type: ' + type); + throw new errors.TypeError('INVALIDARGVALUE', 'fd', 'type'); } @@ -644,8 +645,7 @@ protoGetter('localPort', function localPort() { Socket.prototype.write = function(chunk, encoding, cb) { if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) { - throw new TypeError( - 'Invalid data, chunk must be a string or buffer, not ' + typeof chunk); + throw new errors.TypeError('INVALIDARG', 'chunk', ['Buffer', 'string']); } return stream.Duplex.prototype.write.apply(this, arguments); }; @@ -824,7 +824,9 @@ function connect(self, address, port, addressType, localAddress, localPort) { localAddress = localAddress || '::'; bind = self._handle.bind6; } else { - self._destroy(new TypeError('Invalid addressType: ' + addressType)); + self._destroy(new errors.TypeError('INVALIDARGVALUE', + 'addressType', + addressType)); return; } @@ -939,19 +941,20 @@ function lookupAndConnect(self, options) { var localAddress = options.localAddress; var localPort = options.localPort; - if (localAddress && !exports.isIP(localAddress)) - throw new TypeError('"localAddress" option must be a valid IP: ' + - localAddress); + if (localAddress && !exports.isIP(localAddress)) { + throw new errors.TypeError('INVALIDOPT', + 'localAddress', + 'valid IP address'); + } if (localPort && typeof localPort !== 'number') - throw new TypeError('"localPort" option should be a number: ' + localPort); + throw new errors.TypeError('INVALIDOPT', 'localPort', 'number'); if (typeof port !== 'undefined') { if (typeof port !== 'number' && typeof port !== 'string') - throw new TypeError('"port" option should be a number or string: ' + - port); + throw new errors.TypeError('INVALIDOPT', 'port', ['string', 'number']); if (!isLegalPort(port)) - throw new RangeError('"port" option should be >= 0 and < 65536: ' + port); + throw new errors.RangeError('PORTRANGE'); } port |= 0; @@ -966,7 +969,7 @@ function lookupAndConnect(self, options) { } if (options.lookup && typeof options.lookup !== 'function') - throw new TypeError('"lookup" option should be a function'); + throw new errors.TypeError('INVALIDOPT', 'lookup', 'function'); var dnsopts = { family: options.family, @@ -1108,7 +1111,7 @@ function Server(options, connectionListener) { this.on('connection', connectionListener); } } else { - throw new TypeError('options must be an object'); + throw new errors.TypeError('INVALIDARG', 'options', 'object'); } this._connections = 0; diff --git a/lib/path.js b/lib/path.js index cad86ab9a21731..1f35c98e5cbfb7 100644 --- a/lib/path.js +++ b/lib/path.js @@ -1,10 +1,10 @@ 'use strict'; -const inspect = require('util').inspect; +const errors = require('internal/errors'); function assertPath(path) { if (typeof path !== 'string') { - throw new TypeError('Path must be a string. Received ' + inspect(path)); + throw new errors.TypeError('INVALIDARG', 'path', 'string'); } } @@ -797,7 +797,7 @@ const win32 = { basename: function basename(path, ext) { if (ext !== undefined && typeof ext !== 'string') - throw new TypeError('"ext" argument must be a string'); + throw new errors.TypeError('INVALIDARG', 'ext', 'string'); assertPath(path); var start = 0; var end = -1; @@ -938,9 +938,7 @@ const win32 = { format: function format(pathObject) { if (pathObject === null || typeof pathObject !== 'object') { - throw new TypeError( - `Parameter "pathObject" must be an object, not ${typeof pathObject}` - ); + throw new errors.TypeError('INVALIDARG', 'pathObject', 'object'); } return _format('\\', pathObject); }, @@ -1351,7 +1349,7 @@ const posix = { basename: function basename(path, ext) { if (ext !== undefined && typeof ext !== 'string') - throw new TypeError('"ext" argument must be a string'); + throw new errors.TypeError('INVALIDARG', 'ext', 'string'); assertPath(path); var start = 0; @@ -1480,9 +1478,7 @@ const posix = { format: function format(pathObject) { if (pathObject === null || typeof pathObject !== 'object') { - throw new TypeError( - `Parameter "pathObject" must be an object, not ${typeof pathObject}` - ); + throw new errors.TypeError('INVALIDARG', 'pathObject', 'object'); } return _format('/', pathObject); }, diff --git a/lib/readline.js b/lib/readline.js index 57a27a97705e73..ab7635cb4d0d5f 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -8,6 +8,7 @@ const kHistorySize = 30; +const errors = require('internal/errors'); const util = require('util'); const debug = util.debuglog('readline'); const inherits = util.inherits; @@ -54,7 +55,7 @@ function Interface(input, output, completer, terminal) { } if (completer && typeof completer !== 'function') { - throw new TypeError('Argument "completer" must be a function'); + throw new errors.TypeError('INVALIDARG', 'completer', 'function'); } if (historySize === undefined) { @@ -64,7 +65,7 @@ function Interface(input, output, completer, terminal) { if (typeof historySize !== 'number' || isNaN(historySize) || historySize < 0) { - throw new TypeError('Argument "historySize" must be a positive number'); + throw new errors.TypeError('INVALIDARG', 'historySize', 'positive number'); } // backwards compat; check the isTTY prop of the output stream @@ -221,7 +222,7 @@ Interface.prototype._onLine = function(line) { Interface.prototype._writeToOutput = function _writeToOutput(stringToWrite) { if (typeof stringToWrite !== 'string') - throw new TypeError('"stringToWrite" argument must be a string'); + throw new errors.TypeError('INVALIDARG', 'stringToWrite', 'string'); if (this.output !== null && this.output !== undefined) this.output.write(stringToWrite); diff --git a/lib/timers.js b/lib/timers.js index dc2506e01e09a4..7085f0c161616b 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const TimerWrap = process.binding('timer_wrap').Timer; const L = require('internal/linkedlist'); const assert = require('assert'); @@ -282,12 +283,13 @@ const unenroll = exports.unenroll = function(item) { // Using existing objects as timers slightly reduces object overhead. exports.enroll = function(item, msecs) { if (typeof msecs !== 'number') { - throw new TypeError('"msecs" argument must be a number'); + throw new errors.TypeError('INVALIDARG', 'msecs', 'number'); } if (msecs < 0 || !isFinite(msecs)) { - throw new RangeError('"msecs" argument must be ' + - 'a non-negative finite number'); + throw new errors.RangeError('INVALIDARG', + 'msecs', + 'non-negative finite number'); } // if this item was already in a list somewhere @@ -311,7 +313,7 @@ exports.enroll = function(item, msecs) { exports.setTimeout = function(callback, after) { if (typeof callback !== 'function') { - throw new TypeError('"callback" argument must be a function'); + throw new errors.TypeError('CALLBACKREQUIRED'); } after *= 1; // coalesce to number or NaN @@ -371,7 +373,7 @@ const clearTimeout = exports.clearTimeout = function(timer) { exports.setInterval = function(callback, repeat) { if (typeof callback !== 'function') { - throw new TypeError('"callback" argument must be a function'); + throw new errors.TypeError('CALLBACKREQUIRED'); } repeat *= 1; // coalesce to number or NaN @@ -565,7 +567,7 @@ Immediate.prototype._idlePrev = undefined; exports.setImmediate = function(callback, arg1, arg2, arg3) { if (typeof callback !== 'function') { - throw new TypeError('"callback" argument must be a function'); + throw new errors.TypeError('CALLBACKREQUIRED'); } var i, args; diff --git a/lib/url.js b/lib/url.js index c4d6ed2e33a5f5..79142457ffe50c 100644 --- a/lib/url.js +++ b/lib/url.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const punycode = require('punycode'); exports.parse = urlParse; @@ -76,7 +77,7 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { if (typeof url !== 'string') { - throw new TypeError('Parameter "url" must be a string, not ' + typeof url); + throw new errors.TypeError('INVALIDARG', 'url', 'string'); } // Copy chrome, IE, opera backslash-handling behavior. @@ -532,13 +533,12 @@ function urlFormat(obj) { // If it's an obj, this is a no-op. // this way, you can call url_format() on strings // to clean up potentially wonky urls. - if (typeof obj === 'string') obj = urlParse(obj); - + if (typeof obj === 'string') + obj = urlParse(obj); else if (typeof obj !== 'object' || obj === null) - throw new TypeError('Parameter "urlObj" must be an object, not ' + - obj === null ? 'null' : typeof obj); - - else if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + throw new errors.TypeError('INVALIDARG', 'urlObj', 'object'); + else if (!(obj instanceof Url)) + return Url.prototype.format.call(obj); return obj.format(); } diff --git a/lib/util.js b/lib/util.js index 6fd399f88fd4d6..72e376410acfb0 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const uv = process.binding('uv'); const Buffer = require('buffer').Buffer; const internalUtil = require('internal/util'); @@ -881,16 +882,13 @@ exports.log = function() { exports.inherits = function(ctor, superCtor) { if (ctor === undefined || ctor === null) - throw new TypeError('The constructor to "inherits" must not be ' + - 'null or undefined'); + throw new errors.TypeError('REQUIREDARG', 'constructor'); if (superCtor === undefined || superCtor === null) - throw new TypeError('The super constructor to "inherits" must not ' + - 'be null or undefined'); + throw new errors.TypeError('REQUIREDARG', 'superConstructor'); if (superCtor.prototype === undefined) - throw new TypeError('The super constructor to "inherits" must ' + - 'have a prototype'); + throw new errors.TypeError('PROTOTYPEREQUIRED'); ctor.super_ = superCtor; Object.setPrototypeOf(ctor.prototype, superCtor.prototype); diff --git a/lib/zlib.js b/lib/zlib.js index 3915e5ddabd318..83f9a3599b4f6b 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -1,13 +1,12 @@ 'use strict'; +const errors = require('internal/errors'); const Buffer = require('buffer').Buffer; const Transform = require('_stream_transform'); const binding = process.binding('zlib'); const util = require('util'); const assert = require('assert').ok; const kMaxLength = require('buffer').kMaxLength; -const kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + - 'than 0x' + kMaxLength.toString(16) + ' bytes'; // zlib doesn't provide these, so kludge them in following the same // const naming scheme zlib uses. @@ -217,7 +216,7 @@ function zlibBuffer(engine, buffer, callback) { var err = null; if (nread >= kMaxLength) { - err = new RangeError(kRangeErrorMessage); + err = new errors.RangeError('BUFFERTOOLARGE', kMaxLength); } else { buf = Buffer.concat(buffers, nread); } @@ -232,7 +231,7 @@ function zlibBufferSync(engine, buffer) { if (typeof buffer === 'string') buffer = Buffer.from(buffer); if (!(buffer instanceof Buffer)) - throw new TypeError('Not a string or buffer'); + throw new errors.TypeError('INVALIDARG', 'buffer', ['Buffer', 'string']); var flushFlag = engine._finishFlushFlag; @@ -399,14 +398,14 @@ util.inherits(Zlib, Transform); Zlib.prototype.params = function(level, strategy, callback) { if (level < exports.Z_MIN_LEVEL || level > exports.Z_MAX_LEVEL) { - throw new RangeError('Invalid compression level: ' + level); + throw new errors.RangeError('INVALIDARGVALUE', 'level', level); } if (strategy != exports.Z_FILTERED && strategy != exports.Z_HUFFMAN_ONLY && strategy != exports.Z_RLE && strategy != exports.Z_FIXED && strategy != exports.Z_DEFAULT_STRATEGY) { - throw new TypeError('Invalid strategy: ' + strategy); + throw new errors.TypeError('INVALIDARGVALUE', 'strategy', strategy); } if (this._level !== level || this._strategy !== strategy) { @@ -550,7 +549,7 @@ Zlib.prototype._processChunk = function(chunk, flushFlag, cb) { if (nread >= kMaxLength) { _close(this); - throw new RangeError(kRangeErrorMessage); + throw new errors.RangeError('BUFFERTOOLARGE', kMaxLength); } var buf = Buffer.concat(buffers, nread); diff --git a/node.gyp b/node.gyp index 86dffef5c6716c..fbffbb8c9db9db 100644 --- a/node.gyp +++ b/node.gyp @@ -72,6 +72,7 @@ 'lib/zlib.js', 'lib/internal/child_process.js', 'lib/internal/cluster.js', + 'lib/internal/errors.js', 'lib/internal/freelist.js', 'lib/internal/linkedlist.js', 'lib/internal/net.js', diff --git a/test/common.js b/test/common.js index ffd1ba1366f15f..c99ae44d2419c0 100644 --- a/test/common.js +++ b/test/common.js @@ -516,3 +516,26 @@ exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) { return expectedExitCodes.indexOf(exitCode) > -1; } }; + +// Basically a wrapper for assert.throws that also understands Node.js +// custom error codes. If the second argument is an object with a `code` +// property on it, then that will be checked. +exports.throws = function throws(...args) { + if (args.length > 1 && + typeof args[1] === 'object' && + (typeof args[1].code === 'string' || + typeof args[1].name === 'string' || + typeof args[1].type === 'function')) { + assert.throws(args[0], (err) => { + const check = args[1]; + if ((typeof check.type === 'function' && !(err instanceof check.type)) || + (typeof check.name === 'string' && check.name !== err.name) || + (typeof check.code === 'string' && check.code !== err.code)) { + return false; + } + return true; + }); + } else { + assert.throws.apply(null, args); + } +}; diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index d06f7b678e714b..85de11ff14bdb9 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -481,8 +481,8 @@ function testBlockTypeError(method, block) { method(block); threw = false; } catch (e) { - assert.equal(e.toString(), - 'TypeError: "block" argument must be a function'); + assert.strictEqual(e.code, 'INVALIDARG'); + assert.ok(e instanceof TypeError); } assert.ok(threw); diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js index 3ce8c71987a179..d30f5e1d64d55c 100644 --- a/test/parallel/test-buffer-alloc.js +++ b/test/parallel/test-buffer-alloc.js @@ -1436,16 +1436,13 @@ assert.throws(function() { Buffer.allocUnsafe(10).copy(); }); -const regErrorMsg = new RegExp('First argument must be a string, Buffer, ' + - 'ArrayBuffer, Array, or array-like object.'); - -assert.throws(function() { +common.throws(function() { Buffer.from(); -}, regErrorMsg); +}, {code: 'INVALIDARG'}); -assert.throws(function() { +common.throws(function() { Buffer.from(null); -}, regErrorMsg); +}, {code: 'INVALIDARG'}); // Test that ParseArrayIndex handles full uint32 diff --git a/test/parallel/test-buffer-compare-offset.js b/test/parallel/test-buffer-compare-offset.js index 540d644c2847ff..c2362ff14d5449 100644 --- a/test/parallel/test-buffer-compare-offset.js +++ b/test/parallel/test-buffer-compare-offset.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); @@ -52,12 +52,12 @@ assert.equal(1, a.compare(b, Infinity, -Infinity)); // zero length target because default for targetEnd <= targetSource assert.equal(1, a.compare(b, '0xff')); -const oor = /out of range index/; +const oor = {code: 'INDEXOUTOFRANGE'}; -assert.throws(() => a.compare(b, 0, 100, 0), oor); -assert.throws(() => a.compare(b, 0, 1, 0, 100), oor); -assert.throws(() => a.compare(b, -1), oor); -assert.throws(() => a.compare(b, 0, '0xff'), oor); -assert.throws(() => a.compare(b, 0, Infinity), oor); -assert.throws(() => a.compare(b, -Infinity, Infinity), oor); -assert.throws(() => a.compare(), /Argument must be a Buffer/); +common.throws(() => a.compare(b, 0, 100, 0), oor); +common.throws(() => a.compare(b, 0, 1, 0, 100), oor); +common.throws(() => a.compare(b, -1), oor); +common.throws(() => a.compare(b, 0, '0xff'), oor); +common.throws(() => a.compare(b, 0, Infinity), oor); +common.throws(() => a.compare(b, -Infinity, Infinity), oor); +common.throws(() => a.compare(), {code: 'INVALIDARG'}); diff --git a/test/parallel/test-buffer-concat.js b/test/parallel/test-buffer-concat.js index bc628e656b4f11..5d38d9bd8e5048 100644 --- a/test/parallel/test-buffer-concat.js +++ b/test/parallel/test-buffer-concat.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); -var assert = require('assert'); +const common = require('../common'); +const assert = require('assert'); var zero = []; var one = [ Buffer.from('asdf') ]; @@ -28,10 +28,7 @@ assertWrongList(['hello', 'world']); assertWrongList(['hello', Buffer.from('world')]); function assertWrongList(value) { - assert.throws(function() { + common.throws(function() { Buffer.concat(value); - }, function(err) { - return err instanceof TypeError && - err.message === '"list" argument must be an Array of Buffers'; - }); + }, {code: 'INVALIDARG', type: TypeError}); } diff --git a/test/parallel/test-buffer.js b/test/parallel/test-buffer.js index d9a9d4e3b0e9c0..9dbf740095dc8c 100644 --- a/test/parallel/test-buffer.js +++ b/test/parallel/test-buffer.js @@ -1439,16 +1439,13 @@ assert.throws(function() { Buffer(10).copy(); }); -const regErrorMsg = new RegExp('First argument must be a string, Buffer, ' + - 'ArrayBuffer, Array, or array-like object.'); - -assert.throws(function() { +common.throws(function() { new Buffer(); -}, regErrorMsg); +}, {code: 'INVALIDARG'}); -assert.throws(function() { +common.throws(function() { new Buffer(null); -}, regErrorMsg); +}, {code: 'INVALIDARG'}); // Test prototype getters don't throw diff --git a/test/parallel/test-child-process-spawn-typeerror.js b/test/parallel/test-child-process-spawn-typeerror.js index bf59779a75ff5d..a6e0d17e44de76 100644 --- a/test/parallel/test-child-process-spawn-typeerror.js +++ b/test/parallel/test-child-process-spawn-typeerror.js @@ -1,14 +1,14 @@ 'use strict'; +const common = require('../common'); const assert = require('assert'); const child_process = require('child_process'); const spawn = child_process.spawn; const fork = child_process.fork; const execFile = child_process.execFile; -const common = require('../common'); const cmd = common.isWindows ? 'rundll32' : 'ls'; const invalidcmd = 'hopefully_you_dont_have_this_on_your_machine'; -const invalidArgsMsg = /Incorrect value of args option/; -const invalidOptionsMsg = /"options" argument must be an object/; +const invalidArgsMsg = {code: 'INVALIDARG'}; +const invalidOptionsMsg = {code: 'INVALIDARG'}; const empty = common.fixturesDir + '/empty.js'; assert.throws(function() { @@ -38,19 +38,19 @@ assert.throws(function() { spawn(); }, /Bad argument/); -assert.throws(function() { +common.throws(function() { spawn(cmd, null); }, invalidArgsMsg); -assert.throws(function() { +common.throws(function() { spawn(cmd, true); }, invalidArgsMsg); -assert.throws(function() { +common.throws(function() { spawn(cmd, [], null); }, invalidOptionsMsg); -assert.throws(function() { +common.throws(function() { spawn(cmd, [], 1); }, invalidOptionsMsg); diff --git a/test/parallel/test-child-process-spawnsync-input.js b/test/parallel/test-child-process-spawnsync-input.js index fdd29aa7d0cc50..0d3036f1a15ce7 100644 --- a/test/parallel/test-child-process-spawnsync-input.js +++ b/test/parallel/test-child-process-spawnsync-input.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); @@ -55,9 +55,9 @@ var options = { input: 1234 }; -assert.throws(function() { +common.throws(function() { spawnSync('cat', [], options); -}, /TypeError:.*should be Buffer or string not number/); +}, {code: 'INVALIDOPT'}); options = { diff --git a/test/parallel/test-child-process-validate-stdio.js b/test/parallel/test-child-process-validate-stdio.js index 09d000467404b6..2f1dd17bbde440 100644 --- a/test/parallel/test-child-process-validate-stdio.js +++ b/test/parallel/test-child-process-validate-stdio.js @@ -1,19 +1,19 @@ 'use strict'; // Flags: --expose_internals -require('../common'); +const common = require('../common'); const assert = require('assert'); const _validateStdio = require('internal/child_process')._validateStdio; // should throw if string and not ignore, pipe, or inherit -assert.throws(function() { +common.throws(function() { _validateStdio('foo'); -}, /Incorrect value of stdio option/); +}, {code: 'INVALIDOPTVALUE'}); // should throw if not a string or array -assert.throws(function() { +common.throws(function() { _validateStdio(600); -}, /Incorrect value of stdio option/); +}, {code: 'INVALIDOPTVALUE'}); // should populate stdio with undefined if len < 3 { @@ -27,9 +27,9 @@ assert.throws(function() { // should throw if stdio has ipc and sync is true const stdio2 = ['ipc', 'ipc', 'ipc']; -assert.throws(function() { +common.throws(function() { _validateStdio(stdio2, true); -}, /You cannot use IPC with synchronous forks/); +}, {code: 'IPCNOSYNCFORK'}); { const stdio3 = [process.stdin, process.stdout, process.stderr]; diff --git a/test/parallel/test-console-instance.js b/test/parallel/test-console-instance.js index 2a8a6e3678f58a..63fd98a5b94b8d 100644 --- a/test/parallel/test-console-instance.js +++ b/test/parallel/test-console-instance.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const Stream = require('stream'); const Console = require('console').Console; @@ -19,16 +19,16 @@ assert.equal('function', typeof Console); // make sure that the Console constructor throws // when not given a writable stream instance -assert.throws(function() { +common.throws(function() { new Console(); -}, /Console expects a writable stream/); +}, {code: 'CONSOLEWRITABLE'}); // Console constructor should throw if stderr exists but is not writable -assert.throws(function() { +common.throws(function() { out.write = function() {}; err.write = undefined; new Console(out, err); -}, /Console expects writable stream instances/); +}, {code: 'CONSOLEWRITABLE'}); out.write = err.write = function(d) {}; diff --git a/test/parallel/test-dgram-setTTL.js b/test/parallel/test-dgram-setTTL.js index 88627a314c3848..e899080aff38d5 100644 --- a/test/parallel/test-dgram-setTTL.js +++ b/test/parallel/test-dgram-setTTL.js @@ -9,9 +9,9 @@ socket.on('listening', function() { var result = socket.setTTL(16); assert.strictEqual(result, 16); - assert.throws(function() { + common.throws(function() { socket.setTTL('foo'); - }, /Argument must be a number/); + }, {code: 'INVALIDARG'}); socket.close(); }); diff --git a/test/parallel/test-dns.js b/test/parallel/test-dns.js index cd5914e026d6a9..eab22a202c9c2b 100644 --- a/test/parallel/test-dns.js +++ b/test/parallel/test-dns.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const dns = require('dns'); @@ -150,9 +150,9 @@ assert.throws(function() { dns.lookupService('0.0.0.0'); }, /Invalid arguments/); -assert.throws(function() { +common.throws(function() { dns.lookupService('fasdfdsaf', 0, noop); -}, /"host" argument needs to be a valid IP address/); +}, {code: 'INVALIDARG'}); assert.doesNotThrow(function() { dns.lookupService('0.0.0.0', '0', noop); @@ -162,18 +162,18 @@ assert.doesNotThrow(function() { dns.lookupService('0.0.0.0', 0, noop); }); -assert.throws(function() { +common.throws(function() { dns.lookupService('0.0.0.0', null, noop); -}, /"port" should be >= 0 and < 65536, got "null"/); +}, {code: 'PORTRANGE'}); -assert.throws(function() { +common.throws(function() { dns.lookupService('0.0.0.0', undefined, noop); -}, /"port" should be >= 0 and < 65536, got "undefined"/); +}, {code: 'PORTRANGE'}); -assert.throws(function() { +common.throws(function() { dns.lookupService('0.0.0.0', 65538, noop); -}, /"port" should be >= 0 and < 65536, got "65538"/); +}, {code: 'PORTRANGE'}); -assert.throws(function() { +common.throws(function() { dns.lookupService('0.0.0.0', 'test', noop); -}, /"port" should be >= 0 and < 65536, got "test"/); +}, {code: 'PORTRANGE'}); diff --git a/test/parallel/test-file-write-stream3.js b/test/parallel/test-file-write-stream3.js index 1243460f9f314f..89cc802125a0b6 100644 --- a/test/parallel/test-file-write-stream3.js +++ b/test/parallel/test-file-write-stream3.js @@ -159,11 +159,11 @@ function run_test_3() { const run_test_4 = common.mustCall(function() { // Error: start must be >= zero - assert.throws( + common.throws( function() { fs.createWriteStream(filepath, { start: -5, flags: 'r+' }); }, - /"start" must be/ + {code: 'INVALIDOPT'} ); }); diff --git a/test/parallel/test-fs-access.js b/test/parallel/test-fs-access.js index 6b82b4bbf312c3..4a7cce69aa04c6 100644 --- a/test/parallel/test-fs-access.js +++ b/test/parallel/test-fs-access.js @@ -94,13 +94,13 @@ assert.throws(function() { fs.access(100, fs.F_OK, function(err) {}); }, /path must be a string or Buffer/); -assert.throws(function() { +common.throws(function() { fs.access(__filename, fs.F_OK); -}, /"callback" argument must be a function/); +}, {code: 'CALLBACKREQUIRED'}); -assert.throws(function() { +common.throws(function() { fs.access(__filename, fs.F_OK, {}); -}, /"callback" argument must be a function/); +}, {code: 'CALLBACKREQUIRED'}); assert.doesNotThrow(function() { fs.accessSync(__filename); diff --git a/test/parallel/test-fs-read-stream-inherit.js b/test/parallel/test-fs-read-stream-inherit.js index c4216f4e13905b..3160ee524d02c9 100644 --- a/test/parallel/test-fs-read-stream-inherit.js +++ b/test/parallel/test-fs-read-stream-inherit.js @@ -112,9 +112,9 @@ file6.on('end', function() { assert.equal(file6.data, 'yz\n'); }); -assert.throws(function() { +common.throws(function() { fs.createReadStream(rangeFile, Object.create({start: 10, end: 2})); -}, /"start" option must be <= "end" option/); +}, {code: 'INVALIDOPT'}); var stream = fs.createReadStream(rangeFile, Object.create({ start: 0, end: 0 })); diff --git a/test/parallel/test-fs-read-stream-throw-type-error.js b/test/parallel/test-fs-read-stream-throw-type-error.js index 81f924d355b91c..c8fae329fbe666 100644 --- a/test/parallel/test-fs-read-stream-throw-type-error.js +++ b/test/parallel/test-fs-read-stream-throw-type-error.js @@ -16,18 +16,18 @@ assert.doesNotThrow(function() { fs.createReadStream(example, {encoding: 'utf8'}); }); -assert.throws(function() { +common.throws(function() { fs.createReadStream(example, null); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createReadStream(example, 123); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createReadStream(example, 0); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createReadStream(example, true); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createReadStream(example, false); -}, /"options" argument must be a string or an object/); +}, {code: 'INVALIDARG'}); diff --git a/test/parallel/test-fs-read-stream.js b/test/parallel/test-fs-read-stream.js index 98a316e60e4a02..d64f77fe0c20a8 100644 --- a/test/parallel/test-fs-read-stream.js +++ b/test/parallel/test-fs-read-stream.js @@ -105,9 +105,9 @@ file6.on('end', function() { assert.equal(file6.data, 'yz\n'); }); -assert.throws(function() { +common.throws(function() { fs.createReadStream(rangeFile, {start: 10, end: 2}); -}, /"start" option must be <= "end" option/); +}, {code: 'INVALIDOPT'}); var stream = fs.createReadStream(rangeFile, { start: 0, end: 0 }); stream.data = ''; diff --git a/test/parallel/test-fs-watchfile.js b/test/parallel/test-fs-watchfile.js index d30261859c39b7..06340032432828 100644 --- a/test/parallel/test-fs-watchfile.js +++ b/test/parallel/test-fs-watchfile.js @@ -14,9 +14,9 @@ assert.throws(function() { fs.watchFile('./another-file', {}, 'bad listener'); }, /"watchFile\(\)" requires a listener function/); -assert.throws(function() { +common.throws(function() { fs.watchFile(new Object(), function() {}); -}, /Path must be a string/); +}, {code: 'INVALIDARG'}); const enoentFile = path.join(common.tmpDir, 'non-existent-file'); const expectedStatObject = new fs.Stats( diff --git a/test/parallel/test-fs-write-stream-throw-type-error.js b/test/parallel/test-fs-write-stream-throw-type-error.js index 007ac03f1bb342..c877b63e4357fd 100644 --- a/test/parallel/test-fs-write-stream-throw-type-error.js +++ b/test/parallel/test-fs-write-stream-throw-type-error.js @@ -18,18 +18,18 @@ assert.doesNotThrow(function() { fs.createWriteStream(example, {encoding: 'utf8'}); }); -assert.throws(function() { +common.throws(function() { fs.createWriteStream(example, null); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createWriteStream(example, 123); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createWriteStream(example, 0); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createWriteStream(example, true); -}, /"options" argument must be a string or an object/); -assert.throws(function() { +}, {code: 'INVALIDARG'}); +common.throws(function() { fs.createWriteStream(example, false); -}, /"options" argument must be a string or an object/); +}, {code: 'INVALIDARG'}); diff --git a/test/parallel/test-http-agent-keepalive.js b/test/parallel/test-http-agent-keepalive.js index 2b939db68d7cbe..c7aba73b15a439 100644 --- a/test/parallel/test-http-agent-keepalive.js +++ b/test/parallel/test-http-agent-keepalive.js @@ -89,7 +89,7 @@ function remoteError() { }); req.on('error', function(err) { assert.ok(err); - assert.equal(err.message, 'socket hang up'); + assert.strictEqual(err.code, 'ECONNRESET'); assert.equal(agent.sockets[name].length, 1); assert.equal(agent.freeSockets[name], undefined); // Wait socket 'close' event emit diff --git a/test/parallel/test-http-url.parse-only-support-http-https-protocol.js b/test/parallel/test-http-url.parse-only-support-http-https-protocol.js index e7ed82817146ea..ddd11d551c3245 100644 --- a/test/parallel/test-http-url.parse-only-support-http-https-protocol.js +++ b/test/parallel/test-http-url.parse-only-support-http-https-protocol.js @@ -4,63 +4,29 @@ var assert = require('assert'); var http = require('http'); var url = require('url'); +function errChecker(protocol, expected) { + return function(err) { + if (err instanceof Error) { + console.log(err); + assert.strictEqual(err.code, 'HTTPUNSUPPORTEDPROTOCOL'); + assert.strictEqual(err.name, 'Error[HTTPUNSUPPORTEDPROTOCOL]'); + assert.strictEqual( + err.message, + `Protocol "${protocol}" is not supported. Expected "${expected}"`); + return true; + } + }; +} -assert.throws(function() { - http.request(url.parse('file:///whatever')); -}, function(err) { - if (err instanceof Error) { - assert.strictEqual(err.message, 'Protocol "file:" not supported.' + - ' Expected "http:"'); - return true; - } -}); - -assert.throws(function() { - http.request(url.parse('mailto:asdf@asdf.com')); -}, function(err) { - if (err instanceof Error) { - assert.strictEqual(err.message, 'Protocol "mailto:" not supported.' + - ' Expected "http:"'); - return true; - } -}); - -assert.throws(function() { - http.request(url.parse('ftp://www.example.com')); -}, function(err) { - if (err instanceof Error) { - assert.strictEqual(err.message, 'Protocol "ftp:" not supported.' + - ' Expected "http:"'); - return true; - } -}); - -assert.throws(function() { - http.request(url.parse('javascript:alert(\'hello\');')); -}, function(err) { - if (err instanceof Error) { - assert.strictEqual(err.message, 'Protocol "javascript:" not supported.' + - ' Expected "http:"'); - return true; - } -}); - -assert.throws(function() { - http.request(url.parse('xmpp:isaacschlueter@jabber.org')); -}, function(err) { - if (err instanceof Error) { - assert.strictEqual(err.message, 'Protocol "xmpp:" not supported.' + - ' Expected "http:"'); - return true; - } -}); - -assert.throws(function() { - http.request(url.parse('f://some.host/path')); -}, function(err) { - if (err instanceof Error) { - assert.strictEqual(err.message, 'Protocol "f:" not supported.' + - ' Expected "http:"'); - return true; - } +[ + ['file:', 'file:///whatever'], + ['mailto:', 'mailto:asdf@asdf.com'], + ['ftp:', 'ftp://www.example.org'], + ['javascript:', 'javascript:alert(\'hello\');'], + ['xmpp:', 'xmpp:example@jabber.org'], + ['f:', 'f://some.host/path'] +].forEach((item) => { + assert.throws(function() { + http.request(url.parse(item[1])); + }, errChecker(item[0], 'http:')); }); diff --git a/test/parallel/test-http-write-head.js b/test/parallel/test-http-write-head.js index a56d6f18d0e377..c3a316d232e858 100644 --- a/test/parallel/test-http-write-head.js +++ b/test/parallel/test-http-write-head.js @@ -25,7 +25,9 @@ var s = http.createServer(function(req, res) { res.setHeader('foo', undefined); } catch (e) { assert.ok(e instanceof Error); - assert.equal(e.message, '"value" required in setHeader("foo", value)'); + assert.strictEqual(e.code, 'REQUIREDARG'); + assert.strictEqual(e.message, + '"value" argument is required and cannot be undefined'); threw = true; } assert.ok(threw, 'Undefined value should throw'); diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js new file mode 100644 index 00000000000000..cec874ad816c8f --- /dev/null +++ b/test/parallel/test-internal-errors.js @@ -0,0 +1,106 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); + +const errors = require('internal/errors'); + +assert.ok(errors.Error); +assert.ok(errors.TypeError); +assert.ok(errors.RangeError); + +assert.throws(() => { + new errors.Error('SOME KEY THAT DOES NOT EXIST'); +}, /An invalid error message key was used/); + +// Test Unknown Signal error +{ + // Unknown signal: ${sig} + const err = new errors.Error('UNKNOWNSIGNAL', 'FOO'); + assert.strictEqual(err.name, 'Error[UNKNOWNSIGNAL]'); + assert.strictEqual(err.code, 'UNKNOWNSIGNAL'); + assert.strictEqual(err.message, 'Unknown signal: FOO'); +} + +// Test Invalid Argument errors +{ + // '${arg}' argument must be ${prefix} ${expected} + const err = new errors.TypeError('INVALIDARG', 'Foo', 'Bar'); + assert.strictEqual(err.name, 'TypeError[INVALIDARG]'); + assert.strictEqual(err.code, 'INVALIDARG'); + assert.strictEqual(err.message, '"Foo" argument must be a Bar'); +} + +{ + // '${arg}' argument must be ${prefix} ${expected} + const err = new errors.TypeError('INVALIDARG', 'Foo', 'Object'); + assert.strictEqual(err.name, 'TypeError[INVALIDARG]'); + assert.strictEqual(err.code, 'INVALIDARG'); + assert.strictEqual(err.message, '"Foo" argument must be an Object'); +} + +{ + // '${arg}' argument must be ${prefix} ${expected} + const err = new errors.TypeError('INVALIDARG', 'Foo', ['Object']); + assert.strictEqual(err.name, 'TypeError[INVALIDARG]'); + assert.strictEqual(err.code, 'INVALIDARG'); + assert.strictEqual(err.message, '"Foo" argument must be an Object'); +} + +{ + // '${arg}' argument must be one of: ${expected} + const err = new errors.TypeError('INVALIDARG', 'Foo', ['Object', 'Bar']); + assert.strictEqual(err.name, 'TypeError[INVALIDARG]'); + assert.strictEqual(err.code, 'INVALIDARG'); + assert.strictEqual(err.message, + '"Foo" argument must be one of: Object, Bar'); +} + +// Test Required Argument Error +{ + // `'${arg}' argument is required and cannot be undefined.` + const err = new errors.TypeError('REQUIREDARG', 'Foo'); + assert.strictEqual(err.name, 'TypeError[REQUIREDARG]'); + assert.strictEqual(err.code, 'REQUIREDARG'); + assert.strictEqual(err.message, + '"Foo" argument is required and cannot be undefined'); +} + +// Test Invalid Option Value Error +{ + // Invalid value for "${opt}" option. + const err = new errors.TypeError('INVALIDOPTVALUE', 'Foo'); + assert.strictEqual(err.name, 'TypeError[INVALIDOPTVALUE]'); + assert.strictEqual(err.code, 'INVALIDOPTVALUE'); + assert.strictEqual(err.message, + 'Invalid value for "Foo" option'); +} + +{ + // Invalid value for "${opt}" option: ${val}. + const err = new errors.TypeError('INVALIDOPTVALUE', 'Foo', 'Bar'); + assert.strictEqual(err.name, 'TypeError[INVALIDOPTVALUE]'); + assert.strictEqual(err.code, 'INVALIDOPTVALUE'); + assert.strictEqual(err.message, + 'Invalid value for "Foo" option: Bar'); +} + +// Test Invalid Argument Value Error +{ + // Invalid value for "${arg}" argument. + const err = new errors.TypeError('INVALIDARGVALUE', 'Foo'); + assert.strictEqual(err.name, 'TypeError[INVALIDARGVALUE]'); + assert.strictEqual(err.code, 'INVALIDARGVALUE'); + assert.strictEqual(err.message, + 'Invalid value for "Foo" argument'); +} + +{ + // Invalid value for "${arg}" argument: ${val}. + const err = new errors.TypeError('INVALIDARGVALUE', 'Foo', 'Bar'); + assert.strictEqual(err.name, 'TypeError[INVALIDARGVALUE]'); + assert.strictEqual(err.code, 'INVALIDARGVALUE'); + assert.strictEqual(err.message, + 'Invalid value for "Foo" argument: Bar'); +} diff --git a/test/parallel/test-net-create-connection.js b/test/parallel/test-net-create-connection.js index 6cc71c7afa8b73..0f84f68846d939 100644 --- a/test/parallel/test-net-create-connection.js +++ b/test/parallel/test-net-create-connection.js @@ -25,7 +25,7 @@ server.listen(tcpPort, 'localhost', function() { assert.throws(function() { net.createConnection(opts, cb); }, function(err) { - return err instanceof errtype && msg === err.message; + return err instanceof errtype && msg === err.code; }); } @@ -39,59 +39,59 @@ server.listen(tcpPort, 'localhost', function() { fail({ port: true - }, TypeError, '"port" option should be a number or string: true'); + }, TypeError, 'INVALIDOPT'); fail({ port: false - }, TypeError, '"port" option should be a number or string: false'); + }, TypeError, 'INVALIDOPT'); fail({ port: [] - }, TypeError, '"port" option should be a number or string: '); + }, TypeError, 'INVALIDOPT'); fail({ port: {} - }, TypeError, '"port" option should be a number or string: [object Object]'); + }, TypeError, 'INVALIDOPT'); fail({ port: null - }, TypeError, '"port" option should be a number or string: null'); + }, TypeError, 'INVALIDOPT'); fail({ port: '' - }, RangeError, '"port" option should be >= 0 and < 65536: '); + }, RangeError, 'PORTRANGE'); fail({ port: ' ' - }, RangeError, '"port" option should be >= 0 and < 65536: '); + }, RangeError, 'PORTRANGE'); fail({ port: '0x' - }, RangeError, '"port" option should be >= 0 and < 65536: 0x'); + }, RangeError, 'PORTRANGE'); fail({ port: '-0x1' - }, RangeError, '"port" option should be >= 0 and < 65536: -0x1'); + }, RangeError, 'PORTRANGE'); fail({ port: NaN - }, RangeError, '"port" option should be >= 0 and < 65536: NaN'); + }, RangeError, 'PORTRANGE'); fail({ port: Infinity - }, RangeError, '"port" option should be >= 0 and < 65536: Infinity'); + }, RangeError, 'PORTRANGE'); fail({ port: -1 - }, RangeError, '"port" option should be >= 0 and < 65536: -1'); + }, RangeError, 'PORTRANGE'); fail({ port: 65536 - }, RangeError, '"port" option should be >= 0 and < 65536: 65536'); + }, RangeError, 'PORTRANGE'); fail({ hints: (dns.ADDRCONFIG | dns.V4MAPPED) + 42, - }, TypeError, 'Invalid argument: hints must use valid flags'); + }, TypeError, 'INVALIDOPTVALUE'); }); // Try connecting to random ports, but do so once the server is closed diff --git a/test/parallel/test-net-listen-port-option.js b/test/parallel/test-net-listen-port-option.js index 18b256c973eaec..841d9664a540ca 100644 --- a/test/parallel/test-net-listen-port-option.js +++ b/test/parallel/test-net-listen-port-option.js @@ -16,9 +16,9 @@ net.Server().listen({ port: '' + common.PORT }, close); '+Infinity', '-Infinity' ].forEach(function(port) { - assert.throws(function() { + common.throws(function() { net.Server().listen({ port: port }, assert.fail); - }, /"port" argument must be >= 0 and < 65536/i); + }, {code: 'PORTRANGE'}); }); [null, true, false].forEach(function(port) { diff --git a/test/parallel/test-net-socket-write-error.js b/test/parallel/test-net-socket-write-error.js index db236be1a5e8f3..bdd542b59fb905 100644 --- a/test/parallel/test-net-socket-write-error.js +++ b/test/parallel/test-net-socket-write-error.js @@ -1,16 +1,15 @@ 'use strict'; const common = require('../common'); -const assert = require('assert'); const net = require('net'); const server = net.createServer().listen(common.PORT, connectToServer); function connectToServer() { const client = net.createConnection(common.PORT, () => { - assert.throws(() => { + common.throws(() => { client.write(1337); - }, /Invalid data, chunk must be a string or buffer, not number/); + }, {code: 'INVALIDARG'}); client.end(); }) diff --git a/test/parallel/test-path-parse-format.js b/test/parallel/test-path-parse-format.js index a42794790d1459..a86a31774b3ae5 100644 --- a/test/parallel/test-path-parse-format.js +++ b/test/parallel/test-path-parse-format.js @@ -162,10 +162,7 @@ function checkErrors(path) { path[errorCase.method].apply(path, errorCase.input); } catch (err) { assert.ok(err instanceof TypeError); - assert.ok( - errorCase.message.test(err.message), - 'expected ' + errorCase.message + ' to match ' + err.message - ); + assert.strictEqual(err.code, 'INVALIDARG'); return; } diff --git a/test/parallel/test-process-emitwarning.js b/test/parallel/test-process-emitwarning.js index 33547377bb901b..925253f5b5d1de 100644 --- a/test/parallel/test-process-emitwarning.js +++ b/test/parallel/test-process-emitwarning.js @@ -25,5 +25,5 @@ util.inherits(CustomWarning, Error); process.emitWarning(new CustomWarning()); // TypeError is thrown on invalid output -assert.throws(() => process.emitWarning(1), TypeError); -assert.throws(() => process.emitWarning({}), TypeError); +common.throws(() => process.emitWarning(1), {code: 'INVALIDARG'}); +common.throws(() => process.emitWarning({}), {code: 'INVALIDARG'}); diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index 9a3b7f5ef3ec2f..567994be5ad62a 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -1,6 +1,6 @@ // Flags: --expose_internals 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const readline = require('readline'); const internalReadline = require('internal/readline'); @@ -236,19 +236,12 @@ function isWarned(emitter) { // constructor throws if completer is not a function or undefined fi = new FakeInput(); - assert.throws(function() { + common.throws(function() { readline.createInterface({ input: fi, completer: 'string is not valid' }); - }, function(err) { - if (err instanceof TypeError) { - if (/Argument "completer" must be a function/.test(err)) { - return true; - } - } - return false; - }); + }, {type: TypeError, code: 'INVALIDARG'}); // sending a multi-byte utf8 char over multiple writes var buf = Buffer.from('☮', 'utf8'); diff --git a/test/parallel/test-regress-GH-5727.js b/test/parallel/test-regress-GH-5727.js index ae8cca9cbf3338..a6288b004e9875 100644 --- a/test/parallel/test-regress-GH-5727.js +++ b/test/parallel/test-regress-GH-5727.js @@ -4,7 +4,7 @@ const assert = require('assert'); const net = require('net'); const invalidPort = -1 >>> 0; -const errorMessage = /"port" argument must be \>= 0 and \< 65536/; +const errorMessage = {code: 'PORTRANGE'}; net.Server().listen(common.PORT, function() { const address = this.address(); @@ -15,16 +15,16 @@ net.Server().listen(common.PORT, function() { }); // The first argument is a configuration object -assert.throws(() => { +common.throws(() => { net.Server().listen({ port: invalidPort }, common.fail); }, errorMessage); // The first argument is the port, no IP given. -assert.throws(() => { +common.throws(() => { net.Server().listen(invalidPort, common.fail); }, errorMessage); // The first argument is the port, the second an IP. -assert.throws(() => { +common.throws(() => { net.Server().listen(invalidPort, '0.0.0.0', common.fail); }, errorMessage); diff --git a/test/parallel/test-timers-throw-when-cb-not-function.js b/test/parallel/test-timers-throw-when-cb-not-function.js index 13533107934bd6..c6dfb29be943a2 100644 --- a/test/parallel/test-timers-throw-when-cb-not-function.js +++ b/test/parallel/test-timers-throw-when-cb-not-function.js @@ -1,6 +1,5 @@ 'use strict'; -require('../common'); -const assert = require('assert'); +const common = require('../common'); function doSetTimeout(callback, after) { return function() { @@ -8,18 +7,18 @@ function doSetTimeout(callback, after) { }; } -assert.throws(doSetTimeout('foo'), - /"callback" argument must be a function/); -assert.throws(doSetTimeout({foo: 'bar'}), - /"callback" argument must be a function/); -assert.throws(doSetTimeout(), - /"callback" argument must be a function/); -assert.throws(doSetTimeout(undefined, 0), - /"callback" argument must be a function/); -assert.throws(doSetTimeout(null, 0), - /"callback" argument must be a function/); -assert.throws(doSetTimeout(false, 0), - /"callback" argument must be a function/); +common.throws(doSetTimeout('foo'), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetTimeout({foo: 'bar'}), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetTimeout(), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetTimeout(undefined, 0), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetTimeout(null, 0), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetTimeout(false, 0), + {code: 'CALLBACKREQUIRED'}); function doSetInterval(callback, after) { @@ -28,18 +27,18 @@ function doSetInterval(callback, after) { }; } -assert.throws(doSetInterval('foo'), - /"callback" argument must be a function/); -assert.throws(doSetInterval({foo: 'bar'}), - /"callback" argument must be a function/); -assert.throws(doSetInterval(), - /"callback" argument must be a function/); -assert.throws(doSetInterval(undefined, 0), - /"callback" argument must be a function/); -assert.throws(doSetInterval(null, 0), - /"callback" argument must be a function/); -assert.throws(doSetInterval(false, 0), - /"callback" argument must be a function/); +common.throws(doSetInterval('foo'), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetInterval({foo: 'bar'}), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetInterval(), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetInterval(undefined, 0), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetInterval(null, 0), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetInterval(false, 0), + {code: 'CALLBACKREQUIRED'}); function doSetImmediate(callback, after) { @@ -48,15 +47,15 @@ function doSetImmediate(callback, after) { }; } -assert.throws(doSetImmediate('foo'), - /"callback" argument must be a function/); -assert.throws(doSetImmediate({foo: 'bar'}), - /"callback" argument must be a function/); -assert.throws(doSetImmediate(), - /"callback" argument must be a function/); -assert.throws(doSetImmediate(undefined, 0), - /"callback" argument must be a function/); -assert.throws(doSetImmediate(null, 0), - /"callback" argument must be a function/); -assert.throws(doSetImmediate(false, 0), - /"callback" argument must be a function/); +common.throws(doSetImmediate('foo'), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetImmediate({foo: 'bar'}), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetImmediate(), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetImmediate(undefined, 0), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetImmediate(null, 0), + {code: 'CALLBACKREQUIRED'}); +common.throws(doSetImmediate(false, 0), + {code: 'CALLBACKREQUIRED'}); diff --git a/test/parallel/test-tls-basic-validations.js b/test/parallel/test-tls-basic-validations.js index a741f3b49c47ac..2c76d353189fc6 100644 --- a/test/parallel/test-tls-basic-validations.js +++ b/test/parallel/test-tls-basic-validations.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const tls = require('tls'); @@ -19,8 +19,8 @@ assert.throws(() => tls.createServer({key: 'dummykey', passphrase: 1}), assert.throws(() => tls.createServer({ecdhCurve: 1}), /TypeError: ECDH curve name must be a string/); -assert.throws(() => tls.createServer({handshakeTimeout: 'abcd'}), - /TypeError: handshakeTimeout must be a number/); +common.throws(() => tls.createServer({handshakeTimeout: 'abcd'}), + {code: 'INVALIDOPT'}); assert.throws(() => tls.createServer({sessionTimeout: 'abcd'}), /TypeError: Session timeout must be a 32-bit integer/); diff --git a/test/parallel/test-tty-stdout-end.js b/test/parallel/test-tty-stdout-end.js index a33a2e5ed27209..7084e53b7ac3ba 100644 --- a/test/parallel/test-tty-stdout-end.js +++ b/test/parallel/test-tty-stdout-end.js @@ -1,15 +1,5 @@ 'use strict'; // Can't test this when 'make test' doesn't assign a tty to the stdout. -require('../common'); -const assert = require('assert'); +const common = require('../common'); -const shouldThrow = function() { - process.stdout.end(); -}; - -const validateError = function(e) { - return e instanceof Error && - e.message === 'process.stdout cannot be closed.'; -}; - -assert.throws(shouldThrow, validateError); +common.throws(() => process.stdout.end(), {code: 'STDOUTCLOSE', type: Error});