1 |
|
2 |
|
3 |
|
4 |
|
5 | (function(define) { 'use strict';
|
6 | define(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/timer').set;
|
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); }));
|