Skip to content

Commit 5b9f9f8

Browse files
author
Eric Wendelin
committed
Switch to Object-based construction from Constructor based. e.g. StackTrace.get(). Simplify code with stacktrace-gps enhancements.
1 parent b2adcab commit 5b9f9f8

File tree

5 files changed

+92
-137
lines changed

5 files changed

+92
-137
lines changed

dist/stacktrace.js

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
ES6Promise.polyfill();
1313
var Promise = ES6Promise.Promise;
1414

15+
var _options = {
16+
filter: function (stackframe) {
17+
// Filter out stackframes for this library by default
18+
return (stackframe.functionName || '').indexOf('StackTrace$$') === -1 &&
19+
(stackframe.functionName || '').indexOf('ErrorStackParser$$') === -1 &&
20+
(stackframe.functionName || '').indexOf('StackTraceGPS$$') === -1 &&
21+
(stackframe.functionName || '').indexOf('StackGenerator$$') === -1;
22+
}
23+
};
24+
1525
/**
1626
* Merge 2 given Objects. If a conflict occurs the second object wins.
1727
* Does not do deep merges.
@@ -35,73 +45,56 @@
3545
return target;
3646
}
3747

38-
return function StackTrace() {
39-
this.options = {
40-
filter: function (stackframe) {
41-
// Filter out stackframes for this library by default
42-
return (stackframe.functionName || '').indexOf('StackTrace$$') !== 0 &&
43-
(stackframe.functionName || '').indexOf('ErrorStackParser$$') !== 0 &&
44-
(stackframe.functionName || '').indexOf('StackTraceGPS$$') !== 0 &&
45-
(stackframe.functionName || '').indexOf('StackGenerator$$') !== 0;
46-
}
47-
};
48-
48+
return {
4949
/**
5050
* Get a backtrace from invocation point.
5151
* @param opts Options Object
5252
* @return Array[StackFrame]
5353
*/
54-
this.get = function StackTrace$$get(opts) {
54+
get: function StackTrace$$get(opts) {
5555
var err = new Error();
5656
if (err.stack || err['opera#sourceloc']) {
5757
return this.fromError(err, opts);
5858
} else {
5959
return this.generateArtificially(opts);
6060
}
61-
};
61+
},
6262

6363
/**
6464
* Given an error object, parse it.
6565
* @param error Error object
6666
* @param opts Object for options
6767
* @return Array[StackFrame]
6868
*/
69-
this.fromError = function StackTrace$$fromError(error, opts) {
70-
opts = _merge(this.options, opts);
69+
fromError: function StackTrace$$fromError(error, opts) {
70+
opts = _merge(_options, opts);
7171
return new Promise(function (resolve) {
7272
var stackframes = ErrorStackParser.parse(error);
7373
if (typeof opts.filter === 'function') {
7474
stackframes = stackframes.filter(opts.filter);
7575
}
76-
resolve(Promise.all(stackframes.map(this.getMappedLocation)));
76+
resolve(Promise.all(stackframes.map(function(sf) {
77+
return new Promise(function (resolve) {
78+
function resolveOriginal(_) { resolve(sf); }
79+
new StackTraceGPS(opts).pinpoint(sf)
80+
.then(resolve, resolveOriginal)['catch'](resolveOriginal);
81+
});
82+
})));
7783
}.bind(this));
78-
};
79-
80-
this.getMappedLocation = function StackTrace$$getMappedLocation(stackframe) {
81-
return new Promise(function (resolve) {
82-
// TODO: pass along pre-cache
83-
new StackTraceGPS().getMappedLocation(stackframe)
84-
.then(function onResolved(loc) {
85-
resolve(new StackFrame(loc.name, stackframe.args, loc.source, loc.line, loc.column));
86-
})['catch'](function onError() {
87-
resolve(stackframe);
88-
});
89-
});
90-
};
84+
},
9185

