events: remove reaches into _events internals

Refactor lib & src code to eliminate all deep reaches into the
internal _events dictionary object, instead use available APIs
and add an extra method to EventEmitter: rawListeners.

PR-URL: https://github.com/nodejs/node/pull/17440
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Anatoli Papirovski 2017-11-25 13:26:28 -05:00
parent e24ad97832
commit decab712ba
No known key found for this signature in database
GPG Key ID: 614E2E1ABEB4B2C0
7 changed files with 69 additions and 22 deletions

View File

@ -574,6 +574,39 @@ to indicate an unlimited number of listeners.
Returns a reference to the `EventEmitter`, so that calls can be chained.
### emitter.rawListeners(eventName)
<!-- YAML
added: REPLACEME
-->
- `eventName` {any}
Returns a copy of the array of listeners for the event named `eventName`,
including any wrappers (such as those created by `.once`).
```js
const emitter = new EventEmitter();
emitter.once('log', () => console.log('log once'));
// Returns a new Array with a function `onceWrapper` which has a property
// `listener` which contains the original listener bound above
const listeners = emitter.rawListeners('log');
const logFnWrapper = listeners[0];
// logs "log once" to the console and does not unbind the `once` event
logFnWrapper.listener();
// logs "log once" to the console and removes the listener
logFnWrapper();
emitter.on('log', () => console.log('log persistently'));
// will return a new Array with a single function bound by `on` above
const newListeners = emitter.rawListeners('log');
// logs "log persistently" twice
newListeners[0]();
emitter.emit('log');
```
[`--trace-warnings`]: cli.html#cli_trace_warnings
[`EventEmitter.defaultMaxListeners`]: #events_eventemitter_defaultmaxlisteners
[`domain`]: domain.html

View File

@ -32,6 +32,7 @@ module.exports = EventEmitter;
EventEmitter.EventEmitter = EventEmitter;
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
@ -357,8 +358,8 @@ EventEmitter.prototype.removeAllListeners =
return this;
};
EventEmitter.prototype.listeners = function listeners(type) {
const events = this._events;
function _listeners(target, type, unwrap) {
const events = target._events;
if (events === undefined)
return [];
@ -368,9 +369,18 @@ EventEmitter.prototype.listeners = function listeners(type) {
return [];
if (typeof evlistener === 'function')
return [evlistener.listener || evlistener];
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrapListeners(evlistener);
return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {

View File

@ -13,7 +13,6 @@
function startup() {
const EventEmitter = NativeModule.require('events');
process._eventsCount = 0;
const origProcProto = Object.getPrototypeOf(process);
Object.setPrototypeOf(origProcProto, EventEmitter.prototype);

View File

@ -56,7 +56,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') > 0) {
return sigintHandlersWrap(realRunInThisContext, this, [options]);
} else {
return realRunInThisContext.call(this, options);
@ -64,7 +64,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') > 0) {
return sigintHandlersWrap(realRunInContext, this,
[contextifiedSandbox, options]);
} else {
@ -95,14 +95,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.rawListeners('SIGINT');
process.removeAllListeners('SIGINT');

View File

@ -145,7 +145,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") \

View File

@ -3354,12 +3354,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<Object> events_obj = Object::New(env->isolate());
CHECK(events_obj->SetPrototype(env->context(),
Null(env->isolate())).FromJust());
process->Set(env->events_string(), events_obj);
}

View File

@ -82,3 +82,22 @@ function listener2() {}
const s = new TestStream();
assert.deepStrictEqual(s.listeners('foo'), []);
}
{
const ee = new events.EventEmitter();
ee.on('foo', listener);
const wrappedListener = ee.rawListeners('foo');
assert.strictEqual(wrappedListener.length, 1);
assert.strictEqual(wrappedListener[0], listener);
assert.notStrictEqual(wrappedListener, ee.rawListeners('foo'));
ee.once('foo', listener);
const wrappedListeners = ee.rawListeners('foo');
assert.strictEqual(wrappedListeners.length, 2);
assert.strictEqual(wrappedListeners[0], listener);
assert.notStrictEqual(wrappedListeners[1], listener);
assert.strictEqual(wrappedListeners[1].listener, listener);
assert.notStrictEqual(wrappedListeners, ee.rawListeners('foo'));
ee.emit('foo');
assert.strictEqual(wrappedListeners.length, 2);
assert.strictEqual(wrappedListeners[1].listener, listener);
}