198 lines
5.0 KiB
JavaScript
198 lines
5.0 KiB
JavaScript
|
/** @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); }));
|