Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions packages/browser/src/tracekit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ interface ComputeStackTrace {
* @param {(string|number)=} depth
*/
(ex: Error, depth?: string | number): StackTrace;
_computeStackTraceFromStackProp(ex: any): StackTrace;
}

/**
Expand Down Expand Up @@ -469,10 +470,11 @@ TraceKit._computeStackTrace = (function _computeStackTraceWrapper() {
* @memberof TraceKit._computeStackTrace
*/
function _computeStackTraceFromStackProp(ex: any) {
if (!ex.stack) {
if (!ex || !ex.stack) {
return null;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a nitpick, can we just document that chrome is also used for edge.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its the line below that

// Chromium based browsers: Chrome, Brave, new Opera, new Edge
var chrome = /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
// gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it
// generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
Expand All @@ -492,19 +494,17 @@ TraceKit._computeStackTrace = (function _computeStackTraceWrapper() {

for (var i = 0, j = lines.length; i < j; ++i) {
if ((parts = chrome.exec(lines[i]))) {
var isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line
isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
if (isEval && (submatch = chromeEval.exec(parts[2]))) {
// throw out eval line/column and use top-most line/column number
parts[2] = submatch[1]; // url
// NOTE: It's messing out our integration tests in Karma, let's see if we can live with it – Kamil
// parts[3] = submatch[2]; // line
// parts[4] = submatch[3]; // column
parts[3] = submatch[2]; // line
parts[4] = submatch[3]; // column
}
element = {
url: !isNative ? parts[2] : null,
url: parts[2],
func: parts[1] || UNKNOWN_FUNCTION,
args: isNative ? [parts[2]] : [],
args: [],
line: parts[3] ? +parts[3] : null,
column: parts[4] ? +parts[4] : null,
};
Expand All @@ -521,9 +521,8 @@ TraceKit._computeStackTrace = (function _computeStackTraceWrapper() {
if (isEval && (submatch = geckoEval.exec(parts[3]))) {
// throw out eval line/column and use top-most line number
parts[3] = submatch[1];
// NOTE: It's messing out our integration tests in Karma, let's see if we can live with it – Kamil
// parts[4] = submatch[2];
// parts[5] = null; // no column when eval
parts[4] = submatch[2];
parts[5] = ''; // no column when eval
} else if (i === 0 && !parts[5] && ex.columnNumber !== void 0) {
// FireFox uses this awesome columnNumber property for its top frame
// Also note, Firefox's column number is 0-based and everything else expects 1-based,
Expand Down
194 changes: 194 additions & 0 deletions packages/browser/test/tracekit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { expect } from 'chai';

import { _computeStackTrace } from '../src/tracekit';

const CHROME73_NATIVE_CODE_EXCEPTION = {
stack: `Error: test
at fooIterator (http://192.168.20.143:5000/test:20:17)
at Array.map (<anonymous>)
at foo (http://192.168.20.143:5000/test:19:19)
at http://192.168.20.143:5000/test:24:7`,
};

const FIREFOX66_NATIVE_CODE_EXCEPTION = {
stack: `fooIterator@http://192.168.20.143:5000/test:20:17
foo@http://192.168.20.143:5000/test:19:19
@http://192.168.20.143:5000/test:24:7`,
};

const SAFARI12_NATIVE_CODE_EXCEPTION = {
stack: `fooIterator@http://192.168.20.143:5000/test:20:26
map@[native code]
foo@http://192.168.20.143:5000/test:19:22
global code@http://192.168.20.143:5000/test:24:10`,
};

const EDGE44_NATIVE_CODE_EXCEPTION = {
stack: `Error: test
at fooIterator (http://192.168.20.143:5000/test:20:11)
at Array.prototype.map (native code)
at foo (http://192.168.20.143:5000/test:19:9)
at Global code (http://192.168.20.143:5000/test:24:7)`,
};

describe('Tracekit', () => {
describe('computeStackTrace', () => {
it('no exception', () => {
const stacktrace = _computeStackTrace._computeStackTraceFromStackProp(undefined);
expect(stacktrace).equal(null);
});

it('no stack', () => {
const stacktrace = _computeStackTrace._computeStackTraceFromStackProp({});
expect(stacktrace).equal(null);
});

it('chrome73', () => {
const stacktrace = _computeStackTrace._computeStackTraceFromStackProp(CHROME73_NATIVE_CODE_EXCEPTION);

expect(stacktrace.stack).deep.equal([
{
args: [],
column: 17,
context: null,
func: 'fooIterator',
line: 20,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: null,
context: null,
func: 'Array.map',
line: null,
url: '<anonymous>',
},
{
args: [],
column: 19,
context: null,
func: 'foo',
line: 19,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: 7,
context: null,
func: '?',
line: 24,
url: 'http://192.168.20.143:5000/test',
},
]);
});

it('firefox66', () => {
const stacktrace = _computeStackTrace._computeStackTraceFromStackProp(FIREFOX66_NATIVE_CODE_EXCEPTION);

expect(stacktrace.stack).deep.equal([
{
args: [],
column: 17,
context: null,
func: 'fooIterator',
line: 20,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: 19,
context: null,
func: 'foo',
line: 19,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: 7,
context: null,
func: '?',
line: 24,
url: 'http://192.168.20.143:5000/test',
},
]);
});

it('safari12', () => {
const stacktrace = _computeStackTrace._computeStackTraceFromStackProp(SAFARI12_NATIVE_CODE_EXCEPTION);

expect(stacktrace.stack).deep.equal([
{
args: [],
column: 26,
context: null,
func: 'fooIterator',
line: 20,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: null,
context: null,
func: 'map',
line: null,
url: '[native code]',
},
{
args: [],
column: 22,
context: null,
func: 'foo',
line: 19,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: 10,
context: null,
func: 'global code',
line: 24,
url: 'http://192.168.20.143:5000/test',
},
]);
});

it('edge44', () => {
const stacktrace = _computeStackTrace._computeStackTraceFromStackProp(EDGE44_NATIVE_CODE_EXCEPTION);

expect(stacktrace.stack).deep.equal([
{
args: [],
column: 11,
context: null,
func: 'fooIterator',
line: 20,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: null,
context: null,
func: 'Array.prototype.map',
line: null,
url: 'native code',
},
{
args: [],
column: 9,
context: null,
func: 'foo',
line: 19,
url: 'http://192.168.20.143:5000/test',
},
{
args: [],
column: 7,
context: null,
func: 'Global code',
line: 24,
url: 'http://192.168.20.143:5000/test',
},
]);
});
});
});