9286
/**
9387
* Use StackGenerator to generate a backtrace.
9488
* @param opts Object options
9589
* @returns Array[StackFrame]
9690
*/
97-
this.generateArtificially = function StackTrace$$generateArtificially(opts) {
98-
opts = _merge(this.options, opts);
91+
generateArtificially: function StackTrace$$generateArtificially(opts) {
92+
opts = _merge(_options, opts);
9993
var stackFrames = StackGenerator.backtrace(opts);
10094
if (typeof opts.filter === 'function') {
10195
stackFrames = stackFrames.filter(opts.filter);
10296
}
10397
return Promise.resolve(stackFrames);
104-
};
98+
}
10599
};
106100
}));
107-

dist/stacktrace.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/stacktrace.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/stacktrace-spec.js

100644100755
Lines changed: 37 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* global Errors: false */
12
describe('StackTrace', function () {
23
var callback;
34
var debugCallback;
@@ -16,18 +17,10 @@ describe('StackTrace', function () {
1617
};
1718
});
1819

19-
describe('#constructor', function () {
20-
it('should allow empty arguments', function () {
21-
expect(function () {
22-
new StackTrace(); // jshint ignore:line
23-
}).not.toThrow();
24-
});
25-
});
26-
2720
describe('#get', function () {
2821
it('gets stacktrace from current location', function () {
2922
runs(function testStackTraceGet() {
30-
new StackTrace().get().then(callback, errback)['catch'](debugErrback);
23+
StackTrace.get().then(callback, errback)['catch'](debugErrback);
3124
});
3225
waits(100);
3326
runs(function () {
@@ -39,9 +32,17 @@ describe('StackTrace', function () {
3932
});
4033

4134
describe('#fromError', function () {
42-
it('rejects with Error given non-Error object', function () {
35+
var server;
36+
beforeEach(function () {
37+
server = sinon.fakeServer.create();
38+
});
39+
afterEach(function () {
40+
server.restore();
41+
});
42+
43+
it('rejects with Error given unparsable Error object', function () {
4344
runs(function () {
44-
new StackTrace().fromError('BOGUS')
45+
StackTrace.fromError({message: 'ERROR_MESSAGE'})
4546
.then(callback, errback)['catch'](errback);
4647
});
4748
waits(100);
@@ -52,83 +53,49 @@ describe('StackTrace', function () {
5253
});
5354

5455
it('parses stacktrace from given Error object', function () {
55-
//FIXME: shims for IE9
5656
runs(function () {
57-
try {
58-
throw new Error('Yikes!');
59-
} catch (e) {
60-
new StackTrace().fromError(e)
61-
.then(callback, errback)['catch'](errback);
62-
}
63-
});
64-
waits(100);
65-
runs(function () {
66-
expect(callback).toHaveBeenCalled();
67-
expect(errback).not.toHaveBeenCalled();
57+
server.respondWith('GET', 'http://path/to/file.js', [404, { 'Content-Type': 'text/plain' }, '']);
58+
StackTrace.fromError(Errors.IE_11)
59+
.then(callback, debugErrback)['catch'](debugErrback);
60+
server.respond();
6861
});
69-
});
70-
71-
it('totally extracts function names', function () {
72-
//FIXME: shims for IE9
73-
//FIXME: need function name for opera 12
74-
var TEST_FUNCTION = function TEST_FUNCTION() {
75-
try {
76-
throw new Error('Yikes!');
77-
} catch (e) {
78-
function onlySpecSourcesPlease(stackFrame) {
79-
return (stackFrame.fileName || '').indexOf('stacktrace-spec.js') !== -1;
80-
}
81-
82-
new StackTrace().fromError(e, {filter: onlySpecSourcesPlease})
83-
.then(callback, errback)['catch'](errback);
84-
}
85-
};
86-
runs(TEST_FUNCTION);
8762
waits(100);
8863
runs(function () {
8964
expect(callback).toHaveBeenCalled();
9065
var stackFrames = callback.mostRecentCall.args[0];
91-
expect(stackFrames.length).toEqual(1);
92-
expect(stackFrames[0].fileName).toMatch(/stacktrace\-spec\.js\b/);
93-
expect(stackFrames[0].functionName).toEqual('TEST_FUNCTION');
66+
expect(stackFrames.length).toEqual(3);
67+
expect(stackFrames[0].fileName).toEqual('http://path/to/file.js');
9468
expect(errback).not.toHaveBeenCalled();
9569
});
9670
});
9771

98-
xit('uses source maps to enhance stack frames', function () {
99-
100-
});
101-
});
102-
103-
describe('#getMappedLocation', function () {
104-
var server;
105-
beforeEach(function () {
106-
server = sinon.fakeServer.create();
107-
});
108-
afterEach(function () {
109-
server.restore();
110-
});
111-
112-
it('defaults to given stackframe if source map location not found', function () {
72+
it('filters returned stack', function () {
11373
runs(function () {
114-
var stackframe = new StackFrame(undefined, [], 'http://localhost:9999/test.min.js', 1, 32);
115-
new StackTrace().getMappedLocation(stackframe).then(callback, errback)['catch'](debugErrback);
74+
function onlyFoos(stackFrame) {
75+
return stackFrame.functionName === 'foo';
76+
}
77+
78+
StackTrace.fromError(Errors.IE_11, {filter: onlyFoos})
79+
.then(callback, errback)['catch'](debugErrback);
11680
server.requests[0].respond(404, {}, '');
11781
});
11882
waits(100);
11983
runs(function () {
12084
expect(callback).toHaveBeenCalled();
121-
expect(callback.mostRecentCall.args[0]).toMatchStackFrame([undefined, [], 'http://localhost:9999/test.min.js', 1, 32]);
85+
var stackFrames = callback.mostRecentCall.args[0];
86+
expect(stackFrames.length).toEqual(1);
87+
expect(stackFrames[0].fileName).toEqual('http://path/to/file.js');
88+
expect(stackFrames[0].functionName).toEqual('foo');
12289
expect(errback).not.toHaveBeenCalled();
12390
});
12491
});
12592

12693
it('uses source maps to enhance stack frames', function () {
12794
runs(function () {
128-
var stackframe = new StackFrame(undefined, [], 'http://localhost:9999/test.min.js', 1, 32);
129-
new StackTrace().getMappedLocation(stackframe).then(callback, errback)['catch'](debugErrback);
130-
var source = 'var foo=function(){};function bar(){}var baz=eval("XXX");\n//@ sourceMappingURL=test.js.map';
131-
server.requests[0].respond(200, {'Content-Type': 'application/x-javascript'}, source);
95+
var stack = 'TypeError: Unable to get property \'undef\' of undefined or null reference\n at foo (http://path/to/file.js:45:13)';
96+
StackTrace.fromError({stack: stack}).then(callback, errback)['catch'](debugErrback);
97+
var sourceMin = 'var foo=function(){};function bar(){}var baz=eval("XXX");\n//@ sourceMappingURL=test.js.map';
98+
server.requests[0].respond(200, {'Content-Type': 'application/x-javascript'}, sourceMin);
13299
});
133100
waits(100);
134101
runs(function () {
@@ -138,7 +105,9 @@ describe('StackTrace', function () {
138105
waits(100);
139106
runs(function () {
140107
expect(callback).toHaveBeenCalled();
141-
expect(callback.mostRecentCall.args[0]).toMatchStackFrame(['bar', [], './test.js', 2, 9]);
108+
var stackFrames = callback.mostRecentCall.args[0];
109+
expect(stackFrames.length).toEqual(1);
110+
expect(stackFrames[0]).toMatchStackFrame(['foo', undefined, 'http://path/to/file.js', 45, 13]);
142111
expect(errback).not.toHaveBeenCalled();
143112
});
144113
});
@@ -151,7 +120,7 @@ describe('StackTrace', function () {
151120
return stackFrame.getFunctionName() &&
152121
stackFrame.getFunctionName().indexOf('testGenerateArtificially') > -1;
153122
};
154-
new StackTrace().generateArtificially({filter: stackFrameFilter})
123+
StackTrace.generateArtificially({filter: stackFrameFilter})
155124
.then(callback, errback)['catch'](debugErrback);
156125
});
157126
waits(100);

stacktrace.js

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
ES6Promise.polyfill();
1313
var Promise = ES6Promise.Promise;
1414

15+
var _options = {
16+
filter: function (stackframe) {
17+
// Filter out stackframes for this library by default
18+
return (stackframe.functionName || '').indexOf('StackTrace$$') === -1 &&
19+
(stackframe.functionName || '').indexOf('ErrorStackParser$$') === -1 &&
20+
(stackframe.functionName || '').indexOf('StackTraceGPS$$') === -1 &&
21+
(stackframe.functionName || '').indexOf('StackGenerator$$') === -1;
22+
}
23+
};
24+
1525
/**
1626
* Merge 2 given Objects. If a conflict occurs the second object wins.
1727
* Does not do deep merges.
@@ -35,73 +45,56 @@
3545
return target;
3646
}
3747

38-
return function StackTrace() {
39-
this.options = {
40-
filter: function (stackframe) {
41-
// Filter out stackframes for this library by default
42-
return (stackframe.functionName || '').indexOf('StackTrace$$') !== 0 &&
43-
(stackframe.functionName || '').indexOf('ErrorStackParser$$') !== 0 &&
44-
(stackframe.functionName || '').indexOf('StackTraceGPS$$') !== 0 &&
45-
(stackframe.functionName || '').indexOf('StackGenerator$$') !== 0;
46-
}
47-
};
48-
48+
return {
4949
/**
5050
* Get a backtrace from invocation point.
5151
* @param opts Options Object
5252
* @return Array[StackFrame]
5353
*/
54-
this.get = function StackTrace$$get(opts) {
54+
get: function StackTrace$$get(opts) {
5555
var err = new Error();
5656
if (err.stack || err['opera#sourceloc']) {
5757
return this.fromError(err, opts);
5858
} else {
5959
return this.generateArtificially(opts);
6060
}
61-
};
61+
},
6262

6363
/**
6464
* Given an error object, parse it.
6565
* @param error Error object
6666
* @param opts Object for options
6767
* @return Array[StackFrame]
6868
*/
69-
this.fromError = function StackTrace$$fromError(error, opts) {
70-
opts = _merge(this.options, opts);
69+
fromError: function StackTrace$$fromError(error, opts) {
70+
opts = _merge(_options, opts);
7171
return new Promise(function (resolve) {
7272
var stackframes = ErrorStackParser.parse(error);
7373
if (typeof opts.filter === 'function') {
7474
stackframes = stackframes.filter(opts.filter);
7575
}
76-
resolve(Promise.all(stackframes.map(this.getMappedLocation)));
76+
resolve(Promise.all(stackframes.map(function(sf) {
77+
return new Promise(function (resolve) {
78+
function resolveOriginal(_) { resolve(sf); }
79+
new StackTraceGPS(opts).pinpoint(sf)
80+
.then(resolve, resolveOriginal)['catch'](resolveOriginal);
81+
});
82+
})));
7783
}.bind(this));
78-
};
79-
80-
this.getMappedLocation = function StackTrace$$getMappedLocation(stackframe) {
81-
return new Promise(function (resolve) {
82-
// TODO: pass along pre-cache
83-
new StackTraceGPS().getMappedLocation(stackframe)
84-
.then(function onResolved(loc) {
85-
resolve(new StackFrame(loc.name, stackframe.args, loc.source, loc.line, loc.column));
86-
})['catch'](function onError() {
87-
resolve(stackframe);
88-
});
89-
});
90-
};
84+
},
9185

9286
/**
9387
* Use StackGenerator to generate a backtrace.
9488
* @param opts Object options
9589
* @returns Array[StackFrame]
9690
*/
97-
this.generateArtificially = function StackTrace$$generateArtificially(opts) {
98-
opts = _merge(this.options, opts);
91+
generateArtificially: function StackTrace$$generateArtificially(opts) {
92+
opts = _merge(_options, opts);
9993
var stackFrames = StackGenerator.backtrace(opts);
10094
if (typeof opts.filter === 'function') {
10195
stackFrames = stackFrames.filter(opts.filter);
10296
}
10397
return Promise.resolve(stackFrames);
104-
};
98+
}
10599
};
106100
}));
107-

0 commit comments

Comments
 (0)