diff --git a/benchmark/events/ee-add-remove.js b/benchmark/events/ee-add-remove.js index 7b6ec35f29b636..ea6268bd011c57 100644 --- a/benchmark/events/ee-add-remove.js +++ b/benchmark/events/ee-add-remove.js @@ -2,18 +2,30 @@ const common = require('../common.js'); const events = require('events'); -const bench = common.createBenchmark(main, { n: [25e4] }); +const bench = common.createBenchmark(main, { + n: [5e6], + events: [0, 5], + listeners: [1, 5] +}); function main(conf) { - const n = conf.n | 0; + let n = conf.n | 0; + const eventsCOunt = conf.events | 0; + const listenersCount = conf.listeners | 0; const ee = new events.EventEmitter(); const listeners = []; + if (listenersCount === 1) + n *= 2; + var k; - for (k = 0; k < 10; k += 1) + for (k = 0; k < listenersCount; k += 1) listeners.push(function() {}); + for (k = 0; k < eventsCOunt; k++) + ee.on(`dummyunused${k}`, () => {}); + bench.start(); for (var i = 0; i < n; i += 1) { const dummy = (i % 2 === 0) ? 'dummy0' : 'dummy1'; diff --git a/benchmark/events/ee-emit-multi.js b/benchmark/events/ee-emit-multi.js new file mode 100644 index 00000000000000..b92206f8361d64 --- /dev/null +++ b/benchmark/events/ee-emit-multi.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common.js'); +const EventEmitter = require('events').EventEmitter; + +const bench = common.createBenchmark(main, { + n: [2e7], + listeners: [1, 5, 10], +}); + +function main(conf) { + var n = conf.n | 0; + const listeners = Math.max(conf.listeners | 0, 1); + + const ee = new EventEmitter(); + + if (listeners === 1) + n *= 5; + else if (listeners === 5) + n *= 2; + + for (var k = 0; k < listeners; k += 1) { + ee.on('dummy', function() {}); + ee.on(`dummy${k}`, function() {}); + } + + bench.start(); + for (var i = 0; i < n; i += 1) { + if (i % 3 === 0) + ee.emit('dummy', true, 5); + else if (i % 2 === 0) + ee.emit('dummy', true, 5, 10, false); + else + ee.emit('dummy'); + } + bench.end(n); +} diff --git a/benchmark/events/ee-emit.js b/benchmark/events/ee-emit.js index 3d7eb43b228b71..de60f938ddd5b7 100644 --- a/benchmark/events/ee-emit.js +++ b/benchmark/events/ee-emit.js @@ -3,20 +3,27 @@ const common = require('../common.js'); const EventEmitter = require('events').EventEmitter; const bench = common.createBenchmark(main, { - n: [2e6], + n: [2e7], argc: [0, 2, 4, 10], listeners: [1, 5, 10], }); function main(conf) { - const n = conf.n | 0; + var n = conf.n | 0; const argc = conf.argc | 0; const listeners = Math.max(conf.listeners | 0, 1); const ee = new EventEmitter(); - for (var k = 0; k < listeners; k += 1) + if (listeners === 1) + n *= 5; + else if (listeners === 5) + n *= 2; + + for (var k = 0; k < listeners; k += 1) { ee.on('dummy', function() {}); + ee.on(`dummy${k}`, function() {}); + } var i; switch (argc) { diff --git a/benchmark/events/ee-event-names.js b/benchmark/events/ee-event-names.js new file mode 100644 index 00000000000000..92892ba03c5ced --- /dev/null +++ b/benchmark/events/ee-event-names.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common.js'); +const EventEmitter = require('events').EventEmitter; + +const bench = common.createBenchmark(main, { n: [1e6] }); + +function main(conf) { + const n = conf.n | 0; + + const ee = new EventEmitter(); + + for (var k = 0; k < 9; k += 1) { + ee.on(`dummy0${k}`, function() {}); + ee.on(`dummy1${k}`, function() {}); + ee.on(`dummy2${k}`, function() {}); + } + + ee.removeAllListeners('dummy01'); + ee.removeAllListeners('dummy11'); + ee.removeAllListeners('dummy21'); + ee.removeAllListeners('dummy06'); + ee.removeAllListeners('dummy16'); + ee.removeAllListeners('dummy26'); + + bench.start(); + for (var i = 0; i < n; i += 1) { + ee.eventNames(); + } + bench.end(n); +} diff --git a/benchmark/events/ee-listeners-many.js b/benchmark/events/ee-listeners-many.js index 6cb0682b1ca9c3..6173244814d5ad 100644 --- a/benchmark/events/ee-listeners-many.js +++ b/benchmark/events/ee-listeners-many.js @@ -2,7 +2,7 @@ const common = require('../common.js'); const EventEmitter = require('events').EventEmitter; -const bench = common.createBenchmark(main, { n: [5e6] }); +const bench = common.createBenchmark(main, { n: [1e7] }); function main(conf) { const n = conf.n | 0; diff --git a/benchmark/events/ee-listeners.js b/benchmark/events/ee-listeners.js index dff73de0b17fc4..53e24bd750af9f 100644 --- a/benchmark/events/ee-listeners.js +++ b/benchmark/events/ee-listeners.js @@ -2,7 +2,7 @@ const common = require('../common.js'); const EventEmitter = require('events').EventEmitter; -const bench = common.createBenchmark(main, { n: [5e6] }); +const bench = common.createBenchmark(main, { n: [5e7] }); function main(conf) { const n = conf.n | 0; diff --git a/benchmark/events/ee-once.js b/benchmark/events/ee-once.js index d9e87a2b0843af..43617d68814646 100644 --- a/benchmark/events/ee-once.js +++ b/benchmark/events/ee-once.js @@ -2,19 +2,27 @@ const common = require('../common.js'); const EventEmitter = require('events').EventEmitter; -const bench = common.createBenchmark(main, { n: [2e7] }); +const bench = common.createBenchmark(main, { + n: [5e6], + listeners: [1, 5] +}); function main(conf) { - const n = conf.n | 0; + let n = conf.n | 0; + const listeners = conf.listeners | 0; + + if (listeners === 1) + n *= 2; const ee = new EventEmitter(); function listener() {} bench.start(); - for (var i = 0; i < n; i += 1) { + for (var i = 0; i < n; ++i) { const dummy = (i % 2 === 0) ? 'dummy0' : 'dummy1'; - ee.once(dummy, listener); + for (var j = 0; j < listeners; ++j) + ee.once(dummy, listener); ee.emit(dummy); } bench.end(n); diff --git a/doc/api/events.md b/doc/api/events.md index 443137f1705e26..655c12fec7554a 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -344,7 +344,7 @@ added: v3.2.0 Returns the number of listeners listening to the event named `eventName`. -### emitter.listeners(eventName) +### emitter.listeners(eventName[, unwrap]) -- `eventName` {any} +* `eventName` {any} The name of the event. +* `unwrap` {boolean} When set to true, will return the original listeners + instead of the wrapper functions created by `.once()`. **Default:** `true` Returns a copy of the array of listeners for the event named `eventName`. diff --git a/lib/domain.js b/lib/domain.js index dc3c550866c924..4185ea3c437eaf 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -94,6 +94,9 @@ class Domain extends EventEmitter { this.members = []; asyncHook.enable(); + + this.on('newListener', setHasErrorListener); + this.on('removeListener', removeHasErrorListener); } } @@ -105,6 +108,19 @@ exports.create = exports.createDomain = function() { // the active domain is always the one that we're currently in. exports.active = null; + +function setHasErrorListener(type) { + if (type === 'error') + this._hasErrorListener = true; +} + +function removeHasErrorListener(type) { + if (type === 'error' && !this.listenerCount('error')) + this._hasErrorListener = false; +} + +Domain.prototype._hasErrorListener = false; + Domain.prototype.members = undefined; // Called by process._fatalException in case an error was thrown. diff --git a/lib/events.js b/lib/events.js index 2a83ab7dc64c7b..99ec464ffe4f9a 100644 --- a/lib/events.js +++ b/lib/events.js @@ -36,6 +36,7 @@ EventEmitter.usingDomains = false; EventEmitter.prototype.domain = undefined; EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are @@ -81,7 +82,8 @@ EventEmitter.init = function() { this._eventsCount = 0; } - this._maxListeners = this._maxListeners || undefined; + if (!this._maxListeners) + this._maxListeners = undefined; }; // Obviously not all Emitters should be limited to 10. This function allows @@ -156,12 +158,13 @@ EventEmitter.prototype.emit = function emit(type, ...args) { } if (typeof handler === 'function') { - handler.apply(this, args); + Reflect.apply(handler, this, args); } else { - const len = handler.length; - const listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].apply(this, args); + const prevEmitting = handler.emitting; + handler.emitting = true; + for (var i = 0; i < handler.length; ++i) + Reflect.apply(handler[i], this, args); + handler.emitting = prevEmitting; } if (needDomainExit) @@ -171,16 +174,13 @@ EventEmitter.prototype.emit = function emit(type, ...args) { }; function _addListener(target, type, listener, prepend) { - var m; - var events; - var existing; - if (typeof listener !== 'function') { const errors = lazyErrors(); throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'listener', 'Function'); } - events = target._events; + var events = target._events; + var list; if (events === undefined) { events = target._events = Object.create(null); target._eventsCount = 0; @@ -188,49 +188,53 @@ function _addListener(target, type, listener, prepend) { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener !== undefined) { - target.emit('newListener', type, - listener.listener ? listener.listener : listener); + target.emit('newListener', type, listener.listener || listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object events = target._events; } - existing = events[type]; + list = events[type]; } - if (existing === undefined) { + if (list === undefined) { // Optimize the case of one listener. Don't need the extra array object. - existing = events[type] = listener; + events[type] = listener; ++target._eventsCount; - } else { - if (typeof existing === 'function') { - // Adding the second element, need to change to array. - existing = events[type] = - prepend ? [listener, existing] : [existing, listener]; - // If we've already got an array, just append. - } else if (prepend) { - existing.unshift(listener); - } else { - existing.push(listener); - } + return target; + } - // Check for listener leak - if (!existing.warned) { - 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 ' + - 'increase limit'); - w.name = 'MaxListenersExceededWarning'; - w.emitter = target; - w.type = type; - w.count = existing.length; - process.emitWarning(w); - } + if (typeof list === 'function') { + // Adding the second element, need to change to array. + list = events[type] = prepend ? [listener, list] : [list, listener]; + } else if (list.emitting) { + const { warned } = list; + list = events[type] = + arrayCloneWithElement(list, listener, prepend ? 1 : 0); + if (warned) { + list.warned = true; + return target; } + } else if (prepend) { + list.unshift(listener); + } else { + list.push(listener); + } + + // Check for listener leak + const m = $getMaxListeners(target); + if (m > 0 && list.length > m && !list.warned) { + list.warned = true; + // No error code for this since it is a Warning + const w = new Error('Possible EventEmitter memory leak detected. ' + + `${list.length} ${String(type)} listeners ` + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = list.length; + process.emitWarning(w); } return target; @@ -247,11 +251,11 @@ EventEmitter.prototype.prependListener = return _addListener(this, type, listener, true); }; -function onceWrapper(...args) { +function onceWrapper() { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; - this.listener.apply(this.target, args); + Reflect.apply(this.listener, this.target, arguments); } } @@ -286,36 +290,36 @@ EventEmitter.prototype.prependOnceListener = // Emits a 'removeListener' event if and only if the listener was removed. EventEmitter.prototype.removeListener = function removeListener(type, listener) { - var list, events, position, i, originalListener; - if (typeof listener !== 'function') { const errors = lazyErrors(); throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'listener', 'Function'); } - events = this._events; + const events = this._events; if (events === undefined) return this; - list = events[type]; + const list = events[type]; if (list === undefined) return this; if (list === listener || list.listener === listener) { - if (--this._eventsCount === 0) - this._events = Object.create(null); - else { - delete events[type]; - if (events.removeListener) - this.emit('removeListener', type, list.listener || listener); - } - } else if (typeof list !== 'function') { - position = -1; + events[type] = undefined; + --this._eventsCount; + if (events.removeListener !== undefined) + this.emit('removeListener', type, list.listener || listener); + return this; + } - for (i = list.length - 1; i >= 0; i--) { - if (list[i] === listener || list[i].listener === listener) { - originalListener = list[i].listener; + if (typeof list !== 'function') { + let position = -1; + + for (var i = list.length - 1; i >= 0; --i) { + const l = list[i]; + if (l === listener || l.listener === listener) { + if (l.listener) + listener = l.listener; position = i; break; } @@ -324,19 +328,25 @@ EventEmitter.prototype.removeListener = if (position < 0) return this; - if (position === 0) + if (list.length === 2) + events[type] = list[position ? 0 : 1]; + else if (list.emitting) { + const { warned } = list; + events[type] = sliceOne(list, position); + if (warned) + events[type].warned = true; + } else if (position === 0) list.shift(); + else if (position === list.length - 1) + list.pop(); else { if (spliceOne === undefined) spliceOne = require('internal/util').spliceOne; spliceOne(list, position); } - if (list.length === 1) - events[type] = list[0]; - if (events.removeListener !== undefined) - this.emit('removeListener', type, originalListener || listener); + this.emit('removeListener', type, listener); } return this; @@ -356,26 +366,23 @@ EventEmitter.prototype.removeAllListeners = this._events = Object.create(null); this._eventsCount = 0; } else if (events[type] !== undefined) { - if (--this._eventsCount === 0) - this._events = Object.create(null); - else - delete events[type]; + events[type] = undefined; + --this._eventsCount; } return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { - var keys = Object.keys(events); + const keys = Reflect.ownKeys(events); var key; for (i = 0; i < keys.length; ++i) { key = keys[i]; - if (key === 'removeListener') continue; + if (key === 'removeListener' || events[key] === undefined) + continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); - this._events = Object.create(null); - this._eventsCount = 0; return this; } @@ -385,7 +392,7 @@ EventEmitter.prototype.removeAllListeners = this.removeListener(type, listeners); } else if (listeners !== undefined) { // LIFO order - for (i = listeners.length - 1; i >= 0; i--) { + for (i = listeners.length - 1; i >= 0; --i) { this.removeListener(type, listeners[i]); } } @@ -393,7 +400,7 @@ EventEmitter.prototype.removeAllListeners = return this; }; -EventEmitter.prototype.listeners = function listeners(type) { +EventEmitter.prototype.listeners = function listeners(type, unwrap = true) { const events = this._events; if (events === undefined) @@ -406,7 +413,7 @@ EventEmitter.prototype.listeners = function listeners(type) { if (typeof evlistener === 'function') return [evlistener.listener || evlistener]; - return unwrapListeners(evlistener); + return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener); }; EventEmitter.listenerCount = function(emitter, type) { @@ -435,16 +442,50 @@ function listenerCount(type) { } EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; + if (this._eventsCount === 0) + return []; + + const events = this._events; + const keys = Reflect.ownKeys(events); + const actualEventNames = new Array(this._eventsCount); + + for (var i = 0, j = 0; i < keys.length; ++i) { + if (events[keys[i]] !== undefined) { + actualEventNames[j] = keys[i]; + j++; + } + } + + return actualEventNames; }; -function arrayClone(arr, n) { - var copy = new Array(n); - for (var i = 0; i < n; ++i) +function arrayClone(arr) { + const copy = new Array(arr.length); + for (var i = 0; i < arr.length; ++i) copy[i] = arr[i]; return copy; } +function arrayCloneWithElement(arr, element, prepend) { + const len = arr.length; + const copy = new Array(len + 1); + for (var i = 0 + prepend; i < len + prepend; ++i) + copy[i] = arr[i - prepend]; + copy[prepend ? 0 : len] = element; + return copy; +} + +function sliceOne(arr, index) { + const len = arr.length - 1; + const copy = new Array(len); + for (var i = 0, offset = 0; i < len; ++i) { + if (i === index) + offset = 1; + copy[i] = arr[i + offset]; + } + return copy; +} + function unwrapListeners(arr) { const ret = new Array(arr.length); for (var i = 0; i < ret.length; ++i) { diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index a565916193000f..65dff6714a0b4f 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -12,7 +12,6 @@ function startup() { const EventEmitter = NativeModule.require('events'); - process._eventsCount = 0; const origProcProto = Object.getPrototypeOf(process); Object.setPrototypeOf(origProcProto, EventEmitter.prototype); @@ -370,17 +369,17 @@ const { kAfter, kExecutionAsyncId, kInitTriggerAsyncId } = async_wrap.constants; - process._fatalException = function(er) { + process._fatalException = function(er, shouldCatch = true) { var caught; // It's possible that kInitTriggerAsyncId was set for a constructor call // that threw and was never cleared. So clear it now. async_id_fields[kInitTriggerAsyncId] = 0; - if (process.domain && process.domain._errorHandler) + if (shouldCatch && process.domain && process.domain._errorHandler) caught = process.domain._errorHandler(er); - if (!caught) + if (shouldCatch && !caught) caught = process.emit('uncaughtException', er); // If someone handled it, then great. otherwise, die in C++ land diff --git a/lib/vm.js b/lib/vm.js index b34f10dbee5ff8..5c202ffdd54431 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -43,7 +43,7 @@ const realRunInThisContext = Script.prototype.runInThisContext; const realRunInContext = Script.prototype.runInContext; Script.prototype.runInThisContext = function(options) { - if (options && options.breakOnSigint && process._events.SIGINT) { + if (options && options.breakOnSigint && process.listenerCount('SIGINT')) { return sigintHandlersWrap(realRunInThisContext, this, [options]); } else { return realRunInThisContext.call(this, options); @@ -51,7 +51,7 @@ Script.prototype.runInThisContext = function(options) { }; Script.prototype.runInContext = function(contextifiedSandbox, options) { - if (options && options.breakOnSigint && process._events.SIGINT) { + if (options && options.breakOnSigint && process.listenerCount('SIGINT')) { return sigintHandlersWrap(realRunInContext, this, [contextifiedSandbox, options]); } else { @@ -82,14 +82,7 @@ function createScript(code, options) { // Remove all SIGINT listeners and re-attach them after the wrapped function // has executed, so that caught SIGINT are handled by the listeners again. function sigintHandlersWrap(fn, thisArg, argsArray) { - // Using the internal list here to make sure `.once()` wrappers are used, - // not the original ones. - let sigintListeners = process._events.SIGINT; - - if (Array.isArray(sigintListeners)) - sigintListeners = sigintListeners.slice(); - else - sigintListeners = [sigintListeners]; + const sigintListeners = process.listeners('SIGINT', false); process.removeAllListeners('SIGINT'); diff --git a/src/async_wrap.cc b/src/async_wrap.cc index c5e97bd4a66714..4eda169416ec21 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -154,8 +154,7 @@ static void DestroyAsyncIdsCallback(Environment* env, void* data) { env->context(), Undefined(env->isolate()), 1, &async_id_value); if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); + FatalException(env->isolate(), try_catch, false); UNREACHABLE(); } } @@ -175,8 +174,7 @@ void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) { MaybeLocal ar = fn->Call( env->context(), Undefined(env->isolate()), 1, &async_id_value); if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); + FatalException(env->isolate(), try_catch, false); UNREACHABLE(); } } @@ -209,8 +207,7 @@ void AsyncWrap::EmitBefore(Environment* env, double async_id) { MaybeLocal ar = fn->Call( env->context(), Undefined(env->isolate()), 1, &async_id_value); if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); + FatalException(env->isolate(), try_catch, false); UNREACHABLE(); } } @@ -245,8 +242,7 @@ void AsyncWrap::EmitAfter(Environment* env, double async_id) { MaybeLocal ar = fn->Call( env->context(), Undefined(env->isolate()), 1, &async_id_value); if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); + FatalException(env->isolate(), try_catch, false); UNREACHABLE(); } } @@ -753,8 +749,7 @@ void AsyncWrap::EmitAsyncInit(Environment* env, env->context(), object, arraysize(argv), argv); if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); + FatalException(env->isolate(), try_catch, false); } } diff --git a/src/env.h b/src/env.h index f5161b9f8a9959..9f11123e0fefc0 100644 --- a/src/env.h +++ b/src/env.h @@ -146,7 +146,6 @@ class ModuleWrap; V(env_pairs_string, "envPairs") \ V(errno_string, "errno") \ V(error_string, "error") \ - V(events_string, "_events") \ V(exiting_string, "_exiting") \ V(exit_code_string, "exitCode") \ V(exit_string, "exit") \ diff --git a/src/node.cc b/src/node.cc index 57c1a4cc8a7f1e..f4c3f218c4b907 100644 --- a/src/node.cc +++ b/src/node.cc @@ -779,28 +779,6 @@ void* ArrayBufferAllocator::Allocate(size_t size) { namespace { -bool DomainHasErrorHandler(const Environment* env, - const Local& domain) { - HandleScope scope(env->isolate()); - - Local domain_event_listeners_v = domain->Get(env->events_string()); - if (!domain_event_listeners_v->IsObject()) - return false; - - Local domain_event_listeners_o = - domain_event_listeners_v.As(); - - Local domain_error_listeners_v = - domain_event_listeners_o->Get(env->error_string()); - - if (domain_error_listeners_v->IsFunction() || - (domain_error_listeners_v->IsArray() && - domain_error_listeners_v.As()->Length() > 0)) - return true; - - return false; -} - bool DomainsStackHasErrorHandler(const Environment* env) { HandleScope scope(env->isolate()); @@ -818,7 +796,12 @@ bool DomainsStackHasErrorHandler(const Environment* env) { return false; Local domain = domain_v.As(); - if (DomainHasErrorHandler(env, domain)) + + Local has_error_handler = domain->Get( + env->context(), OneByteString(env->isolate(), + "_hasErrorListener")).ToLocalChecked(); + + if (has_error_handler->IsTrue()) return true; } @@ -2401,7 +2384,8 @@ NO_RETURN void FatalError(const char* location, const char* message) { void FatalException(Isolate* isolate, Local error, - Local message) { + Local message, + bool should_catch) { HandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); @@ -2424,9 +2408,14 @@ void FatalException(Isolate* isolate, // Do not call FatalException when _fatalException handler throws fatal_try_catch.SetVerbose(false); + Local argv[2] = { + error, + Boolean::New(env->isolate(), should_catch) + }; + // this will return true if the JS layer handled it, false otherwise - Local caught = - fatal_exception_function->Call(process_object, 1, &error); + Local caught = fatal_exception_function->Call( + env->context(), process_object, 2, argv).FromMaybe(Local()); if (fatal_try_catch.HasCaught()) { // the fatal exception function threw, so we must exit @@ -2449,10 +2438,27 @@ void FatalException(Isolate* isolate, } -void FatalException(Isolate* isolate, const TryCatch& try_catch) { +void FatalException(Isolate* isolate, + const TryCatch& try_catch, + bool should_catch) { + HandleScope scope(isolate); + if (!try_catch.IsVerbose()) { + FatalException(isolate, + try_catch.Exception(), + try_catch.Message(), + should_catch); + } +} + + +void FatalException(Isolate* isolate, + const TryCatch& try_catch) { HandleScope scope(isolate); if (!try_catch.IsVerbose()) { - FatalException(isolate, try_catch.Exception(), try_catch.Message()); + FatalException(isolate, + try_catch.Exception(), + try_catch.Message(), + true); } } @@ -2460,28 +2466,10 @@ void FatalException(Isolate* isolate, const TryCatch& try_catch) { static void OnMessage(Local message, Local error) { // The current version of V8 sends messages for errors only // (thus `error` is always set). - FatalException(Isolate::GetCurrent(), error, message); + FatalException(Isolate::GetCurrent(), error, message, true); } -void ClearFatalExceptionHandlers(Environment* env) { - Local process = env->process_object(); - Local events = - process->Get(env->context(), env->events_string()).ToLocalChecked(); - - if (events->IsObject()) { - events.As()->Set( - env->context(), - OneByteString(env->isolate(), "uncaughtException"), - Undefined(env->isolate())).FromJust(); - } - - process->Set( - env->context(), - env->domain_string(), - Undefined(env->isolate())).FromJust(); -} - // Call process.emitWarning(str), fmt is a snprintf() format string void ProcessEmitWarning(Environment* env, const char* fmt, ...) { char warning[1024]; @@ -3348,12 +3336,6 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "_setupNextTick", SetupNextTick); env->SetMethod(process, "_setupPromises", SetupPromises); env->SetMethod(process, "_setupDomainUse", SetupDomainUse); - - // pre-set _events object for faster emit checks - Local events_obj = Object::New(env->isolate()); - CHECK(events_obj->SetPrototype(env->context(), - Null(env->isolate())).FromJust()); - process->Set(env->events_string(), events_obj); } diff --git a/src/node.h b/src/node.h index 583e58cea47383..a10bee2f66b26f 100644 --- a/src/node.h +++ b/src/node.h @@ -366,6 +366,10 @@ NODE_DEPRECATED("Use ParseEncoding(isolate, ...)", NODE_EXTERN void FatalException(v8::Isolate* isolate, const v8::TryCatch& try_catch); +void FatalException(v8::Isolate* isolate, + const v8::TryCatch& try_catch, + bool should_catch); + NODE_DEPRECATED("Use FatalException(isolate, ...)", inline void FatalException(const v8::TryCatch& try_catch) { return FatalException(v8::Isolate::GetCurrent(), try_catch); diff --git a/src/node_internals.h b/src/node_internals.h index b7d032c68d151d..9a932df2348bdc 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -330,11 +330,6 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land. }; -// Clear any domain and/or uncaughtException handlers to force the error's -// propagation and shutdown the process. Use this to force the process to exit -// by clearing all callbacks that could handle the error. -void ClearFatalExceptionHandlers(Environment* env); - namespace Buffer { v8::MaybeLocal Copy(Environment* env, const char* data, size_t len); v8::MaybeLocal New(Environment* env, size_t size); diff --git a/src/node_url.cc b/src/node_url.cc index 67c6986da876c8..78fd0c1f7cde0a 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -2183,8 +2183,7 @@ const Local URL::ToObject(Environment* env) const { ->Call(env->context(), undef, 9, argv); if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(isolate, try_catch); + FatalException(isolate, try_catch, false); } return ret.ToLocalChecked(); diff --git a/test/parallel/test-process-emit.js b/test/parallel/test-process-emit.js index f4f6d546f15cfa..f7dc5ef615ce1a 100644 --- a/test/parallel/test-process-emit.js +++ b/test/parallel/test-process-emit.js @@ -19,4 +19,5 @@ process.emit('normal', 'normalData'); process.emit(sym, 'symbolData'); process.emit('SIGPIPE', 'signalData'); -assert.strictEqual(isNaN(process._eventsCount), false); +assert.strictEqual(process._eventsCount, + Reflect.ownKeys(process._events).length); diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index fd622f8cabc564..59cf95a8efb251 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -39,7 +39,8 @@ class FakeInput extends EventEmitter { function isWarned(emitter) { for (const name in emitter) { const listeners = emitter[name]; - if (listeners.warned) return true; + if (listeners && listeners.warned) + return true; } return false; }