| /** @license MIT License (c) copyright 2010-2014 original author or authors */ |
| /** @author Brian Cavalier */ |
| /** @author John Hann */ |
| |
| (function(define) { 'use strict'; |
| define(function(require) { |
| |
| var defaultStackJumpSeparator = 'from execution context:'; |
| var defaultStackFilter = /[\s\(\/\\](node|module|timers)\.js:|when([\/\\]{1,2}(lib|monitor|es6-shim)[\/\\]{1,2}|\.js)|(new\sPromise)\b|(\b(PromiseMonitor|ConsoleReporter|Scheduler|RunHandlerTask|ProgressTask|Promise|.*Handler)\.[\w_]\w\w+\b)|\b(tryCatch\w+|getHandler\w*)\b/i; |
| |
| var setTimer = require('../lib/env').setTimer; |
| var error = require('./error'); |
| |
| var executionContext = []; |
| |
| function PromiseMonitor(reporter) { |
| this.logDelay = 0; |
| this.stackFilter = defaultStackFilter; |
| this.stackJumpSeparator = defaultStackJumpSeparator; |
| this.filterDuplicateFrames = true; |
| |
| this._reporter = reporter; |
| if(typeof reporter.configurePromiseMonitor === 'function') { |
| reporter.configurePromiseMonitor(this); |
| } |
| |
| this._traces = []; |
| this._traceTask = 0; |
| |
| var self = this; |
| this._doLogTraces = function() { |
| self._logTraces(); |
| }; |
| } |
| |
| PromiseMonitor.prototype.monitor = function(Promise) { |
| var self = this; |
| Promise.createContext = function(p, context) { |
| p.context = self.createContext(p, context); |
| }; |
| |
| Promise.enterContext = function(p) { |
| executionContext.push(p.context); |
| }; |
| |
| Promise.exitContext = function() { |
| executionContext.pop(); |
| }; |
| |
| Promise.onPotentiallyUnhandledRejection = function(rejection, extraContext) { |
| return self.addTrace(rejection, extraContext); |
| }; |
| |
| Promise.onPotentiallyUnhandledRejectionHandled = function(rejection) { |
| return self.removeTrace(rejection); |
| }; |
| |
| Promise.onFatalRejection = function(rejection, extraContext) { |
| return self.fatal(rejection, extraContext); |
| }; |
| |
| return this; |
| }; |
| |
| PromiseMonitor.prototype.createContext = function(at, parentContext) { |
| var context = { |
| parent: parentContext || executionContext[executionContext.length - 1], |
| stack: void 0 |
| }; |
| error.captureStack(context, at.constructor); |
| return context; |
| }; |
| |
| PromiseMonitor.prototype.addTrace = function(handler, extraContext) { |
| var t, i; |
| |
| for(i = this._traces.length-1; i >= 0; --i) { |
| t = this._traces[i]; |
| if(t.handler === handler) { |
| break; |
| } |
| } |
| |
| if(i >= 0) { |
| t.extraContext = extraContext; |
| } else { |
| this._traces.push({ |
| handler: handler, |
| extraContext: extraContext |
| }); |
| } |
| |
| this.logTraces(); |
| }; |
| |
| PromiseMonitor.prototype.removeTrace = function(/*handler*/) { |
| this.logTraces(); |
| }; |
| |
| PromiseMonitor.prototype.fatal = function(handler, extraContext) { |
| var err = new Error(); |
| err.stack = this._createLongTrace(handler.value, handler.context, extraContext).join('\n'); |
| setTimer(function() { |
| throw err; |
| }, 0); |
| }; |
| |
| PromiseMonitor.prototype.logTraces = function() { |
| if(!this._traceTask) { |
| this._traceTask = setTimer(this._doLogTraces, this.logDelay); |
| } |
| }; |
| |
| PromiseMonitor.prototype._logTraces = function() { |
| this._traceTask = void 0; |
| this._traces = this._traces.filter(filterHandled); |
| this._reporter.log(this.formatTraces(this._traces)); |
| }; |
| |
| |
| PromiseMonitor.prototype.formatTraces = function(traces) { |
| return traces.map(function(t) { |
| return this._createLongTrace(t.handler.value, t.handler.context, t.extraContext); |
| }, this); |
| }; |
| |
| PromiseMonitor.prototype._createLongTrace = function(e, context, extraContext) { |
| var trace = error.parse(e) || [String(e) + ' (WARNING: non-Error used)']; |
| trace = filterFrames(this.stackFilter, trace, 0); |
| this._appendContext(trace, context); |
| this._appendContext(trace, extraContext); |
| return this.filterDuplicateFrames ? this._removeDuplicates(trace) : trace; |
| }; |
| |
| PromiseMonitor.prototype._removeDuplicates = function(trace) { |
| var seen = {}; |
| var sep = this.stackJumpSeparator; |
| var count = 0; |
| return trace.reduceRight(function(deduped, line, i) { |
| if(i === 0) { |
| deduped.unshift(line); |
| } else if(line === sep) { |
| if(count > 0) { |
| deduped.unshift(line); |
| count = 0; |
| } |
| } else if(!seen[line]) { |
| seen[line] = true; |
| deduped.unshift(line); |
| ++count; |
| } |
| return deduped; |
| }, []); |
| }; |
| |
| PromiseMonitor.prototype._appendContext = function(trace, context) { |
| trace.push.apply(trace, this._createTrace(context)); |
| }; |
| |
| PromiseMonitor.prototype._createTrace = function(traceChain) { |
| var trace = []; |
| var stack; |
| |
| while(traceChain) { |
| stack = error.parse(traceChain); |
| |
| if (stack) { |
| stack = filterFrames(this.stackFilter, stack); |
| appendStack(trace, stack, this.stackJumpSeparator); |
| } |
| |
| traceChain = traceChain.parent; |
| } |
| |
| return trace; |
| }; |
| |
| function appendStack(trace, stack, separator) { |
| if (stack.length > 1) { |
| stack[0] = separator; |
| trace.push.apply(trace, stack); |
| } |
| } |
| |
| function filterFrames(stackFilter, stack) { |
| return stack.filter(function(frame) { |
| return !stackFilter.test(frame); |
| }); |
| } |
| |
| function filterHandled(t) { |
| return !t.handler.handled; |
| } |
| |
| return PromiseMonitor; |
| }); |
| }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); |