@@ -87,6 +87,7 @@ const { addAbortListener } = require('internal/events/abort_listener');
8787const kCapture = Symbol ( 'kCapture' ) ;
8888const kErrorMonitor = Symbol ( 'events.errorMonitor' ) ;
8989const kShapeMode = Symbol ( 'shapeMode' ) ;
90+ const kEmitting = Symbol ( 'events.emitting' ) ;
9091const kMaxEventTargetListeners = Symbol ( 'events.maxEventTargetListeners' ) ;
9192const kMaxEventTargetListenersWarned =
9293 Symbol ( 'events.maxEventTargetListenersWarned' ) ;
@@ -514,19 +515,22 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
514515 addCatch ( this , result , type , args ) ;
515516 }
516517 } else {
517- const len = handler . length ;
518- const listeners = arrayClone ( handler ) ;
519- for ( let i = 0 ; i < len ; ++ i ) {
520- const result = ReflectApply ( listeners [ i ] , this , args ) ;
521-
522- // We check if result is undefined first because that
523- // is the most common case so we do not pay any perf
524- // penalty.
525- // This code is duplicated because extracting it away
526- // would make it non-inlineable.
527- if ( result !== undefined && result !== null ) {
528- addCatch ( this , result , type , args ) ;
518+ handler [ kEmitting ] ++ ;
519+ try {
520+ for ( let i = 0 ; i < handler . length ; ++ i ) {
521+ const result = ReflectApply ( handler [ i ] , this , args ) ;
522+
523+ // We check if result is undefined first because that
524+ // is the most common case so we do not pay any perf
525+ // penalty.
526+ // This code is duplicated because extracting it away
527+ // would make it non-inlineable.
528+ if ( result !== undefined && result !== null ) {
529+ addCatch ( this , result , type , args ) ;
530+ }
529531 }
532+ } finally {
533+ handler [ kEmitting ] -- ;
530534 }
531535 }
532536
@@ -565,13 +569,17 @@ function _addListener(target, type, listener, prepend) {
565569 } else {
566570 if ( typeof existing === 'function' ) {
567571 // Adding the second element, need to change to array.
568- existing = events [ type ] =
569- prepend ? [ listener , existing ] : [ existing , listener ] ;
572+ existing = prepend ? [ listener , existing ] : [ existing , listener ] ;
573+ existing [ kEmitting ] = 0 ;
574+ events [ type ] = existing ;
570575 // If we've already got an array, just append.
571- } else if ( prepend ) {
572- existing . unshift ( listener ) ;
573576 } else {
574- existing . push ( listener ) ;
577+ existing = ensureMutableListenerArray ( events , type , existing ) ;
578+ if ( prepend ) {
579+ existing . unshift ( listener ) ;
580+ } else {
581+ existing . push ( listener ) ;
582+ }
575583 }
576584
577585 // Check for listener leak
@@ -674,7 +682,7 @@ EventEmitter.prototype.removeListener =
674682 if ( events === undefined )
675683 return this ;
676684
677- const list = events [ type ] ;
685+ let list = events [ type ] ;
678686 if ( list === undefined )
679687 return this ;
680688
@@ -692,6 +700,7 @@ EventEmitter.prototype.removeListener =
692700 if ( events . removeListener !== undefined )
693701 this . emit ( 'removeListener' , type , list . listener || listener ) ;
694702 } else if ( typeof list !== 'function' ) {
703+ list = ensureMutableListenerArray ( events , type , list ) ;
695704 let position = - 1 ;
696705
697706 for ( let i = list . length - 1 ; i >= 0 ; i -- ) {
@@ -875,6 +884,24 @@ function arrayClone(arr) {
875884 return ArrayPrototypeSlice ( arr ) ;
876885}
877886
887+ function cloneEventListenerArray ( arr ) {
888+ const copy = arrayClone ( arr ) ;
889+ copy [ kEmitting ] = 0 ;
890+ if ( arr . warned ) {
891+ copy . warned = true ;
892+ }
893+ return copy ;
894+ }
895+
896+ function ensureMutableListenerArray ( events , type , handler ) {
897+ if ( handler [ kEmitting ] > 0 ) {
898+ const copy = cloneEventListenerArray ( handler ) ;
899+ events [ type ] = copy ;
900+ return copy ;
901+ }
902+ return handler ;
903+ }
904+
878905function unwrapListeners ( arr ) {
879906 const ret = arrayClone ( arr ) ;
880907 for ( let i = 0 ; i < ret . length ; ++ i ) {
0 commit comments