From dd5206820c3ae91a4a397750a646b25de3dfbfea Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Sun, 29 Oct 2023 17:29:59 +0100 Subject: [PATCH] stream: pre-allocate _events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/50428 Reviewed-By: Matteo Collina Reviewed-By: Yagiz Nizipli Reviewed-By: Vinícius Lourenço Claro Cardoso Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell --- 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 f42a11ab3e701d..1950f19ddcb75d 100644 --- a/lib/events.js +++ b/lib/events.js @@ -86,6 +86,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'); @@ -343,6 +344,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; @@ -685,9 +689,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); @@ -749,6 +757,7 @@ EventEmitter.prototype.removeAllListeners = else delete events[type]; } + this[kShapeMode] = false; return this; } @@ -761,6 +770,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 92dd7d68301400..3800399c82ad62 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -319,6 +319,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 4facf8c5cd80b8..17fc7bbbbf5b65 100644 --- a/lib/internal/streams/writable.js +++ b/lib/internal/streams/writable.js @@ -385,6 +385,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; }