From e5ad5456a28add7f47d70f68427695aa4705d5ef Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 25 Sep 2017 21:43:32 -0700 Subject: [PATCH] events: migrate to internal/errors PR-URL: https://github.com/nodejs/node/pull/15623 Reviewed-By: Joyee Cheung Reviewed-By: Ruben Bridgewater Reviewed-By: Michael Dawson Reviewed-By: Matteo Collina --- doc/api/errors.md | 6 ++ lib/events.js | 55 ++++++++++++++----- lib/internal/errors.js | 6 ++ .../test-event-emitter-add-listeners.js | 9 ++- test/parallel/test-event-emitter-errors.js | 25 ++++++--- .../test-event-emitter-max-listeners.js | 22 ++++++-- test/parallel/test-event-emitter-once.js | 8 ++- test/parallel/test-event-emitter-prepend.js | 8 ++- .../test-event-emitter-remove-listeners.js | 8 ++- 9 files changed, 110 insertions(+), 37 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 1b78f2c3379b20..13cde982839b3e 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1251,6 +1251,12 @@ buffer. Used when a string that contains unescaped characters was received. + +### ERR_UNHANDLED_ERROR + +Used when an unhandled "error" occurs (for instance, when an `'error'` event +is emitted by an `EventEmitter` but an `'error'` handler is not registered). + ### ERR_UNKNOWN_ENCODING diff --git a/lib/events.js b/lib/events.js index dce1dc48fa42e8..1414a1429dfe3b 100644 --- a/lib/events.js +++ b/lib/events.js @@ -41,6 +41,13 @@ EventEmitter.prototype._maxListeners = undefined; // added to it. This is a useful default which helps finding memory leaks. var defaultMaxListeners = 10; +var errors; +function lazyErrors() { + if (errors === undefined) + errors = require('internal/errors'); + return errors; +} + Object.defineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function() { @@ -52,8 +59,10 @@ Object.defineProperty(EventEmitter, 'defaultMaxListeners', { console; // check whether the input is a positive number (whose value is zero or // greater and not a NaN). - if (typeof arg !== 'number' || arg < 0 || arg !== arg) - throw new TypeError('"defaultMaxListeners" must be a positive number'); + if (typeof arg !== 'number' || arg < 0 || arg !== arg) { + const errors = lazyErrors(); + throw new errors.TypeError('ERR_OUT_OF_RANGE', 'defaultMaxListeners'); + } defaultMaxListeners = arg; } }); @@ -79,8 +88,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 = lazyErrors(); + throw new errors.TypeError('ERR_OUT_OF_RANGE', 'n'); + } this._maxListeners = n; return this; }; @@ -170,8 +181,10 @@ EventEmitter.prototype.emit = function emit(type) { if (arguments.length > 1) er = arguments[1]; if (domain) { - if (!er) - er = new Error('Unhandled "error" event'); + if (!er) { + const errors = lazyErrors(); + er = new errors.Error('ERR_UNHANDLED_ERROR'); + } if (typeof er === 'object' && er !== null) { er.domainEmitter = this; er.domain = domain; @@ -182,7 +195,8 @@ EventEmitter.prototype.emit = function emit(type) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user - const err = new Error('Unhandled "error" event. (' + er + ')'); + const errors = lazyErrors(); + const err = new errors.Error('ERR_UNHANDLED_ERROR', er); err.context = er; throw err; } @@ -234,8 +248,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 = lazyErrors(); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'listener', 'function'); + } events = target._events; if (!events) { @@ -278,6 +294,7 @@ function _addListener(target, type, listener, prepend) { m = $getMaxListeners(target); if (m && m > 0 && existing.length > m) { existing.warned = true; + // No error code for this since it is a Warning const w = new Error('Possible EventEmitter memory leak detected. ' + `${existing.length} ${String(type)} listeners ` + 'added. Use emitter.setMaxListeners() to ' + @@ -337,16 +354,21 @@ 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 = lazyErrors(); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', '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 = lazyErrors(); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'listener', + 'function'); + } this.prependListener(type, _onceWrap(this, type, listener)); return this; }; @@ -356,8 +378,11 @@ 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 = lazyErrors(); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'listener', + 'function'); + } events = this._events; if (!events) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 9a5551617d3e4b..7c5c8a1f0a5346 100755 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -327,6 +327,12 @@ E('ERR_TRANSFORM_WITH_LENGTH_0', 'Calling transform done when writableState.length != 0'); E('ERR_UNESCAPED_CHARACTERS', (name) => `${name} contains unescaped characters`); +E('ERR_UNHANDLED_ERROR', + (err) => { + const msg = 'Unhandled error.'; + if (err === undefined) return msg; + return `${msg} (${err})`; + }); E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s'); E('ERR_UNKNOWN_FILE_EXTENSION', 'Unknown file extension: %s'); E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s'); diff --git a/test/parallel/test-event-emitter-add-listeners.js b/test/parallel/test-event-emitter-add-listeners.js index d6177a78806e49..2a02a94940fa5d 100644 --- a/test/parallel/test-event-emitter-add-listeners.js +++ b/test/parallel/test-event-emitter-add-listeners.js @@ -86,8 +86,11 @@ const EventEmitter = require('events'); } // Verify that the listener must be a function -assert.throws(() => { +common.expectsError(() => { const ee = new EventEmitter(); - ee.on('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "listener" argument must be of type function' +}); diff --git a/test/parallel/test-event-emitter-errors.js b/test/parallel/test-event-emitter-errors.js index d52d3b4f4e74ef..ef2bbee93f8bfd 100644 --- a/test/parallel/test-event-emitter-errors.js +++ b/test/parallel/test-event-emitter-errors.js @@ -1,14 +1,23 @@ 'use strict'; -require('../common'); +const common = require('../common'); const EventEmitter = require('events'); -const assert = require('assert'); const EE = new EventEmitter(); -assert.throws(() => { - EE.emit('error', 'Accepts a string'); -}, /^Error: Unhandled "error" event\. \(Accepts a string\)$/); +common.expectsError( + () => EE.emit('error', 'Accepts a string'), + { + code: 'ERR_UNHANDLED_ERROR', + type: Error, + message: 'Unhandled error. (Accepts a string)' + } +); -assert.throws(() => { - EE.emit('error', { message: 'Error!' }); -}, /^Error: Unhandled "error" event\. \(\[object Object\]\)$/); +common.expectsError( + () => EE.emit('error', { message: 'Error!' }), + { + code: 'ERR_UNHANDLED_ERROR', + type: Error, + message: 'Unhandled error. ([object Object])' + } +); diff --git a/test/parallel/test-event-emitter-max-listeners.js b/test/parallel/test-event-emitter-max-listeners.js index cf8c072271db35..319e0dffd6494d 100644 --- a/test/parallel/test-event-emitter-max-listeners.js +++ b/test/parallel/test-event-emitter-max-listeners.js @@ -21,7 +21,6 @@ 'use strict'; const common = require('../common'); -const assert = require('assert'); const events = require('events'); const e = new events.EventEmitter(); @@ -31,12 +30,25 @@ e.on('maxListeners', common.mustCall()); e.setMaxListeners(42); const throwsObjs = [NaN, -1, 'and even this']; -const maxError = /^TypeError: "n" argument must be a positive number$/; -const defError = /^TypeError: "defaultMaxListeners" must be a positive number$/; for (const obj of throwsObjs) { - assert.throws(() => e.setMaxListeners(obj), maxError); - assert.throws(() => events.defaultMaxListeners = obj, defError); + common.expectsError( + () => e.setMaxListeners(obj), + { + code: 'ERR_OUT_OF_RANGE', + type: TypeError, + message: 'The "n" argument is out of range' + } + ); + + common.expectsError( + () => events.defaultMaxListeners = obj, + { + code: 'ERR_OUT_OF_RANGE', + type: TypeError, + message: 'The "defaultMaxListeners" argument is out of range' + } + ); } e.emit('maxListeners'); diff --git a/test/parallel/test-event-emitter-once.js b/test/parallel/test-event-emitter-once.js index dd4f5133a198d9..35ebf72968a19a 100644 --- a/test/parallel/test-event-emitter-once.js +++ b/test/parallel/test-event-emitter-once.js @@ -50,11 +50,15 @@ e.once('e', common.mustCall()); e.emit('e'); // Verify that the listener must be a function -assert.throws(() => { +common.expectsError(() => { const ee = new EventEmitter(); ee.once('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "listener" argument must be of type function' +}); { // once() has different code paths based on the number of arguments being diff --git a/test/parallel/test-event-emitter-prepend.js b/test/parallel/test-event-emitter-prepend.js index b511af9ae3691d..761eb6d9049b42 100644 --- a/test/parallel/test-event-emitter-prepend.js +++ b/test/parallel/test-event-emitter-prepend.js @@ -19,11 +19,15 @@ myEE.prependOnceListener('foo', myEE.emit('foo'); // Verify that the listener must be a function -assert.throws(() => { +common.expectsError(() => { const ee = new EventEmitter(); ee.prependOnceListener('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "listener" argument must be of type function' +}); // Test fallback if prependListener is undefined. const stream = require('stream'); diff --git a/test/parallel/test-event-emitter-remove-listeners.js b/test/parallel/test-event-emitter-remove-listeners.js index d1c01e0bc3cfae..d4fb542270d85f 100644 --- a/test/parallel/test-event-emitter-remove-listeners.js +++ b/test/parallel/test-event-emitter-remove-listeners.js @@ -144,11 +144,15 @@ function listener2() {} } // Verify that the removed listener must be a function -assert.throws(() => { +common.expectsError(() => { const ee = new EventEmitter(); ee.removeListener('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "listener" argument must be of type function' +}); { const ee = new EventEmitter();