123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- "use strict";
- module.exports = function() {
- var async = require("./async.js");
- var util = require("./util.js");
- var bluebirdFramePattern =
- /[\\\/]bluebird[\\\/]js[\\\/](main|debug|zalgo|instrumented)/;
- var stackFramePattern = null;
- var formatStack = null;
- var indentStackFrames = false;
- var warn;
- function CapturedTrace(parent) {
- this._parent = parent;
- var length = this._length = 1 + (parent === undefined ? 0 : parent._length);
- captureStackTrace(this, CapturedTrace);
- if (length > 32) this.uncycle();
- }
- util.inherits(CapturedTrace, Error);
- CapturedTrace.prototype.uncycle = function() {
- var length = this._length;
- if (length < 2) return;
- var nodes = [];
- var stackToIndex = {};
- for (var i = 0, node = this; node !== undefined; ++i) {
- nodes.push(node);
- node = node._parent;
- }
- length = this._length = i;
- for (var i = length - 1; i >= 0; --i) {
- var stack = nodes[i].stack;
- if (stackToIndex[stack] === undefined) {
- stackToIndex[stack] = i;
- }
- }
- for (var i = 0; i < length; ++i) {
- var currentStack = nodes[i].stack;
- var index = stackToIndex[currentStack];
- if (index !== undefined && index !== i) {
- if (index > 0) {
- nodes[index - 1]._parent = undefined;
- nodes[index - 1]._length = 1;
- }
- nodes[i]._parent = undefined;
- nodes[i]._length = 1;
- var cycleEdgeNode = i > 0 ? nodes[i - 1] : this;
- if (index < length - 1) {
- cycleEdgeNode._parent = nodes[index + 1];
- cycleEdgeNode._parent.uncycle();
- cycleEdgeNode._length =
- cycleEdgeNode._parent._length + 1;
- } else {
- cycleEdgeNode._parent = undefined;
- cycleEdgeNode._length = 1;
- }
- var currentChildLength = cycleEdgeNode._length + 1;
- for (var j = i - 2; j >= 0; --j) {
- nodes[j]._length = currentChildLength;
- currentChildLength++;
- }
- return;
- }
- }
- };
- CapturedTrace.prototype.parent = function() {
- return this._parent;
- };
- CapturedTrace.prototype.hasParent = function() {
- return this._parent !== undefined;
- };
- CapturedTrace.prototype.attachExtraTrace = function(error) {
- if (error.__stackCleaned__) return;
- this.uncycle();
- var parsed = CapturedTrace.parseStackAndMessage(error);
- var message = parsed.message;
- var stacks = [parsed.stack];
- var trace = this;
- while (trace !== undefined) {
- stacks.push(cleanStack(trace.stack.split("\n")));
- trace = trace._parent;
- }
- removeCommonRoots(stacks);
- removeDuplicateOrEmptyJumps(stacks);
- util.notEnumerableProp(error, "stack", reconstructStack(message, stacks));
- util.notEnumerableProp(error, "__stackCleaned__", true);
- };
- function reconstructStack(message, stacks) {
- for (var i = 0; i < stacks.length - 1; ++i) {
- stacks[i].push("From previous event:");
- stacks[i] = stacks[i].join("\n");
- }
- if (i < stacks.length) {
- stacks[i] = stacks[i].join("\n");
- }
- return message + "\n" + stacks.join("\n");
- }
- function removeDuplicateOrEmptyJumps(stacks) {
- for (var i = 0; i < stacks.length; ++i) {
- if (stacks[i].length === 0 ||
- ((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) {
- stacks.splice(i, 1);
- i--;
- }
- }
- }
- function removeCommonRoots(stacks) {
- var current = stacks[0];
- for (var i = 1; i < stacks.length; ++i) {
- var prev = stacks[i];
- var currentLastIndex = current.length - 1;
- var currentLastLine = current[currentLastIndex];
- var commonRootMeetPoint = -1;
- for (var j = prev.length - 1; j >= 0; --j) {
- if (prev[j] === currentLastLine) {
- commonRootMeetPoint = j;
- break;
- }
- }
- for (var j = commonRootMeetPoint; j >= 0; --j) {
- var line = prev[j];
- if (current[currentLastIndex] === line) {
- current.pop();
- currentLastIndex--;
- } else {
- break;
- }
- }
- current = prev;
- }
- }
- function cleanStack(stack) {
- var ret = [];
- for (var i = 0; i < stack.length; ++i) {
- var line = stack[i];
- var isTraceLine = stackFramePattern.test(line) ||
- " (No stack trace)" === line;
- var isInternalFrame = isTraceLine && shouldIgnore(line);
- if (isTraceLine && !isInternalFrame) {
- if (indentStackFrames && line.charAt(0) !== " ") {
- line = " " + line;
- }
- ret.push(line);
- }
- }
- return ret;
- }
- function stackFramesAsArray(error) {
- var stack = error.stack.replace(/\s+$/g, "").split("\n");
- for (var i = 0; i < stack.length; ++i) {
- var line = stack[i];
- if (" (No stack trace)" === line || stackFramePattern.test(line)) {
- break;
- }
- }
- if (i > 0) {
- stack = stack.slice(i);
- }
- return stack;
- }
- CapturedTrace.parseStackAndMessage = function(error) {
- var stack = error.stack;
- var message = error.toString();
- stack = typeof stack === "string" && stack.length > 0
- ? stackFramesAsArray(error) : [" (No stack trace)"];
- return {
- message: message,
- stack: cleanStack(stack)
- };
- };
- CapturedTrace.formatAndLogError = function(error, title) {
- if (typeof console !== "undefined") {
- var message;
- if (typeof error === "object" || typeof error === "function") {
- var stack = error.stack;
- message = title + formatStack(stack, error);
- } else {
- message = title + String(error);
- }
- if (typeof warn === "function") {
- warn(message);
- } else if (typeof console.log === "function" ||
- typeof console.log === "object") {
- console.log(message);
- }
- }
- };
- CapturedTrace.unhandledRejection = function (reason) {
- CapturedTrace.formatAndLogError(reason, "^--- With additional stack trace: ");
- };
- CapturedTrace.isSupported = function () {
- return typeof captureStackTrace === "function";
- };
- CapturedTrace.fireRejectionEvent =
- function(name, localHandler, reason, promise) {
- var localEventFired = false;
- try {
- if (typeof localHandler === "function") {
- localEventFired = true;
- if (name === "rejectionHandled") {
- localHandler(promise);
- } else {
- localHandler(reason, promise);
- }
- }
- } catch (e) {
- async.throwLater(e);
- }
- var globalEventFired = false;
- try {
- globalEventFired = fireGlobalEvent(name, reason, promise);
- } catch (e) {
- globalEventFired = true;
- async.throwLater(e);
- }
- var domEventFired = false;
- if (fireDomEvent) {
- try {
- domEventFired = fireDomEvent(name.toLowerCase(), {
- reason: reason,
- promise: promise
- });
- } catch (e) {
- domEventFired = true;
- async.throwLater(e);
- }
- }
- if (!globalEventFired && !localEventFired && !domEventFired &&
- name === "unhandledRejection") {
- CapturedTrace.formatAndLogError(reason, "Unhandled rejection ");
- }
- };
- function formatNonError(obj) {
- var str;
- if (typeof obj === "function") {
- str = "[function " +
- (obj.name || "anonymous") +
- "]";
- } else {
- str = obj.toString();
- var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/;
- if (ruselessToString.test(str)) {
- try {
- var newStr = JSON.stringify(obj);
- str = newStr;
- }
- catch(e) {
- }
- }
- if (str.length === 0) {
- str = "(empty array)";
- }
- }
- return ("(<" + snip(str) + ">, no stack trace)");
- }
- function snip(str) {
- var maxChars = 41;
- if (str.length < maxChars) {
- return str;
- }
- return str.substr(0, maxChars - 3) + "...";
- }
- var shouldIgnore = function() { return false; };
- var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;
- function parseLineInfo(line) {
- var matches = line.match(parseLineInfoRegex);
- if (matches) {
- return {
- fileName: matches[1],
- line: parseInt(matches[2], 10)
- };
- }
- }
- CapturedTrace.setBounds = function(firstLineError, lastLineError) {
- if (!CapturedTrace.isSupported()) return;
- var firstStackLines = firstLineError.stack.split("\n");
- var lastStackLines = lastLineError.stack.split("\n");
- var firstIndex = -1;
- var lastIndex = -1;
- var firstFileName;
- var lastFileName;
- for (var i = 0; i < firstStackLines.length; ++i) {
- var result = parseLineInfo(firstStackLines[i]);
- if (result) {
- firstFileName = result.fileName;
- firstIndex = result.line;
- break;
- }
- }
- for (var i = 0; i < lastStackLines.length; ++i) {
- var result = parseLineInfo(lastStackLines[i]);
- if (result) {
- lastFileName = result.fileName;
- lastIndex = result.line;
- break;
- }
- }
- if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName ||
- firstFileName !== lastFileName || firstIndex >= lastIndex) {
- return;
- }
- shouldIgnore = function(line) {
- if (bluebirdFramePattern.test(line)) return true;
- var info = parseLineInfo(line);
- if (info) {
- if (info.fileName === firstFileName &&
- (firstIndex <= info.line && info.line <= lastIndex)) {
- return true;
- }
- }
- return false;
- };
- };
- var captureStackTrace = (function stackDetection() {
- var v8stackFramePattern = /^\s*at\s*/;
- var v8stackFormatter = function(stack, error) {
- if (typeof stack === "string") return stack;
- if (error.name !== undefined &&
- error.message !== undefined) {
- return error.toString();
- }
- return formatNonError(error);
- };
- if (typeof Error.stackTraceLimit === "number" &&
- typeof Error.captureStackTrace === "function") {
- Error.stackTraceLimit = Error.stackTraceLimit + 6;
- stackFramePattern = v8stackFramePattern;
- formatStack = v8stackFormatter;
- var captureStackTrace = Error.captureStackTrace;
- shouldIgnore = function(line) {
- return bluebirdFramePattern.test(line);
- };
- return function(receiver, ignoreUntil) {
- Error.stackTraceLimit = Error.stackTraceLimit + 6;
- captureStackTrace(receiver, ignoreUntil);
- Error.stackTraceLimit = Error.stackTraceLimit - 6;
- };
- }
- var err = new Error();
- if (typeof err.stack === "string" &&
- err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) {
- stackFramePattern = /@/;
- formatStack = v8stackFormatter;
- indentStackFrames = true;
- return function captureStackTrace(o) {
- o.stack = new Error().stack;
- };
- }
- var hasStackAfterThrow;
- try { throw new Error(); }
- catch(e) {
- hasStackAfterThrow = ("stack" in e);
- }
- if (!("stack" in err) && hasStackAfterThrow) {
- stackFramePattern = v8stackFramePattern;
- formatStack = v8stackFormatter;
- return function captureStackTrace(o) {
- Error.stackTraceLimit = Error.stackTraceLimit + 6;
- try { throw new Error(); }
- catch(e) { o.stack = e.stack; }
- Error.stackTraceLimit = Error.stackTraceLimit - 6;
- };
- }
- formatStack = function(stack, error) {
- if (typeof stack === "string") return stack;
- if ((typeof error === "object" ||
- typeof error === "function") &&
- error.name !== undefined &&
- error.message !== undefined) {
- return error.toString();
- }
- return formatNonError(error);
- };
- return null;
- })([]);
- var fireDomEvent;
- var fireGlobalEvent = (function() {
- if (util.isNode) {
- return function(name, reason, promise) {
- if (name === "rejectionHandled") {
- return process.emit(name, promise);
- } else {
- return process.emit(name, reason, promise);
- }
- };
- } else {
- var customEventWorks = false;
- var anyEventWorks = true;
- try {
- var ev = new self.CustomEvent("test");
- customEventWorks = ev instanceof CustomEvent;
- } catch (e) {}
- if (!customEventWorks) {
- try {
- var event = document.createEvent("CustomEvent");
- event.initCustomEvent("testingtheevent", false, true, {});
- self.dispatchEvent(event);
- } catch (e) {
- anyEventWorks = false;
- }
- }
- if (anyEventWorks) {
- fireDomEvent = function(type, detail) {
- var event;
- if (customEventWorks) {
- event = new self.CustomEvent(type, {
- detail: detail,
- bubbles: false,
- cancelable: true
- });
- } else if (self.dispatchEvent) {
- event = document.createEvent("CustomEvent");
- event.initCustomEvent(type, false, true, detail);
- }
- return event ? !self.dispatchEvent(event) : false;
- };
- }
- var toWindowMethodNameMap = {};
- toWindowMethodNameMap["unhandledRejection"] = ("on" +
- "unhandledRejection").toLowerCase();
- toWindowMethodNameMap["rejectionHandled"] = ("on" +
- "rejectionHandled").toLowerCase();
- return function(name, reason, promise) {
- var methodName = toWindowMethodNameMap[name];
- var method = self[methodName];
- if (!method) return false;
- if (name === "rejectionHandled") {
- method.call(self, promise);
- } else {
- method.call(self, reason, promise);
- }
- return true;
- };
- }
- })();
- if (typeof console !== "undefined" && typeof console.warn !== "undefined") {
- warn = function (message) {
- console.warn(message);
- };
- if (util.isNode && process.stderr.isTTY) {
- warn = function(message) {
- process.stderr.write("\u001b[31m" + message + "\u001b[39m\n");
- };
- } else if (!util.isNode && typeof (new Error().stack) === "string") {
- warn = function(message) {
- console.warn("%c" + message, "color: red");
- };
- }
- }
- return CapturedTrace;
- };
|