63
63
} ) ( function ( ) {
64
64
"use strict" ;
65
65
66
+ var hasStacks = false ;
67
+ try {
68
+ throw new Error ( ) ;
69
+ } catch ( e ) {
70
+ hasStacks = ! ! e . stack ;
71
+ }
72
+
66
73
// All code after this point will be filtered from stack traces reported
67
74
// by Q.
68
75
var qStartingLine = captureLine ( ) ;
@@ -282,10 +289,10 @@ Q.longStackJumpLimit = 1;
282
289
var STACK_JUMP_SEPARATOR = "From previous event:" ;
283
290
284
291
function makeStackTraceLong ( error , promise ) {
285
- // If possible (that is, if in V8), transform the error stack
286
- // trace by removing Node and Q cruft, then concatenating with
287
- // the stack trace of the promise we are ``done``ing. See #57.
288
- if ( promise . stack &&
292
+ // If possible, transform the error stack trace by removing Node and Q
293
+ // cruft, then concatenating with the stack trace of `promise`. See #57.
294
+ if ( hasStacks &&
295
+ promise . stack &&
289
296
typeof error === "object" &&
290
297
error !== null &&
291
298
error . stack &&
@@ -303,7 +310,7 @@ function filterStackString(stackString) {
303
310
for ( var i = 0 ; i < lines . length ; ++ i ) {
304
311
var line = lines [ i ] ;
305
312
306
- if ( ! isInternalFrame ( line ) && ! isNodeFrame ( line ) ) {
313
+ if ( ! isInternalFrame ( line ) && ! isNodeFrame ( line ) && line ) {
307
314
desiredLines . push ( line ) ;
308
315
}
309
316
}
@@ -315,15 +322,36 @@ function isNodeFrame(stackLine) {
315
322
stackLine . indexOf ( "(node.js:" ) !== - 1 ;
316
323
}
317
324
325
+ function getFileNameAndLineNumber ( stackLine ) {
326
+ // Named functions: "at functionName (filename:lineNumber:columnNumber)"
327
+ // In IE10 function name can have spaces ("Anonymous function") O_o
328
+ var attempt1 = / a t .+ \( ( .+ ) : ( \d + ) : (?: \d + ) \) $ / . exec ( stackLine ) ;
329
+ if ( attempt1 ) {
330
+ return [ attempt1 [ 1 ] , Number ( attempt1 [ 2 ] ) ] ;
331
+ }
332
+
333
+ // Anonymous functions: "at filename:lineNumber:columnNumber"
334
+ var attempt2 = / a t ( [ ^ ] + ) : ( \d + ) : (?: \d + ) $ / . exec ( stackLine ) ;
335
+ if ( attempt2 ) {
336
+ return [ attempt2 [ 1 ] , Number ( attempt2 [ 2 ] ) ] ;
337
+ }
338
+
339
+ // Firefox style: "function@filename:lineNumber or @filename:lineNumber"
340
+ var attempt3 = / .* @ ( .+ ) : ( \d + ) $ / . exec ( stackLine ) ;
341
+ if ( attempt3 ) {
342
+ return [ attempt3 [ 1 ] , Number ( attempt3 [ 2 ] ) ] ;
343
+ }
344
+ }
345
+
318
346
function isInternalFrame ( stackLine ) {
319
- var pieces = / a t . + \( ( . * ) : ( \d + ) : \d + \) / . exec ( stackLine ) ;
347
+ var fileNameAndLineNumber = getFileNameAndLineNumber ( stackLine ) ;
320
348
321
- if ( ! pieces ) {
349
+ if ( ! fileNameAndLineNumber ) {
322
350
return false ;
323
351
}
324
352
325
- var fileName = pieces [ 1 ] ;
326
- var lineNumber = pieces [ 2 ] ;
353
+ var fileName = fileNameAndLineNumber [ 0 ] ;
354
+ var lineNumber = fileNameAndLineNumber [ 1 ] ;
327
355
328
356
return fileName === qFileName &&
329
357
lineNumber >= qStartingLine &&
@@ -333,24 +361,22 @@ function isInternalFrame(stackLine) {
333
361
// discover own file name and line number range for filtering stack
334
362
// traces
335
363
function captureLine ( ) {
336
- if ( Error . captureStackTrace ) {
337
- var fileName , lineNumber ;
338
-
339
- var oldPrepareStackTrace = Error . prepareStackTrace ;
340
-
341
- Error . prepareStackTrace = function ( error , frames ) {
342
- fileName = frames [ 1 ] . getFileName ( ) ;
343
- lineNumber = frames [ 1 ] . getLineNumber ( ) ;
344
- } ;
364
+ if ( ! hasStacks ) {
365
+ return ;
366
+ }
345
367
346
- // teases call of temporary prepareStackTrace
347
- // JSHint and Closure Compiler generate known warnings here
348
- /*jshint expr: true */
349
- new Error ( ) . stack ;
368
+ try {
369
+ throw new Error ( ) ;
370
+ } catch ( e ) {
371
+ var lines = e . stack . split ( "\n" ) ;
372
+ var firstLine = lines [ 0 ] . indexOf ( "@" ) > 0 ? lines [ 1 ] : lines [ 2 ] ;
373
+ var fileNameAndLineNumber = getFileNameAndLineNumber ( firstLine ) ;
374
+ if ( ! fileNameAndLineNumber ) {
375
+ return ;
376
+ }
350
377
351
- Error . prepareStackTrace = oldPrepareStackTrace ;
352
- qFileName = fileName ;
353
- return lineNumber ;
378
+ qFileName = fileNameAndLineNumber [ 0 ] ;
379
+ return fileNameAndLineNumber [ 1 ] ;
354
380
}
355
381
}
356
382
@@ -419,15 +445,18 @@ function defer() {
419
445
return nearer ;
420
446
} ;
421
447
422
- if ( Error . captureStackTrace && Q . longStackJumpLimit > 0 ) {
423
- Error . captureStackTrace ( promise , defer ) ;
424
-
425
- // Reify the stack into a string by using the accessor; this prevents
426
- // memory leaks as per GH-111. At the same time, cut off the first line;
427
- // it's always just "[object Promise]\n", as per the `toString`.
428
- promise . stack = promise . stack . substring (
429
- promise . stack . indexOf ( "\n" ) + 1
430
- ) ;
448
+ if ( Q . longStackJumpLimit > 0 && hasStacks ) {
449
+ try {
450
+ throw new Error ( ) ;
451
+ } catch ( e ) {
452
+ // NOTE: don't try to use `Error.captureStackTrace` or transfer the
453
+ // accessor around; that causes memory leaks as per GH-111. Just
454
+ // reify the stack trace as a string ASAP.
455
+ //
456
+ // At the same time, cut off the first line; it's always just
457
+ // "[object Promise]\n", as per the `toString`.
458
+ promise . stack = e . stack . substring ( e . stack . indexOf ( "\n" ) + 1 ) ;
459
+ }
431
460
}
432
461
433
462
// NOTE: we do the checks for `resolvedPromise` in each method, instead of
0 commit comments