Skip to content

Commit

Permalink
Remove legacy stacktrace parsing from Opera 9, 10 (getsentry#716)
Browse files Browse the repository at this point in the history
  • Loading branch information
benvinegar authored and denstepa committed Jan 5, 2017
1 parent a97170b commit 01a7f33
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 243 deletions.
80 changes: 9 additions & 71 deletions test/vendor/tracekit-parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,87 +171,25 @@ describe('TraceKit', function () {
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: 'bar', args: [], line: 109, column: 1 });
});

it('should parse Opera 8.54 error', function () {
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_854);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 7);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: '?', args: [], line: 44, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://path/to/file.js', func: '?', args: [], line: 31, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: '?', args: [], line: 18, column: null });
assert.deepEqual(stackFrames.stack[3], { url: 'http://path/to/file.js', func: '?', args: [], line: 4, column: null });
assert.deepEqual(stackFrames.stack[4], { url: 'http://path/to/file.js', func: '?', args: [], line: 7, column: null });
assert.deepEqual(stackFrames.stack[5], { url: 'http://path/to/file.js', func: '?', args: [], line: 11, column: null });
assert.deepEqual(stackFrames.stack[6], { url: 'http://path/to/file.js', func: '?', args: [], line: 15, column: null });
});

it('should parse Opera 9.02 error', function () {
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_902);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 7);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: '?', args: [], line: 44, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://path/to/file.js', func: '?', args: [], line: 31, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: '?', args: [], line: 18, column: null });
assert.deepEqual(stackFrames.stack[3], { url: 'http://path/to/file.js', func: '?', args: [], line: 4, column: null });
assert.deepEqual(stackFrames.stack[4], { url: 'http://path/to/file.js', func: '?', args: [], line: 7, column: null });
assert.deepEqual(stackFrames.stack[5], { url: 'http://path/to/file.js', func: '?', args: [], line: 11, column: null });
assert.deepEqual(stackFrames.stack[6], { url: 'http://path/to/file.js', func: '?', args: [], line: 15, column: null });
});

it('should parse Opera 9.27 error', function () {
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_927);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 3);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: '?', args: [], line: 43, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://path/to/file.js', func: '?', args: [], line: 31, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: '?', args: [], line: 18, column: null });
});

it('should parse Opera 9.64 error', function () {
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_964);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 6);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: '?', args: [], line: 27, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://path/to/file.js', func: 'printStackTrace', args: [], line: 18, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: 'bar', args: [], line: 4, column: null });
assert.deepEqual(stackFrames.stack[3], { url: 'http://path/to/file.js', func: 'bar', args: [], line: 7, column: null });
assert.deepEqual(stackFrames.stack[4], { url: 'http://path/to/file.js', func: 'foo', args: [], line: 11, column: null });
assert.deepEqual(stackFrames.stack[5], { url: 'http://path/to/file.js', func: '?', args: [], line: 15, column: null });
});

it('should parse Opera 10 error', function () {
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_10);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 7);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: '?', args: [], line: 42, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://path/to/file.js', func: '?', args: [], line: 27, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: 'printStackTrace', args: [], line: 18, column: null });
assert.deepEqual(stackFrames.stack[3], { url: 'http://path/to/file.js', func: 'bar', args: [], line: 4, column: null });
assert.deepEqual(stackFrames.stack[4], { url: 'http://path/to/file.js', func: 'bar', args: [], line: 7, column: null });
assert.deepEqual(stackFrames.stack[5], { url: 'http://path/to/file.js', func: 'foo', args: [], line: 11, column: null });
assert.deepEqual(stackFrames.stack[6], { url: 'http://path/to/file.js', func: '?', args: [], line: 15, column: null });
});

it('should parse Opera 11 error', function () {
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_11);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 7);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: 'createException', args: [], line: 42, column: 12 });
assert.deepEqual(stackFrames.stack[1], { url: 'http://path/to/file.js', func: 'run', args: ['ex'], line: 27, column: 8 });
assert.deepEqual(stackFrames.stack[2], { url: 'http://path/to/file.js', func: 'printStackTrace', args: ['options'], line: 18, column: 4 });
assert.deepEqual(stackFrames.stack[3], { url: 'http://path/to/file.js', func: 'bar', args: ['n'], line: 4, column: 5 });
assert.deepEqual(stackFrames.stack[4], { url: 'http://path/to/file.js', func: 'bar', args: ['n'], line: 7, column: 4 });
assert.deepEqual(stackFrames.stack[5], { url: 'http://path/to/file.js', func: 'foo', args: [], line: 11, column: 4 });
assert.deepEqual(stackFrames.stack[6], { url: 'http://path/to/file.js', func: '?', args: [], line: 15, column: 3 });
assert.deepEqual(stackFrames.stack.length, 5);
assert.deepEqual(stackFrames.stack[0], { url: 'http://path/to/file.js', func: '<anonymous function: run>', args: ['[arguments not available]'], line: 27, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://domain.com:1234/path/to/file.js', func: 'bar', args: ['[arguments not available]'], line: 18, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://domain.com:1234/path/to/file.js', func: 'foo', args: ['[arguments not available]'], line: 11, column: null });
assert.deepEqual(stackFrames.stack[3], { url: 'http://path/to/file.js', func: '<anonymous function>', args: [], line: 15, column: null });
assert.deepEqual(stackFrames.stack[4], { url: 'http://path/to/file.js', func: 'Error created at <anonymous function>', args: [], line: 15, column: null });
});

