|
1 | 1 | // TODO: AMD/CommonJS/etc wrapper |
2 | 2 | (function() { |
3 | | - function StackEntry(functionName, srcUrl, lineNumber, charNumber) { |
4 | | - // TODO: if used without `new` create a new instance |
5 | | - return { |
6 | | - fn: functionName, |
7 | | - args: [], |
8 | | - src: srcUrl, |
9 | | - line: lineNumber, |
10 | | - char: charNumber |
| 3 | + // V8 Only: See http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi |
| 4 | + // TODO: configurable Error.stackTraceLimit = 20; |
| 5 | + Error.prepareStackTrace = function(error, stack) { |
| 6 | + var stackEntries = []; |
| 7 | + for(var i = 1, len = stack.length; i < len; i++) { |
| 8 | + var cur = stack[i]; |
| 9 | + // IDEA: utilize getEvalOrigin: if this function was created using a call to eval returns a CallSite object representing the location where eval was called |
| 10 | + if (!cur.isNative()) { |
| 11 | + stackEntries.push(new StackEntry(cur.getFunctionName(), cur.getFileName(), cur.getLineNumber(), cur.getColumnNumber())) |
| 12 | + } |
11 | 13 | } |
| 14 | + }; |
| 15 | + |
| 16 | + function StackEntry(functionName, srcUrl, lineNumber, charNumber) { |
| 17 | + this.fn = functionName; |
| 18 | + this.args = []; |
| 19 | + this.src = srcUrl; |
| 20 | + this.line = lineNumber; |
| 21 | + this.char = charNumber; |
12 | 22 | } |
| 23 | + |
13 | 24 | function ErrorInfo(stack, message) { |
14 | | - return { |
15 | | - stack: stack, |
16 | | - message: message |
17 | | - } |
| 25 | + this.stack = stack; |
| 26 | + this.message = message; |
18 | 27 | } |
| 28 | + |
19 | 29 | function ErrorParser() { |
20 | 30 | // TODO: declare regexps here |
21 | | - return { |
22 | | - /** |
23 | | - * Given an Error object, return the function that |
24 | | - * will extract the most information from it. |
25 | | - * @param e {Error} |
26 | | - * @return Function parser |
| 31 | + |
| 32 | + /** |
| 33 | + * Given an Error object, return the function that |
| 34 | + * will extract the most information from it. |
| 35 | + * @param e {Error} |
| 36 | + * @return Function parser |
| 37 | + */ |
| 38 | + this.chooseParser = function(e) { |
| 39 | + if (e['arguments'] && e.stack) { |
| 40 | + return this.parseV8; |
| 41 | + } else if (e.stack && e.sourceURL) { |
| 42 | + return this.parseNitro; |
| 43 | + } else if (e.stack && e.number) { |
| 44 | + return this.parseChakra; |
| 45 | + } else if (e['opera#sourceloc'] || e.stacktrace) { |
| 46 | + return this.parseOPERA; |
| 47 | + } else if (e.stack) { |
| 48 | + return this.parseSpiderMonkey; |
| 49 | + } |
| 50 | + return this.parseOther; |
| 51 | + }; |
| 52 | + |
| 53 | + this.parseV8 = function(e) { // Chrome and node.js |
| 54 | + return new ErrorInfo(e.stack, e.message); |
| 55 | + }; |
| 56 | + |
| 57 | + this.parseNitro = function(e) { //Safari 6 |
| 58 | + /* |
| 59 | + stack: "@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48\n" + |
| 60 | + "dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52\n" + |
| 61 | + "onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:82\n" + |
| 62 | + "[native code]" |
27 | 63 | */ |
28 | | - chooseParser: function(e) { |
29 | | - if (e['arguments'] && e.stack) { |
30 | | - return this.parseV8; |
31 | | - } else if (e.stack && e.sourceURL) { |
32 | | - return this.parseNitro; |
33 | | - } else if (e.stack && e.number) { |
34 | | - return this.parseChakra; |
35 | | - } else if (e['opera#sourceloc'] || e.stacktrace) { |
36 | | - return this.parseOPERA; |
37 | | - } else if (e.stack) { |
38 | | - return this.parseSpiderMonkey; |
39 | | - } |
40 | | - return this.parseOther; |
41 | | - }, |
42 | | - parseV8: function(e) { //Chrome and node.js |
43 | | - /* |
44 | | - stack: "TypeError: Object #<Object> has no method 'undef'\n" + |
45 | | - " at Object.createException (http://127.0.0.1:8000/js/stacktrace.js:42:18)\n" + |
46 | | - " at Object.run (http://127.0.0.1:8000/js/stacktrace.js:31:25)\n" + |
47 | | - " at printStackTrace (http://127.0.0.1:8000/js/stacktrace.js:18:62)\n" + |
48 | | - " at bar (http://127.0.0.1:8000/js/test/functional/testcase1.html:13:17)\n" + |
49 | | - " at bar (http://127.0.0.1:8000/js/test/functional/testcase1.html:16:5)\n" + |
50 | | - " at foo (http://127.0.0.1:8000/js/test/functional/testcase1.html:20:5)\n" + |
51 | | - " at http://127.0.0.1:8000/js/test/functional/testcase1.html:24:4" |
52 | | - */ |
53 | | - var raw = (e.stack + '\n').replace(/^\S[^\(]+?[\n$]/gm, '') |
54 | | - .replace(/^\s+(at eval )?at\s+/gm, '') |
55 | | - .replace(/^([^\(]+?)([\n$])/gm, '{anonymous}()@$1$2') |
56 | | - .replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}()@$1') |
57 | | - .split('\n'); |
| 64 | + // TODO: optimization - can pull these RegExps out and only compile them once |
| 65 | + var raw = e.stack.replace(/\[native code\]\n/m, '') |
| 66 | + .replace(/^(?=\w+Error\:).*$\n/m, '') |
| 67 | + .replace(/^@/gm, '{anonymous}@') |
| 68 | + .split('\n'); |
| 69 | + // TODO: check function identifier defn |
| 70 | + var re = /^([\{\}\w]+)@(.*)\:(\d+)(\:(\d+))?$/; |
| 71 | + var enhancedStack = raw.filter(function(entry) { |
| 72 | + return entry.indexOf('@') !== -1; |
| 73 | + }).map(function(entry) { |
| 74 | + var m = entry.match(re); |
| 75 | + return new StackEntry(m[1], m[2], m[3], m[5]); |
| 76 | + }); |
| 77 | + return new ErrorInfo(enhancedStack, e.message); |
| 78 | + }; |
58 | 79 |
|
59 | | - var re = /^\s+at ([\{\}\w]+) \((.*)\:(\d+)(\:(\d+))?$/; |
60 | | - var enhancedStack = raw.filter(function(entry) { |
61 | | - return entry.indexOf(' (') !== -1; |
62 | | - }).map(function(entry) { |
63 | | - var m = entry.match(re); |
64 | | - return new StackEntry(m[1], m[2], m[3], m[5]); |
65 | | - }); |
66 | | - return new ErrorInfo(enhancedStack, e.message); |
67 | | - }, |
68 | | - parseNitro: function(e) { //Safari 6 |
69 | | - /* |
70 | | - stack: "@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48\n" + |
71 | | - "dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52\n" + |
72 | | - "onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:82\n" + |
73 | | - "[native code]" |
74 | | - */ |
75 | | - // TODO: optimization - can pull these RegExps out and only compile them once |
76 | | - var raw = e.stack.replace(/\[native code\]\n/m, '') |
77 | | - .replace(/^(?=\w+Error\:).*$\n/m, '') |
78 | | - .replace(/^@/gm, '{anonymous}@') |
79 | | - .split('\n'); |
80 | | - // TODO: check function identifier defn |
81 | | - var re = /^([\{\}\w]+)@(.*)\:(\d+)(\:(\d+))?$/; |
82 | | - var enhancedStack = raw.filter(function(entry) { |
83 | | - return entry.indexOf('@') !== -1; |
84 | | - }).map(function(entry) { |
85 | | - var m = entry.match(re); |
86 | | - return new StackEntry(m[1], m[2], m[3], m[5]); |
87 | | - }); |
88 | | - return new ErrorInfo(enhancedStack, e.message); |
89 | | - }, |
90 | | - parseChakra: function(e) {}, |
91 | | - parseSpiderMonkey: function(e) {}, |
92 | | - parseOPERA: function(e) { |
93 | | - // e.message.indexOf("Backtrace:") > -1 -> opera |
94 | | - // !e.stacktrace -> opera |
95 | | - if (!e.stacktrace) { |
96 | | - return 'opera9'; // use e.message |
97 | | - } |
98 | | - // 'opera#sourceloc' in e -> opera9, opera10a |
99 | | - if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) { |
100 | | - return 'opera9'; // use e.message |
101 | | - } |
102 | | - // e.stacktrace && !e.stack -> opera10a |
103 | | - if (!e.stack) { |
104 | | - return 'opera10a'; // use e.stacktrace |
105 | | - } |
106 | | - // e.stacktrace && e.stack -> opera10b |
107 | | - if (e.stacktrace.indexOf("called from line") < 0) { |
108 | | - return 'opera10b'; // use e.stacktrace, format differs from 'opera10a' |
109 | | - } |
110 | | - // e.stacktrace && e.stack -> opera11 |
111 | | - return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b' |
112 | | - }, |
113 | | - parseOther: function(e) {}, |
114 | | - guessAnonymousFunctionName: function() { |
115 | | - // IDEA: can we use sourcemaps here? |
| 80 | + this.parseChakra = function(e) {}; |
| 81 | + this.parseSpiderMonkey = function(e) {}; |
| 82 | + this.parseOPERA = function(e) { |
| 83 | + // e.message.indexOf("Backtrace:") > -1 -> opera |
| 84 | + // !e.stacktrace -> opera |
| 85 | + if (!e.stacktrace) { |
| 86 | + return 'opera9'; // use e.message |
116 | 87 | } |
| 88 | + // 'opera#sourceloc' in e -> opera9, opera10a |
| 89 | + if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) { |
| 90 | + return 'opera9'; // use e.message |
| 91 | + } |
| 92 | + // e.stacktrace && !e.stack -> opera10a |
| 93 | + if (!e.stack) { |
| 94 | + return 'opera10a'; // use e.stacktrace |
| 95 | + } |
| 96 | + // e.stacktrace && e.stack -> opera10b |
| 97 | + if (e.stacktrace.indexOf("called from line") < 0) { |
| 98 | + return 'opera10b'; // use e.stacktrace, format differs from 'opera10a' |
| 99 | + } |
| 100 | + // e.stacktrace && e.stack -> opera11 |
| 101 | + return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b' |
| 102 | + }; |
| 103 | + this.parseOther = function(e) {}; |
| 104 | + this.guessAnonymousFunctionName = function() { |
| 105 | + // IDEA: can we use sourcemaps here? |
117 | 106 | }; |
118 | 107 | } |
119 | 108 |
|
|
0 commit comments