UNPKG

5.13 kBJavaScriptView Raw
1/** @license MIT License (c) copyright 2010-2014 original author or authors */
2/** @author Brian Cavalier */
3/** @author John Hann */
4
5(function(define) { 'use strict';
6define(function(require) {
7
8 var defaultStackJumpSeparator = 'from execution context:';
9 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;
10
11 var setTimer = require('../lib/env').setTimer;
12 var error = require('./error');
13
14 var executionContext = [];
15
16 function PromiseMonitor(reporter) {
17 this.logDelay = 0;
18 this.stackFilter = defaultStackFilter;
19 this.stackJumpSeparator = defaultStackJumpSeparator;
20 this.filterDuplicateFrames = true;
21
22 this._reporter = reporter;
23 if(typeof reporter.configurePromiseMonitor === 'function') {
24 reporter.configurePromiseMonitor(this);
25 }
26
27 this._traces = [];
28 this._traceTask = 0;
29
30 var self = this;
31 this._doLogTraces = function() {
32 self._logTraces();
33 };
34 }
35
36 PromiseMonitor.prototype.monitor = function(Promise) {
37 var self = this;
38 Promise.createContext = function(p, context) {
39 p.context = self.createContext(p, context);
40 };
41
42 Promise.enterContext = function(p) {
43 executionContext.push(p.context);
44 };
45
46 Promise.exitContext = function() {
47 executionContext.pop();
48 };
49
50 Promise.onPotentiallyUnhandledRejection = function(rejection, extraContext) {
51 return self.addTrace(rejection, extraContext);
52 };
53
54 Promise.onPotentiallyUnhandledRejectionHandled = function(rejection) {
55 return self.removeTrace(rejection);
56 };
57
58 Promise.onFatalRejection = function(rejection, extraContext) {
59 return self.fatal(rejection, extraContext);
60 };
61
62 return this;
63 };
64
65 PromiseMonitor.prototype.createContext = function(at, parentContext) {
66 var context = {
67 parent: parentContext || executionContext[executionContext.length - 1],
68 stack: void 0
69 };
70 error.captureStack(context, at.constructor);
71 return context;
72 };
73
74 PromiseMonitor.prototype.addTrace = function(handler, extraContext) {
75 var t, i;
76
77 for(i = this._traces.length-1; i >= 0; --i) {
78 t = this._traces[i];
79 if(t.handler === handler) {
80 break;
81 }
82 }
83
84 if(i >= 0) {
85 t.extraContext = extraContext;
86 } else {
87 this._traces.push({
88 handler: handler,
89 extraContext: extraContext
90 });
91 }
92
93 this.logTraces();
94 };
95
96 PromiseMonitor.prototype.removeTrace = function(/*handler*/) {
97 this.logTraces();
98 };
99
100 PromiseMonitor.prototype.fatal = function(handler, extraContext) {
101 var err = new Error();
102 err.stack = this._createLongTrace(handler.value, handler.context, extraContext).join('\n');
103 setTimer(function() {
104 throw err;
105 }, 0);
106 };
107
108 PromiseMonitor.prototype.logTraces = function() {
109 if(!this._traceTask) {
110 this._traceTask = setTimer(this._doLogTraces, this.logDelay);
111 }
112 };
113
114 PromiseMonitor.prototype._logTraces = function() {
115 this._traceTask = void 0;
116 this._traces = this._traces.filter(filterHandled);
117 this._reporter.log(this.formatTraces(this._traces));
118 };
119
120
121 PromiseMonitor.prototype.formatTraces = function(traces) {
122 return traces.map(function(t) {
123 return this._createLongTrace(t.handler.value, t.handler.context, t.extraContext);
124 }, this);
125 };
126
127 PromiseMonitor.prototype._createLongTrace = function(e, context, extraContext) {
128 var trace = error.parse(e) || [String(e) + ' (WARNING: non-Error used)'];
129 trace = filterFrames(this.stackFilter, trace, 0);
130 this._appendContext(trace, context);
131 this._appendContext(trace, extraContext);
132 return this.filterDuplicateFrames ? this._removeDuplicates(trace) : trace;
133 };
134
135 PromiseMonitor.prototype._removeDuplicates = function(trace) {
136 var seen = {};
137 var sep = this.stackJumpSeparator;
138 var count = 0;
139 return trace.reduceRight(function(deduped, line, i) {
140 if(i === 0) {
141 deduped.unshift(line);
142 } else if(line === sep) {
143 if(count > 0) {
144 deduped.unshift(line);
145 count = 0;
146 }
147 } else if(!seen[line]) {
148 seen[line] = true;
149 deduped.unshift(line);
150 ++count;
151 }
152 return deduped;
153 }, []);
154 };
155
156 PromiseMonitor.prototype._appendContext = function(trace, context) {
157 trace.push.apply(trace, this._createTrace(context));
158 };
159
160 PromiseMonitor.prototype._createTrace = function(traceChain) {
161 var trace = [];
162 var stack;
163
164 while(traceChain) {
165 stack = error.parse(traceChain);
166
167 if (stack) {
168 stack = filterFrames(this.stackFilter, stack);
169 appendStack(trace, stack, this.stackJumpSeparator);
170 }
171
172 traceChain = traceChain.parent;
173 }
174
175 return trace;
176 };
177
178 function appendStack(trace, stack, separator) {
179 if (stack.length > 1) {
180 stack[0] = separator;
181 trace.push.apply(trace, stack);
182 }
183 }
184
185 function filterFrames(stackFilter, stack) {
186 return stack.filter(function(frame) {
187 return !stackFilter.test(frame);
188 });
189 }
190
191 function filterHandled(t) {
192 return !t.handler.handled;
193 }
194
195 return PromiseMonitor;
196});
197}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));