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:
parent
e24ad97832
commit
decab712ba
@ -574,6 +574,39 @@ to indicate an unlimited number of listeners.
|
|||||||
|
|
||||||
Returns a reference to the `EventEmitter`, so that calls can be chained.
|
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
|
[`--trace-warnings`]: cli.html#cli_trace_warnings
|
||||||
[`EventEmitter.defaultMaxListeners`]: #events_eventemitter_defaultmaxlisteners
|
[`EventEmitter.defaultMaxListeners`]: #events_eventemitter_defaultmaxlisteners
|
||||||
[`domain`]: domain.html
|
[`domain`]: domain.html
|
||||||
|
@ -32,6 +32,7 @@ module.exports = EventEmitter;
|
|||||||
EventEmitter.EventEmitter = EventEmitter;
|
EventEmitter.EventEmitter = EventEmitter;
|
||||||
|
|
||||||
EventEmitter.prototype._events = undefined;
|
EventEmitter.prototype._events = undefined;
|
||||||
|
EventEmitter.prototype._eventsCount = 0;
|
||||||
EventEmitter.prototype._maxListeners = undefined;
|
EventEmitter.prototype._maxListeners = undefined;
|
||||||
|
|
||||||
// By default EventEmitters will print a warning if more than 10 listeners are
|
// By default EventEmitters will print a warning if more than 10 listeners are
|
||||||
@ -357,8 +358,8 @@ EventEmitter.prototype.removeAllListeners =
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
EventEmitter.prototype.listeners = function listeners(type) {
|
function _listeners(target, type, unwrap) {
|
||||||
const events = this._events;
|
const events = target._events;
|
||||||
|
|
||||||
if (events === undefined)
|
if (events === undefined)
|
||||||
return [];
|
return [];
|
||||||
@ -368,9 +369,18 @@ EventEmitter.prototype.listeners = function listeners(type) {
|
|||||||
return [];
|
return [];
|
||||||
|
|
||||||
if (typeof evlistener === 'function')
|
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) {
|
EventEmitter.listenerCount = function(emitter, type) {
|
||||||
|
1
lib/internal/bootstrap_node.js
vendored
1
lib/internal/bootstrap_node.js
vendored
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
function startup() {
|
function startup() {
|
||||||
const EventEmitter = NativeModule.require('events');
|
const EventEmitter = NativeModule.require('events');
|
||||||
process._eventsCount = 0;
|
|
||||||
|
|
||||||
const origProcProto = Object.getPrototypeOf(process);
|
const origProcProto = Object.getPrototypeOf(process);
|
||||||
Object.setPrototypeOf(origProcProto, EventEmitter.prototype);
|
Object.setPrototypeOf(origProcProto, EventEmitter.prototype);
|
||||||
|
13
lib/vm.js
13
lib/vm.js
@ -56,7 +56,7 @@ const realRunInThisContext = Script.prototype.runInThisContext;
|
|||||||
const realRunInContext = Script.prototype.runInContext;
|
const realRunInContext = Script.prototype.runInContext;
|
||||||
|
|
||||||
Script.prototype.runInThisContext = function(options) {
|
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]);
|
return sigintHandlersWrap(realRunInThisContext, this, [options]);
|
||||||
} else {
|
} else {
|
||||||
return realRunInThisContext.call(this, options);
|
return realRunInThisContext.call(this, options);
|
||||||
@ -64,7 +64,7 @@ Script.prototype.runInThisContext = function(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.runInContext = function(contextifiedSandbox, 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,
|
return sigintHandlersWrap(realRunInContext, this,
|
||||||
[contextifiedSandbox, options]);
|
[contextifiedSandbox, options]);
|
||||||
} else {
|
} else {
|
||||||
@ -95,14 +95,7 @@ function createScript(code, options) {
|
|||||||
// Remove all SIGINT listeners and re-attach them after the wrapped function
|
// Remove all SIGINT listeners and re-attach them after the wrapped function
|
||||||
// has executed, so that caught SIGINT are handled by the listeners again.
|
// has executed, so that caught SIGINT are handled by the listeners again.
|
||||||
function sigintHandlersWrap(fn, thisArg, argsArray) {
|
function sigintHandlersWrap(fn, thisArg, argsArray) {
|
||||||
// Using the internal list here to make sure `.once()` wrappers are used,
|
const sigintListeners = process.rawListeners('SIGINT');
|
||||||
// not the original ones.
|
|
||||||
let sigintListeners = process._events.SIGINT;
|
|
||||||
|
|
||||||
if (Array.isArray(sigintListeners))
|
|
||||||
sigintListeners = sigintListeners.slice();
|
|
||||||
else
|
|
||||||
sigintListeners = [sigintListeners];
|
|
||||||
|
|
||||||
process.removeAllListeners('SIGINT');
|
process.removeAllListeners('SIGINT');
|
||||||
|
|
||||||
|
@ -145,7 +145,6 @@ class ModuleWrap;
|
|||||||
V(env_pairs_string, "envPairs") \
|
V(env_pairs_string, "envPairs") \
|
||||||
V(errno_string, "errno") \
|
V(errno_string, "errno") \
|
||||||
V(error_string, "error") \
|
V(error_string, "error") \
|
||||||
V(events_string, "_events") \
|
|
||||||
V(exiting_string, "_exiting") \
|
V(exiting_string, "_exiting") \
|
||||||
V(exit_code_string, "exitCode") \
|
V(exit_code_string, "exitCode") \
|
||||||
V(exit_string, "exit") \
|
V(exit_string, "exit") \
|
||||||
|
@ -3354,12 +3354,6 @@ void SetupProcessObject(Environment* env,
|
|||||||
env->SetMethod(process, "_setupNextTick", SetupNextTick);
|
env->SetMethod(process, "_setupNextTick", SetupNextTick);
|
||||||
env->SetMethod(process, "_setupPromises", SetupPromises);
|
env->SetMethod(process, "_setupPromises", SetupPromises);
|
||||||
env->SetMethod(process, "_setupDomainUse", SetupDomainUse);
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,3 +82,22 @@ function listener2() {}
|
|||||||
const s = new TestStream();
|
const s = new TestStream();
|
||||||
assert.deepStrictEqual(s.listeners('foo'), []);
|
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);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user