@@ -86,7 +86,6 @@ const { addAbortListener } = require('internal/events/abort_listener');
8686
8787const kCapture = Symbol ( 'kCapture' ) ;
8888const kErrorMonitor = Symbol ( 'events.errorMonitor' ) ;
89- const kShapeMode = Symbol ( 'shapeMode' ) ;
9089const kEmitting = Symbol ( 'events.emitting' ) ;
9190const kMaxEventTargetListeners = Symbol ( 'events.maxEventTargetListeners' ) ;
9291const kMaxEventTargetListenersWarned =
@@ -335,9 +334,6 @@ EventEmitter.init = function(opts) {
335334 this . _events === ObjectGetPrototypeOf ( this ) . _events ) {
336335 this . _events = { __proto__ : null } ;
337336 this . _eventsCount = 0 ;
338- this [ kShapeMode ] = false ;
339- } else {
340- this [ kShapeMode ] = true ;
341337 }
342338
343339 this . _maxListeners ||= undefined ;
@@ -446,6 +442,39 @@ function enhanceStackTrace(err, own) {
446442 return err . stack + sep + ArrayPrototypeJoin ( ownStack , '\n' ) ;
447443}
448444
445+ function getUnhandledErrorException ( ee , args ) {
446+ let er ;
447+ if ( args . length > 0 )
448+ er = args [ 0 ] ;
449+ if ( er instanceof Error ) {
450+ try {
451+ const capture = { } ;
452+ ErrorCaptureStackTrace ( capture , EventEmitter . prototype . emit ) ;
453+ ObjectDefineProperty ( er , kEnhanceStackBeforeInspector , {
454+ __proto__ : null ,
455+ value : FunctionPrototypeBind ( enhanceStackTrace , ee , er , capture ) ,
456+ configurable : true ,
457+ } ) ;
458+ } catch {
459+ // Continue regardless of error.
460+ }
461+
462+ return er ;
463+ }
464+
465+ let stringifiedEr ;
466+ try {
467+ stringifiedEr = inspect ( er ) ;
468+ } catch {
469+ stringifiedEr = er ;
470+ }
471+
472+ // At least give some kind of context to the user
473+ const err = new ERR_UNHANDLED_ERROR ( stringifiedEr ) ;
474+ err . context = er ;
475+ return err ;
476+ }
477+
449478/**
450479 * Synchronously calls each of the listeners registered
451480 * for the event.
@@ -466,38 +495,10 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
466495
467496 // If there is no 'error' event listener then throw.
468497 if ( doError ) {
469- let er ;
470- if ( args . length > 0 )
471- er = args [ 0 ] ;
472- if ( er instanceof Error ) {
473- try {
474- const capture = { } ;
475- ErrorCaptureStackTrace ( capture , EventEmitter . prototype . emit ) ;
476- ObjectDefineProperty ( er , kEnhanceStackBeforeInspector , {
477- __proto__ : null ,
478- value : FunctionPrototypeBind ( enhanceStackTrace , this , er , capture ) ,
479- configurable : true ,
480- } ) ;
481- } catch {
482- // Continue regardless of error.
483- }
484-
485- // Note: The comments on the `throw` lines are intentional, they show
486- // up in Node's output if this results in an unhandled exception.
487- throw er ; // Unhandled 'error' event
488- }
489-
490- let stringifiedEr ;
491- try {
492- stringifiedEr = inspect ( er ) ;
493- } catch {
494- stringifiedEr = er ;
495- }
496-
497- // At least give some kind of context to the user
498- const err = new ERR_UNHANDLED_ERROR ( stringifiedEr ) ;
499- err . context = er ;
500- throw err ; // Unhandled 'error' event
498+ const er = getUnhandledErrorException ( this , args ) ;
499+ // Note: The comments on the `throw` lines are intentional, they show
500+ // up in Node's output if this results in an unhandled exception.
501+ throw er ; // Unhandled 'error' event
501502 }
502503
503504 const handler = events [ type ] ;
@@ -584,20 +585,23 @@ function _addListener(target, type, listener, prepend) {
584585
585586 // Check for listener leak
586587 m = _getMaxListeners ( target ) ;
587- if ( m > 0 && existing . length > m && ! existing . warned ) {
588- existing . warned = true ;
589- // No error code for this since it is a Warning
590- const w = genericNodeError (
591- `Possible EventEmitter memory leak detected. ${ existing . length } ${ String ( type ) } listeners ` +
592- `added to ${ inspect ( target , { depth : - 1 } ) } . MaxListeners is ${ m } . Use emitter.setMaxListeners() to increase limit` ,
593- { name : 'MaxListenersExceededWarning' , emitter : target , type : type , count : existing . length } ) ;
594- process . emitWarning ( w ) ;
595- }
588+ if ( m > 0 && existing . length > m && ! existing . warned )
589+ warnMaxListenersExceeded ( target , type , existing , m ) ;
596590 }
597591
598592 return target ;
599593}
600594
595+ function warnMaxListenersExceeded ( target , type , existing , m ) {
596+ existing . warned = true ;
597+ // No error code for this since it is a Warning
598+ const w = genericNodeError (
599+ `Possible EventEmitter memory leak detected. ${ existing . length } ${ String ( type ) } listeners ` +
600+ `added to ${ inspect ( target , { depth : - 1 } ) } . MaxListeners is ${ m } . Use emitter.setMaxListeners() to increase limit` ,
601+ { name : 'MaxListenersExceededWarning' , emitter : target , type : type , count : existing . length } ) ;
602+ process . emitWarning ( w ) ;
603+ }
604+
601605/**
602606 * Adds a listener to the event emitter.
603607 * @param {string | symbol } type
@@ -622,22 +626,16 @@ EventEmitter.prototype.prependListener =
622626 return _addListener ( this , type , listener , true ) ;
623627 } ;
624628
625- function onceWrapper ( ) {
626- if ( ! this . fired ) {
627- this . target . removeListener ( this . type , this . wrapFn ) ;
628- this . fired = true ;
629- if ( arguments . length === 0 )
630- return this . listener . call ( this . target ) ;
631- return ReflectApply ( this . listener , this . target , arguments ) ;
632- }
633- }
634-
635629function _onceWrap ( target , type , listener ) {
636- const state = { fired : false , wrapFn : undefined , target, type, listener } ;
637- const wrapped = onceWrapper . bind ( state ) ;
638- wrapped . listener = listener ;
639- state . wrapFn = wrapped ;
640- return wrapped ;
630+ let fired = false ;
631+ function wrapper ( ...args ) {
632+ if ( fired ) return ;
633+ fired = true ;
634+ target . removeListener ( type , wrapper ) ;
635+ return ReflectApply ( listener , target , args ) ;
636+ }
637+ wrapper . listener = listener ;
638+ return wrapper ;
641639}
642640
643641/**
@@ -689,13 +687,12 @@ EventEmitter.prototype.removeListener =
689687 if ( list === listener || list . listener === listener ) {
690688 this . _eventsCount -= 1 ;
691689
692- if ( this [ kShapeMode ] ) {
693- events [ type ] = undefined ;
694- } else if ( this . _eventsCount === 0 ) {
695- this . _events = { __proto__ : null } ;
696- } else {
697- delete events [ type ] ;
698- }
690+ // Leave the key in place with an `undefined` value: repeatedly
691+ // adding and removing a listener for the same event this way keeps
692+ // the `events` object in the same shape and avoids both a `delete`
693+ // (which would put the object into dictionary mode) and allocating
694+ // a fresh object when the last listener is removed.
695+ events [ type ] = undefined ;
699696
700697 if ( events . removeListener !== undefined )
701698 this . emit ( 'removeListener' , type , list . listener || listener ) ;
@@ -755,7 +752,6 @@ EventEmitter.prototype.removeAllListeners =
755752 else
756753 delete events [ type ] ;
757754 }
758- this [ kShapeMode ] = false ;
759755 return this ;
760756 }
761757
@@ -768,7 +764,6 @@ EventEmitter.prototype.removeAllListeners =
768764 this . removeAllListeners ( 'removeListener' ) ;
769765 this . _events = { __proto__ : null } ;
770766 this . _eventsCount = 0 ;
771- this [ kShapeMode ] = false ;
772767 return this ;
773768 }
774769
@@ -868,7 +863,16 @@ EventEmitter.prototype.listenerCount = function listenerCount(type, listener) {
868863 * @returns {(string | symbol)[] }
869864 */
870865EventEmitter . prototype . eventNames = function eventNames ( ) {
871- return this . _eventsCount > 0 ? ReflectOwnKeys ( this . _events ) : [ ] ;
866+ if ( this . _eventsCount === 0 )
867+ return [ ] ;
868+ const events = this . _events ;
869+ const names = [ ] ;
870+ for ( const key of ReflectOwnKeys ( events ) ) {
871+ // Removed listeners leave the key in place with an `undefined` value.
872+ if ( events [ key ] !== undefined )
873+ ArrayPrototypePush ( names , key ) ;
874+ }
875+ return names ;
872876} ;
873877
874878function arrayClone ( arr ) {
0 commit comments