Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81160 views
1
"use strict";
2
module.exports = function() {
3
var async = require("./async.js");
4
var util = require("./util.js");
5
var bluebirdFramePattern =
6
/[\\\/]bluebird[\\\/]js[\\\/](main|debug|zalgo|instrumented)/;
7
var stackFramePattern = null;
8
var formatStack = null;
9
var indentStackFrames = false;
10
var warn;
11
12
function CapturedTrace(parent) {
13
this._parent = parent;
14
var length = this._length = 1 + (parent === undefined ? 0 : parent._length);
15
captureStackTrace(this, CapturedTrace);
16
if (length > 32) this.uncycle();
17
}
18
util.inherits(CapturedTrace, Error);
19
20
CapturedTrace.prototype.uncycle = function() {
21
var length = this._length;
22
if (length < 2) return;
23
var nodes = [];
24
var stackToIndex = {};
25
26
for (var i = 0, node = this; node !== undefined; ++i) {
27
nodes.push(node);
28
node = node._parent;
29
}
30
length = this._length = i;
31
for (var i = length - 1; i >= 0; --i) {
32
var stack = nodes[i].stack;
33
if (stackToIndex[stack] === undefined) {
34
stackToIndex[stack] = i;
35
}
36
}
37
for (var i = 0; i < length; ++i) {
38
var currentStack = nodes[i].stack;
39
var index = stackToIndex[currentStack];
40
if (index !== undefined && index !== i) {
41
if (index > 0) {
42
nodes[index - 1]._parent = undefined;
43
nodes[index - 1]._length = 1;
44
}
45
nodes[i]._parent = undefined;
46
nodes[i]._length = 1;
47
var cycleEdgeNode = i > 0 ? nodes[i - 1] : this;
48
49
if (index < length - 1) {
50
cycleEdgeNode._parent = nodes[index + 1];
51
cycleEdgeNode._parent.uncycle();
52
cycleEdgeNode._length =
53
cycleEdgeNode._parent._length + 1;
54
} else {
55
cycleEdgeNode._parent = undefined;
56
cycleEdgeNode._length = 1;
57
}
58
var currentChildLength = cycleEdgeNode._length + 1;
59
for (var j = i - 2; j >= 0; --j) {
60
nodes[j]._length = currentChildLength;
61
currentChildLength++;
62
}
63
return;
64
}
65
}
66
};
67
68
CapturedTrace.prototype.parent = function() {
69
return this._parent;
70
};
71
72
CapturedTrace.prototype.hasParent = function() {
73
return this._parent !== undefined;
74
};
75
76
CapturedTrace.prototype.attachExtraTrace = function(error) {
77
if (error.__stackCleaned__) return;
78
this.uncycle();
79
var parsed = CapturedTrace.parseStackAndMessage(error);
80
var message = parsed.message;
81
var stacks = [parsed.stack];
82
83
var trace = this;
84
while (trace !== undefined) {
85
stacks.push(cleanStack(trace.stack.split("\n")));
86
trace = trace._parent;
87
}
88
removeCommonRoots(stacks);
89
removeDuplicateOrEmptyJumps(stacks);
90
util.notEnumerableProp(error, "stack", reconstructStack(message, stacks));
91
util.notEnumerableProp(error, "__stackCleaned__", true);
92
};
93
94
function reconstructStack(message, stacks) {
95
for (var i = 0; i < stacks.length - 1; ++i) {
96
stacks[i].push("From previous event:");
97
stacks[i] = stacks[i].join("\n");
98
}
99
if (i < stacks.length) {
100
stacks[i] = stacks[i].join("\n");
101
}
102
return message + "\n" + stacks.join("\n");
103
}
104
105
function removeDuplicateOrEmptyJumps(stacks) {
106
for (var i = 0; i < stacks.length; ++i) {
107
if (stacks[i].length === 0 ||
108
((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) {
109
stacks.splice(i, 1);
110
i--;
111
}
112
}
113
}
114
115
function removeCommonRoots(stacks) {
116
var current = stacks[0];
117
for (var i = 1; i < stacks.length; ++i) {
118
var prev = stacks[i];
119
var currentLastIndex = current.length - 1;
120
var currentLastLine = current[currentLastIndex];
121
var commonRootMeetPoint = -1;
122
123
for (var j = prev.length - 1; j >= 0; --j) {
124
if (prev[j] === currentLastLine) {
125
commonRootMeetPoint = j;
126
break;
127
}
128
}
129
130
for (var j = commonRootMeetPoint; j >= 0; --j) {
131
var line = prev[j];
132
if (current[currentLastIndex] === line) {
133
current.pop();
134
currentLastIndex--;
135
} else {
136
break;
137
}
138
}
139
current = prev;
140
}
141
}
142
143
function cleanStack(stack) {
144
var ret = [];
145
for (var i = 0; i < stack.length; ++i) {
146
var line = stack[i];
147
var isTraceLine = stackFramePattern.test(line) ||
148
" (No stack trace)" === line;
149
var isInternalFrame = isTraceLine && shouldIgnore(line);
150
if (isTraceLine && !isInternalFrame) {
151
if (indentStackFrames && line.charAt(0) !== " ") {
152
line = " " + line;
153
}
154
ret.push(line);
155
}
156
}
157
return ret;
158
}
159
160
function stackFramesAsArray(error) {
161
var stack = error.stack.replace(/\s+$/g, "").split("\n");
162
for (var i = 0; i < stack.length; ++i) {
163
var line = stack[i];
164
if (" (No stack trace)" === line || stackFramePattern.test(line)) {
165
break;
166
}
167
}
168
if (i > 0) {
169
stack = stack.slice(i);
170
}
171
return stack;
172
}
173
174
CapturedTrace.parseStackAndMessage = function(error) {
175
var stack = error.stack;
176
var message = error.toString();
177
stack = typeof stack === "string" && stack.length > 0
178
? stackFramesAsArray(error) : [" (No stack trace)"];
179
return {
180
message: message,
181
stack: cleanStack(stack)
182
};
183
};
184
185
CapturedTrace.formatAndLogError = function(error, title) {
186
if (typeof console !== "undefined") {
187
var message;
188
if (typeof error === "object" || typeof error === "function") {
189
var stack = error.stack;
190
message = title + formatStack(stack, error);
191
} else {
192
message = title + String(error);
193
}
194
if (typeof warn === "function") {
195
warn(message);
196
} else if (typeof console.log === "function" ||
197
typeof console.log === "object") {
198
console.log(message);
199
}
200
}
201
};
202
203
CapturedTrace.unhandledRejection = function (reason) {
204
CapturedTrace.formatAndLogError(reason, "^--- With additional stack trace: ");
205
};
206
207
CapturedTrace.isSupported = function () {
208
return typeof captureStackTrace === "function";
209
};
210
211
CapturedTrace.fireRejectionEvent =
212
function(name, localHandler, reason, promise) {
213
var localEventFired = false;
214
try {
215
if (typeof localHandler === "function") {
216
localEventFired = true;
217
if (name === "rejectionHandled") {
218
localHandler(promise);
219
} else {
220
localHandler(reason, promise);
221
}
222
}
223
} catch (e) {
224
async.throwLater(e);
225
}
226
227
var globalEventFired = false;
228
try {
229
globalEventFired = fireGlobalEvent(name, reason, promise);
230
} catch (e) {
231
globalEventFired = true;
232
async.throwLater(e);
233
}
234
235
var domEventFired = false;
236
if (fireDomEvent) {
237
try {
238
domEventFired = fireDomEvent(name.toLowerCase(), {
239
reason: reason,
240
promise: promise
241
});
242
} catch (e) {
243
domEventFired = true;
244
async.throwLater(e);
245
}
246
}
247
248
if (!globalEventFired && !localEventFired && !domEventFired &&
249
name === "unhandledRejection") {
250
CapturedTrace.formatAndLogError(reason, "Unhandled rejection ");
251
}
252
};
253
254
function formatNonError(obj) {
255
var str;
256
if (typeof obj === "function") {
257
str = "[function " +
258
(obj.name || "anonymous") +
259
"]";
260
} else {
261
str = obj.toString();
262
var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/;
263
if (ruselessToString.test(str)) {
264
try {
265
var newStr = JSON.stringify(obj);
266
str = newStr;
267
}
268
catch(e) {
269
270
}
271
}
272
if (str.length === 0) {
273
str = "(empty array)";
274
}
275
}
276
return ("(<" + snip(str) + ">, no stack trace)");
277
}
278
279
function snip(str) {
280
var maxChars = 41;
281
if (str.length < maxChars) {
282
return str;
283
}
284
return str.substr(0, maxChars - 3) + "...";
285
}
286
287
var shouldIgnore = function() { return false; };
288
var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;
289
function parseLineInfo(line) {
290
var matches = line.match(parseLineInfoRegex);
291
if (matches) {
292
return {
293
fileName: matches[1],
294
line: parseInt(matches[2], 10)
295
};
296
}
297
}
298
CapturedTrace.setBounds = function(firstLineError, lastLineError) {
299
if (!CapturedTrace.isSupported()) return;
300
var firstStackLines = firstLineError.stack.split("\n");
301
var lastStackLines = lastLineError.stack.split("\n");
302
var firstIndex = -1;
303
var lastIndex = -1;
304
var firstFileName;
305
var lastFileName;
306
for (var i = 0; i < firstStackLines.length; ++i) {
307
var result = parseLineInfo(firstStackLines[i]);
308
if (result) {
309
firstFileName = result.fileName;
310
firstIndex = result.line;
311
break;
312
}
313
}
314
for (var i = 0; i < lastStackLines.length; ++i) {
315
var result = parseLineInfo(lastStackLines[i]);
316
if (result) {
317
lastFileName = result.fileName;
318
lastIndex = result.line;
319
break;
320
}
321
}
322
if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName ||
323
firstFileName !== lastFileName || firstIndex >= lastIndex) {
324
return;
325
}
326
327
shouldIgnore = function(line) {
328
if (bluebirdFramePattern.test(line)) return true;
329
var info = parseLineInfo(line);
330
if (info) {
331
if (info.fileName === firstFileName &&
332
(firstIndex <= info.line && info.line <= lastIndex)) {
333
return true;
334
}
335
}
336
return false;
337
};
338
};
339
340
var captureStackTrace = (function stackDetection() {
341
var v8stackFramePattern = /^\s*at\s*/;
342
var v8stackFormatter = function(stack, error) {
343
if (typeof stack === "string") return stack;
344
345
if (error.name !== undefined &&
346
error.message !== undefined) {
347
return error.toString();
348
}
349
return formatNonError(error);
350
};
351
352
if (typeof Error.stackTraceLimit === "number" &&
353
typeof Error.captureStackTrace === "function") {
354
Error.stackTraceLimit = Error.stackTraceLimit + 6;
355
stackFramePattern = v8stackFramePattern;
356
formatStack = v8stackFormatter;
357
var captureStackTrace = Error.captureStackTrace;
358
359
shouldIgnore = function(line) {
360
return bluebirdFramePattern.test(line);
361
};
362
return function(receiver, ignoreUntil) {
363
Error.stackTraceLimit = Error.stackTraceLimit + 6;
364
captureStackTrace(receiver, ignoreUntil);
365
Error.stackTraceLimit = Error.stackTraceLimit - 6;
366
};
367
}
368
var err = new Error();
369
370
if (typeof err.stack === "string" &&
371
err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) {
372
stackFramePattern = /@/;
373
formatStack = v8stackFormatter;
374
indentStackFrames = true;
375
return function captureStackTrace(o) {
376
o.stack = new Error().stack;
377
};
378
}
379
380
var hasStackAfterThrow;
381
try { throw new Error(); }
382
catch(e) {
383
hasStackAfterThrow = ("stack" in e);
384
}
385
if (!("stack" in err) && hasStackAfterThrow) {
386
stackFramePattern = v8stackFramePattern;
387
formatStack = v8stackFormatter;
388
return function captureStackTrace(o) {
389
Error.stackTraceLimit = Error.stackTraceLimit + 6;
390
try { throw new Error(); }
391
catch(e) { o.stack = e.stack; }
392
Error.stackTraceLimit = Error.stackTraceLimit - 6;
393
};
394
}
395
396
formatStack = function(stack, error) {
397
if (typeof stack === "string") return stack;
398
399
if ((typeof error === "object" ||
400
typeof error === "function") &&
401
error.name !== undefined &&
402
error.message !== undefined) {
403
return error.toString();
404
}
405
return formatNonError(error);
406
};
407
408
return null;
409
410
})([]);
411
412
var fireDomEvent;
413
var fireGlobalEvent = (function() {
414
if (util.isNode) {
415
return function(name, reason, promise) {
416
if (name === "rejectionHandled") {
417
return process.emit(name, promise);
418
} else {
419
return process.emit(name, reason, promise);
420
}
421
};
422
} else {
423
var customEventWorks = false;
424
var anyEventWorks = true;
425
try {
426
var ev = new self.CustomEvent("test");
427
customEventWorks = ev instanceof CustomEvent;
428
} catch (e) {}
429
if (!customEventWorks) {
430
try {
431
var event = document.createEvent("CustomEvent");
432
event.initCustomEvent("testingtheevent", false, true, {});
433
self.dispatchEvent(event);
434
} catch (e) {
435
anyEventWorks = false;
436
}
437
}
438
if (anyEventWorks) {
439
fireDomEvent = function(type, detail) {
440
var event;
441
if (customEventWorks) {
442
event = new self.CustomEvent(type, {
443
detail: detail,
444
bubbles: false,
445
cancelable: true
446
});
447
} else if (self.dispatchEvent) {
448
event = document.createEvent("CustomEvent");
449
event.initCustomEvent(type, false, true, detail);
450
}
451
452
return event ? !self.dispatchEvent(event) : false;
453
};
454
}
455
456
var toWindowMethodNameMap = {};
457
toWindowMethodNameMap["unhandledRejection"] = ("on" +
458
"unhandledRejection").toLowerCase();
459
toWindowMethodNameMap["rejectionHandled"] = ("on" +
460
"rejectionHandled").toLowerCase();
461
462
return function(name, reason, promise) {
463
var methodName = toWindowMethodNameMap[name];
464
var method = self[methodName];
465
if (!method) return false;
466
if (name === "rejectionHandled") {
467
method.call(self, promise);
468
} else {
469
method.call(self, reason, promise);
470
}
471
return true;
472
};
473
}
474
})();
475
476
if (typeof console !== "undefined" && typeof console.warn !== "undefined") {
477
warn = function (message) {
478
console.warn(message);
479
};
480
if (util.isNode && process.stderr.isTTY) {
481
warn = function(message) {
482
process.stderr.write("\u001b[31m" + message + "\u001b[39m\n");
483
};
484
} else if (!util.isNode && typeof (new Error().stack) === "string") {
485
warn = function(message) {
486
console.warn("%c" + message, "color: red");
487
};
488
}
489
}
490
491
return CapturedTrace;
492
};
493
494