From 01267246d12b2833c1138d3d906c9ac982c032ae Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Fri, 27 Oct 2023 16:27:36 +0200 Subject: [PATCH] stream: pre-allocate _events PR-URL: https://github.com/nodejs/node/pull/50428 --- lib/events.js | 14 ++++++++++++-- lib/internal/streams/duplex.js | 18 ++++++++++++++++++ lib/internal/streams/readable.js | 15 +++++++++++++++ lib/internal/streams/writable.js | 11 +++++++++++ test/parallel/test-readline-interface.js | 2 +- .../test-readline-promises-interface.js | 2 +- 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/lib/events.js b/lib/events.js index a1837cc1a9107e..3ba2484ece938f 100644 --- a/lib/events.js +++ b/lib/events.js @@ -87,6 +87,7 @@ const { const kCapture = Symbol('kCapture'); const kErrorMonitor = Symbol('events.errorMonitor'); +const kShapeMode = Symbol('shapeMode'); const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners'); const kMaxEventTargetListenersWarned = Symbol('events.maxEventTargetListenersWarned'); @@ -344,6 +345,9 @@ EventEmitter.init = function(opts) { this._events === ObjectGetPrototypeOf(this)._events) { this._events = { __proto__: null }; this._eventsCount = 0; + this[kShapeMode] = false; + } else { + this[kShapeMode] = true; } this._maxListeners = this._maxListeners || undefined; @@ -686,9 +690,13 @@ EventEmitter.prototype.removeListener = return this; if (list === listener || list.listener === listener) { - if (--this._eventsCount === 0) + this._eventsCount -= 1; + + if (this[kShapeMode]) { + events[type] = undefined; + } else if (this._eventsCount === 0) { this._events = { __proto__: null }; - else { + } else { delete events[type]; if (events.removeListener) this.emit('removeListener', type, list.listener || listener); @@ -750,6 +758,7 @@ EventEmitter.prototype.removeAllListeners = else delete events[type]; } + this[kShapeMode] = false; return this; } @@ -762,6 +771,7 @@ EventEmitter.prototype.removeAllListeners = this.removeAllListeners('removeListener'); this._events = { __proto__: null }; this._eventsCount = 0; + this[kShapeMode] = false; return this; } diff --git a/lib/internal/streams/duplex.js b/lib/internal/streams/duplex.js index 834d875be6c4d9..35f6ff4b199de1 100644 --- a/lib/internal/streams/duplex.js +++ b/lib/internal/streams/duplex.js @@ -63,6 +63,24 @@ function Duplex(options) { if (!(this instanceof Duplex)) return new Duplex(options); + this._events ??= { + close: undefined, + error: undefined, + prefinish: undefined, + finish: undefined, + drain: undefined, + data: undefined, + end: undefined, + readable: undefined, + // Skip uncommon events... + // pause: undefined, + // resume: undefined, + // pipe: undefined, + // unpipe: undefined, + // [destroyImpl.kConstruct]: undefined, + // [destroyImpl.kDestroy]: undefined, + }; + this._readableState = new Readable.ReadableState(options, this, true); this._writableState = new Writable.WritableState(options, this, true); diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index 80798a35dcb34f..d22490f38d6229 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -316,6 +316,21 @@ function Readable(options) { if (!(this instanceof Readable)) return new Readable(options); + this._events ??= { + close: undefined, + error: undefined, + data: undefined, + end: undefined, + readable: undefined, + // Skip uncommon events... + // pause: undefined, + // resume: undefined, + // pipe: undefined, + // unpipe: undefined, + // [destroyImpl.kConstruct]: undefined, + // [destroyImpl.kDestroy]: undefined, + }; + this._readableState = new ReadableState(options, this, false); if (options) { diff --git a/lib/internal/streams/writable.js b/lib/internal/streams/writable.js index 74573033eaa44f..69e6c51eb3142b 100644 --- a/lib/internal/streams/writable.js +++ b/lib/internal/streams/writable.js @@ -382,6 +382,17 @@ function Writable(options) { if (!(this instanceof Writable)) return new Writable(options); + this._events ??= { + close: undefined, + error: undefined, + prefinish: undefined, + finish: undefined, + drain: undefined, + // Skip uncommon events... + // [destroyImpl.kConstruct]: undefined, + // [destroyImpl.kDestroy]: undefined, + }; + this._writableState = new WritableState(options, this, false); if (options) { diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index b5ffb490fba0fa..a0946a370f4c11 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -44,7 +44,7 @@ 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; } diff --git a/test/parallel/test-readline-promises-interface.js b/test/parallel/test-readline-promises-interface.js index 2a8c5aae4e3949..b7ce0c4ff20d9a 100644 --- a/test/parallel/test-readline-promises-interface.js +++ b/test/parallel/test-readline-promises-interface.js @@ -22,7 +22,7 @@ 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; }