diff --git a/error-stack-parser.js b/error-stack-parser.js index 41bf29a..410dbce 100644 --- a/error-stack-parser.js +++ b/error-stack-parser.js @@ -54,8 +54,23 @@ }, this); return filtered.map(function(line) { + var evalStackFrame; if (line.indexOf('(eval ') > -1) { - // Throw away eval information until we implement stacktrace.js/stackframe#8 + var regExp = /\), (<[^>]+>:\d+:\d+)\)$/; + var evalParts = regExp.exec(line); + if (evalParts) { + var evalLocationParts = this.extractLocation(evalParts[1]); + + evalStackFrame = new StackFrame({ + functionName: 'eval', + fileName: evalLocationParts[0], + lineNumber: evalLocationParts[1], + columnNumber: evalLocationParts[2], + source: line, + isEval: true + }); + } + line = line.replace(/eval code/g, 'eval').replace(/(\(eval at [^\()]*)|(\)\,.*$)/g, ''); } var tokens = line.replace(/^\s+/, '').replace(/\(eval code/g, '(').split(/\s+/).slice(1); @@ -63,13 +78,19 @@ var functionName = tokens.join(' ') || undefined; var fileName = ['eval', ''].indexOf(locationParts[0]) > -1 ? undefined : locationParts[0]; - return new StackFrame({ + var stackFrame = new StackFrame({ functionName: functionName, fileName: fileName, lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); + + if (evalStackFrame) { + stackFrame.setEvalOrigin(evalStackFrame); + } + + return stackFrame; }, this); }, diff --git a/spec/error-stack-parser-spec.js b/spec/error-stack-parser-spec.js index ed9c023..35c3fa2 100644 --- a/spec/error-stack-parser-spec.js +++ b/spec/error-stack-parser-spec.js @@ -116,6 +116,20 @@ describe('ErrorStackParser', function() { expect(stackFrames[4]).toMatchStackFrame([undefined, undefined, 'http://localhost:8080/file.js', 31, 13]); }); + it('should parse and set eval origin for eval() from V8', function() { + var stackFrames = unit.parse(CapturedExceptions.CHROME_58_EVAL); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.length).toBe(6); + expect(stackFrames[0]).toMatchStackFrame(['willThrow', undefined, 'index.js', 11, undefined]); + expect(stackFrames[0].getEvalOrigin()).toMatchStackFrame(['eval', undefined, '', 3, 3]); + expect(stackFrames[1]).toMatchStackFrame(['eval', undefined, 'index.js', 11, undefined]); + expect(stackFrames[1].getEvalOrigin()).toMatchStackFrame(['eval', undefined, '', 6, 1]); + expect(stackFrames[2]).toMatchStackFrame(['h', undefined, 'index.js', 11, undefined]); + expect(stackFrames[3]).toMatchStackFrame(['g', undefined, 'index.js', 6, undefined]); + expect(stackFrames[4]).toMatchStackFrame(['f', undefined, 'index.js', 2, undefined]); + expect(stackFrames[5]).toMatchStackFrame([undefined, undefined, 'index.js', 23, undefined]); + }); + it('should parse IE 10 Error stacks', function() { var stackFrames = unit.parse(CapturedExceptions.IE_10); expect(stackFrames).toBeTruthy(); diff --git a/spec/fixtures/captured-errors.js b/spec/fixtures/captured-errors.js index e31eced..1ad10d7 100644 --- a/spec/fixtures/captured-errors.js +++ b/spec/fixtures/captured-errors.js @@ -194,6 +194,18 @@ CapturedExceptions.CHROME_48_NESTED_EVAL = { 'at http://localhost:8080/file.js:31:13\n' }; +CapturedExceptions.CHROME_58_EVAL = { + message: 'message string', + name: 'Error', + stack: 'Error: message string\n' + + 'at willThrow (eval at h (index.js:11), :3:3)\n' + + 'at eval (eval at h (index.js:11), :6:1)\n' + + 'at h (index.js:11)\n' + + 'at g (index.js:6)\n' + + 'at f (index.js:2)\n' + + 'at index.js:23\n' +}; + CapturedExceptions.FIREFOX_3 = { fileName: 'http://127.0.0.1:8000/js/stacktrace.js', lineNumber: 44,