@@ -94,6 +94,47 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
9494 return $getMaxListeners ( this ) ;
9595} ;
9696
97+ // Returns the longest sequence of `a` that fully appears in `b`,
98+ // of length at least 3.
99+ // This is a lazy approach but should work well enough, given that stack
100+ // frames are usually unequal or otherwise appear in groups, and that
101+ // we only run this code in case of an unhandled exception.
102+ function longestSeqContainedIn ( a , b ) {
103+ for ( var len = a . length ; len >= 3 ; -- len ) {
104+ for ( var i = 0 ; i < a . length - len ; ++ i ) {
105+ // Attempt to find a[i:i+len] in b
106+ for ( var j = 0 ; j < b . length - len ; ++ j ) {
107+ let matches = true ;
108+ for ( var k = 0 ; k < len ; ++ k ) {
109+ if ( a [ i + k ] !== b [ j + k ] ) {
110+ matches = false ;
111+ break ;
112+ }
113+ }
114+ if ( matches )
115+ return [ len , i , j ] ;
116+ }
117+ }
118+ }
119+
120+ return [ 0 , 0 , 0 ] ;
121+ }
122+
123+ function enhanceStackTrace ( err , own ) {
124+ const sep = '\nEmitted \'error\' event at:\n' ;
125+
126+ const errStack = err . stack . split ( '\n' ) . slice ( 1 ) ;
127+ const ownStack = own . stack . split ( '\n' ) . slice ( 1 ) ;
128+
129+ const [ len , off ] = longestSeqContainedIn ( ownStack , errStack ) ;
130+ if ( len > 0 ) {
131+ ownStack . splice ( off + 1 , len - 1 ,
132+ ' [... lines matching original stack trace ...]' ) ;
133+ }
134+ // Do this last, because it is the only operation with side effects.
135+ err . stack = err . stack + sep + ownStack . join ( '\n' ) ;
136+ }
137+
97138EventEmitter . prototype . emit = function emit ( type , ...args ) {
98139 let doError = ( type === 'error' ) ;
99140
@@ -109,13 +150,25 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
109150 if ( args . length > 0 )
110151 er = args [ 0 ] ;
111152 if ( er instanceof Error ) {
153+ try {
154+ const { kExpandStackSymbol } = require ( 'internal/util' ) ;
155+ const capture = { } ;
156+ Error . captureStackTrace ( capture , EventEmitter . prototype . emit ) ;
157+ Object . defineProperty ( er , kExpandStackSymbol , {
158+ value : enhanceStackTrace . bind ( null , er , capture ) ,
159+ configurable : true
160+ } ) ;
161+ } catch ( e ) { }
162+
163+ // Note: The comments on the `throw` lines are intentional, they show
164+ // up in Node's output if this results in an unhandled exception.
112165 throw er ; // Unhandled 'error' event
113166 }
114167 // At least give some kind of context to the user
115168 const errors = lazyErrors ( ) ;
116169 const err = new errors . Error ( 'ERR_UNHANDLED_ERROR' , er ) ;
117170 err . context = er ;
118- throw err ;
171+ throw err ; // Unhandled 'error' event
119172 }
120173
121174 const handler = events [ type ] ;
0 commit comments