Skip to content

Commit

Permalink
events: try using map
Browse files Browse the repository at this point in the history
  • Loading branch information
rluvaton committed Oct 27, 2023
1 parent eb29e97 commit 4c02bdb
Showing 1 changed file with 74 additions and 41 deletions.
115 changes: 74 additions & 41 deletions lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const {
validateString,
} = require('internal/validators');

const kEvents = Symbol('kEvents');
const kCapture = Symbol('kCapture');
const kErrorMonitor = Symbol('events.errorMonitor');
const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
Expand Down Expand Up @@ -262,8 +263,7 @@ ObjectDefineProperty(EventEmitter.prototype, kCapture, {
enumerable: false,
});

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype[kEvents] = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
Expand All @@ -287,6 +287,40 @@ ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', {
},
});

// _events and _eventsCount are Legacy
ObjectDefineProperty(EventEmitter, '_events', {
__proto__: null,
enumerable: true,
get: function() {
// TODO - maybe in order to not break the ecosystem, create a Proxy that will update the events map
const events = this[kEvents];
if(events === undefined) {

Check failure on line 297 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Expected space(s) after "if"
return undefined;
}

return Object.fromEntries(events.entries());

Check failure on line 301 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { ObjectFromEntries } = primordials;` instead of the global
},
set: function(events) {
this[kEvents] = new Map(events);

Check failure on line 304 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { SafeMap } = primordials;` instead of the global
},
});

ObjectDefineProperty(EventEmitter, '_eventsCount', {
__proto__: null,
enumerable: true,
get: function() {
const events = this[kEvents];
if(events === undefined) {

Check failure on line 313 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Expected space(s) after "if"
return 0;
}

return events.size;
},
set: function() {
// TODO - throw, this is not supported
},
});

ObjectDefineProperties(EventEmitter, {
kMaxEventTargetListeners: {
__proto__: null,
Expand Down Expand Up @@ -339,11 +373,9 @@ EventEmitter.setMaxListeners =
// If you're updating this function definition, please also update any
// re-definitions, such as the one in the Domain module (lib/domain.js).
EventEmitter.init = function(opts) {

if (this._events === undefined ||
this._events === ObjectGetPrototypeOf(this)._events) {
this._events = { };
this._eventsCount = 0;
if (this[kEvents] === undefined ||
this[kEvents] === ObjectGetPrototypeOf(this)[kEvents]) {
this[kEvents] = new Map()

Check failure on line 378 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { SafeMap } = primordials;` instead of the global

Check failure on line 378 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Missing semicolon
}

this._maxListeners = this._maxListeners || undefined;
Expand Down Expand Up @@ -462,11 +494,11 @@ function enhanceStackTrace(err, own) {
EventEmitter.prototype.emit = function emit(type, ...args) {
let doError = (type === 'error');

const events = this._events;
const events = this[kEvents];
if (events !== undefined) {
if (doError && events[kErrorMonitor] !== undefined)
if (doError && events.has(kErrorMonitor))
this.emit(kErrorMonitor, ...args);
doError = (doError && events.error === undefined);
doError = (doError && !events.has('error'));
} else if (!doError)
return false;

Expand Down Expand Up @@ -506,7 +538,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
throw err; // Unhandled 'error' event
}

const handler = events[type];
const handler = events.get(type);

if (handler === undefined)
return false;
Expand Down Expand Up @@ -547,10 +579,9 @@ function _addListener(target, type, listener, prepend) {

checkListener(listener);

events = target._events;
events = target[kEvents];
if (events === undefined) {
events = target._events = { __proto__: null };
target._eventsCount = 0;
events = target[kEvents] = new Map();

Check failure on line 584 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { SafeMap } = primordials;` instead of the global
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
Expand All @@ -559,16 +590,15 @@ function _addListener(target, type, listener, prepend) {
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;
// this[kEvents] to be assigned to a new object
events = target[kEvents];
}
existing = events[type];
}

if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
events[type] = listener;
++target._eventsCount;
events.set(type, listener);
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
Expand Down Expand Up @@ -677,20 +707,22 @@ EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
checkListener(listener);

const events = this._events;
const events = this[kEvents];
if (events === undefined)
return this;

const list = events[type];
const list = events.get(type);
if (list === undefined)
return this;

if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = { __proto__: null };
if (this[kEvents].size === 1)
// TODO - this is no longer creating new objects
// this[kEvents].clear();
this[kEvents] = new Map();

Check failure on line 722 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { SafeMap } = primordials;` instead of the global
else {
delete events[type];
if (events.removeListener)
events.delete(type);
if (events.has('removeListener'))
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
Expand Down Expand Up @@ -735,37 +767,38 @@ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
*/
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
const events = this._events;
const events = this[kEvents];
if (events === undefined)
return this;

// Not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (!events.has('removeListener')) {
if (arguments.length === 0) {
this._events = { __proto__: null };
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = { __proto__: null };
// this[kEvents].clear();
this[kEvents]= new Map();

Check failure on line 778 in lib/events.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Operator '=' must be spaced
} else if (events.has(type)) {
if (this[kEvents].size === 1)
// this[kEvents].clear();
this[kEvents] = new Map();
else
delete events[type];
this[kEvents].delete(type);
}
return this;
}

// Emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (const key of ReflectOwnKeys(events)) {
for (const key of events.keys()) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = { __proto__: null };
this._eventsCount = 0;
// this[kEvents].clear();
this[kEvents] = new Map();
return this;
}

const listeners = events[type];
const listeners = events.get(type);

if (typeof listeners === 'function') {
this.removeListener(type, listeners);
Expand All @@ -780,12 +813,12 @@ EventEmitter.prototype.removeAllListeners =
};

function _listeners(target, type, unwrap) {
const events = target._events;
const events = target[kEvents];

if (events === undefined)
return [];

const evlistener = events[type];
const evlistener = events.get(type);
if (evlistener === undefined)
return [];

Expand Down Expand Up @@ -841,10 +874,10 @@ EventEmitter.prototype.listenerCount = listenerCount;
* @returns {number}
*/
function listenerCount(type, listener) {
const events = this._events;
const events = this[kEvents];

if (events !== undefined) {
const evlistener = events[type];
const evlistener = events.get(type);

if (typeof evlistener === 'function') {
if (listener != null) {
Expand Down Expand Up @@ -878,7 +911,7 @@ function listenerCount(type, listener) {
* @returns {any[]}
*/
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
return this[kEvents].size > 0 ? Array.from(this[kEvents].keys()) : [];
};

function arrayClone(arr) {
Expand Down

0 comments on commit 4c02bdb

Please sign in to comment.