it('should parse Opera 12 error', function () {
// TODO: Improve anonymous function name.
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.OPERA_12);
assert.ok(stackFrames);
assert.deepEqual(stackFrames.stack.length, 3);
assert.deepEqual(stackFrames.stack[0], { url: 'http://localhost:8000/ExceptionLab.html', func: '<anonymous function>', args: ['x'], line: 48, column: 12 });
assert.deepEqual(stackFrames.stack[1], { url: 'http://localhost:8000/ExceptionLab.html', func: 'dumpException3', args: [], line: 46, column: 8 });
assert.deepEqual(stackFrames.stack[2], { url: 'http://localhost:8000/ExceptionLab.html', func: '<anonymous function>', args: ['event'], line: 1, column: 0 });
assert.deepEqual(stackFrames.stack[0], { url: 'http://localhost:8000/ExceptionLab.html', func: '<anonymous function>', args: ['[arguments not available]'], line: 48, column: null });
assert.deepEqual(stackFrames.stack[1], { url: 'http://localhost:8000/ExceptionLab.html', func: 'dumpException3', args: ['[arguments not available]'], line: 46, column: null });
assert.deepEqual(stackFrames.stack[2], { url: 'http://localhost:8000/ExceptionLab.html', func: '<anonymous function>', args: ['[arguments not available]'], line: 1, column: null });
});

it('should parse Opera 25 error', function () {
Expand Down
172 changes: 0 additions & 172 deletions vendor/TraceKit/tracekit.js
Original file line number Diff line number Diff line change
Expand Up @@ -445,153 +445,6 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
};
}

/**
* Computes stack trace information from the stacktrace property.
* Opera 10 uses this property.
* @param {Error} ex
* @return {?Object.<string, *>} Stack trace information.
*/
function computeStackTraceFromStacktraceProp(ex) {
// Access and store the stacktrace property before doing ANYTHING
// else to it because Opera is not very good at providing it
// reliably in other circumstances.
var stacktrace = ex.stacktrace;
if (isUndefined(ex.stacktrace) || !ex.stacktrace) return;

var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i,
opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i,
lines = stacktrace.split('\n'),
stack = [],
parts;

for (var line = 0; line < lines.length; line += 2) {
var element = null;
if ((parts = opera10Regex.exec(lines[line]))) {
element = {
'url': parts[2],
'line': +parts[1],
'column': null,
'func': parts[3],
'args':[]
};
} else if ((parts = opera11Regex.exec(lines[line]))) {
element = {
'url': parts[6],
'line': +parts[1],
'column': +parts[2],
'func': parts[3] || parts[4],
'args': parts[5] ? parts[5].split(',') : []
};
}

if (element) {
if (!element.func && element.line) {
element.func = UNKNOWN_FUNCTION;
}

stack.push(element);
}
}

if (!stack.length) {
return null;
}

return {
'name': ex.name,
'message': ex.message,
'url': getLocationHref(),
'stack': stack
};
}

/**
* NOT TESTED.
* Computes stack trace information from an error message that includes
* the stack trace.
* Opera 9 and earlier use this method if the option to show stack
* traces is turned on in opera:config.
* @param {Error} ex
* @return {?Object.<string, *>} Stack information.
*/
function computeStackTraceFromOperaMultiLineMessage(ex) {
// Opera includes a stack trace into the exception message. An example is:
//
// Statement on line 3: Undefined variable: undefinedFunc
// Backtrace:
// Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz
// undefinedFunc(a);
// Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy
// zzz(x, y, z);
// Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx
// yyy(a, a, a);
// Line 1 of function script
// try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }
// ...

var lines = ex.message.split('\n');
if (lines.length < 4) {
return null;
}

var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i,
lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i,
lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
stack = [],
scripts = document.getElementsByTagName('script'),
parts;

for (var line = 2; line < lines.length; line += 2) {
var item = null;
if ((parts = lineRE1.exec(lines[line]))) {
item = {
'url': parts[2],
'func': parts[3],
'args': [],
'line': +parts[1],
'column': null
};
} else if ((parts = lineRE2.exec(lines[line]))) {
item = {
'url': parts[3],
'func': parts[4],
'args': [],
'line': +parts[1],
'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.
};
var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block
} else if ((parts = lineRE3.exec(lines[line]))) {
var url = window.location.href.replace(/#.*$/, '');
item = {
'url': url,
'func': '',
'args': [],
'line': parts[1],
'column': null
};
}

if (item) {
if (!item.func) {
item.func = UNKNOWN_FUNCTION;
}

stack.push(item);
}
}

if (!stack.length) {
return null; // could not parse multiline exception message as Opera stack trace
}

return {
'name': ex.name,
'message': lines[0],
'url': getLocationHref(),
'stack': stack
};
}

/**
* Adds information about the first frame to incomplete stack traces.
* Safari and IE require this to get complete data on the first frame.
Expand Down Expand Up @@ -716,20 +569,6 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
var stack = null;
depth = (depth == null ? 0 : +depth);

try {
// This must be tried first because Opera 10 *destroys*
// its stacktrace property if you try to access the stack
// property first!!
stack = computeStackTraceFromStacktraceProp(ex);
if (stack) {
return stack;
}
} catch (e) {
if (TraceKit.debug) {
throw e;
}
}

try {
stack = computeStackTraceFromStackProp(ex);
if (stack) {
Expand All @@ -741,17 +580,6 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
}
}

try {
stack = computeStackTraceFromOperaMultiLineMessage(ex);
if (stack) {
return stack;
}
} catch (e) {
if (TraceKit.debug) {
throw e;
}
}

try {
stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);
if (stack) {
Expand Down

0 comments on commit 01a7f33

Please sign in to comment.