fix(errors): require passing stack traces explicitly in ng2 own code
This commit is contained in:
parent
5c88f662cd
commit
8ab773538b
|
@ -259,8 +259,8 @@ export function bootstrap(appComponentType: Type,
|
||||||
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||||
},
|
},
|
||||||
|
|
||||||
(err) => {
|
(err, stackTrace) => {
|
||||||
bootstrapProcess.reject(err)
|
bootstrapProcess.reject(err, stackTrace)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -338,7 +338,7 @@ class _AsyncInjectorStrategy {
|
||||||
var deps = this.injector._resolveDependencies(key, binding, true);
|
var deps = this.injector._resolveDependencies(key, binding, true);
|
||||||
var depsPromise = PromiseWrapper.all(deps);
|
var depsPromise = PromiseWrapper.all(deps);
|
||||||
|
|
||||||
var promise = PromiseWrapper.then(depsPromise, null, (e) => this._errorHandler(key, e))
|
var promise = PromiseWrapper.then(depsPromise, null, (e, s) => this._errorHandler(key, e, s))
|
||||||
.then(deps => this._findOrCreate(key, binding, deps))
|
.then(deps => this._findOrCreate(key, binding, deps))
|
||||||
.then(instance => this._cacheInstance(key, instance));
|
.then(instance => this._cacheInstance(key, instance));
|
||||||
|
|
||||||
|
@ -346,9 +346,9 @@ class _AsyncInjectorStrategy {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
_errorHandler(key: Key, e): Promise<any> {
|
_errorHandler(key: Key, e, stack): Promise<any> {
|
||||||
if (e instanceof AbstractBindingError) e.addKey(key);
|
if (e instanceof AbstractBindingError) e.addKey(key);
|
||||||
return PromiseWrapper.reject(e);
|
return PromiseWrapper.reject(e, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
_findOrCreate(key: Key, binding: ResolvedBinding, deps: List<any>) {
|
_findOrCreate(key: Key, binding: ResolvedBinding, deps: List<any>) {
|
||||||
|
|
|
@ -6,9 +6,13 @@ export 'dart:async' show Future, Stream, StreamController, StreamSubscription;
|
||||||
class PromiseWrapper {
|
class PromiseWrapper {
|
||||||
static Future resolve(obj) => new Future.value(obj);
|
static Future resolve(obj) => new Future.value(obj);
|
||||||
|
|
||||||
static Future reject(obj) => new Future.error(
|
static Future reject(obj, stackTrace) => new Future.error(
|
||||||
obj,
|
obj,
|
||||||
obj is Error ? obj.stackTrace : null);
|
stackTrace != null
|
||||||
|
? stackTrace
|
||||||
|
: obj is Error
|
||||||
|
? obj.stackTrace
|
||||||
|
: null);
|
||||||
|
|
||||||
static Future<List> all(List<Future> promises) => Future.wait(promises);
|
static Future<List> all(List<Future> promises) => Future.wait(promises);
|
||||||
|
|
||||||
|
@ -23,7 +27,7 @@ class PromiseWrapper {
|
||||||
return promise.catchError(onError);
|
return promise.catchError(onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Completer completer() => new _Completer(new Completer());
|
static CompleterWrapper completer() => new CompleterWrapper(new Completer());
|
||||||
|
|
||||||
// TODO(vic): create a TimerWrapper
|
// TODO(vic): create a TimerWrapper
|
||||||
static Timer setTimeout(fn(), int millis)
|
static Timer setTimeout(fn(), int millis)
|
||||||
|
@ -99,10 +103,10 @@ class EventEmitter extends Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Completer {
|
class CompleterWrapper {
|
||||||
final Completer c;
|
final Completer c;
|
||||||
|
|
||||||
_Completer(this.c);
|
CompleterWrapper(this.c);
|
||||||
|
|
||||||
Future get promise => c.future;
|
Future get promise => c.future;
|
||||||
|
|
||||||
|
@ -110,11 +114,10 @@ class _Completer {
|
||||||
c.complete(v);
|
c.complete(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reject(v) {
|
void reject(error, stack) {
|
||||||
var stack = null;
|
if (stack == null && error is Error) {
|
||||||
if (v is Error) {
|
stack = error.stackTrace;
|
||||||
stack = v.stackTrace;
|
|
||||||
}
|
}
|
||||||
c.completeError(v, stack);
|
c.completeError(error, stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export var Promise = (<any>global).Promise;
|
||||||
export class PromiseWrapper {
|
export class PromiseWrapper {
|
||||||
static resolve(obj): Promise<any> { return Promise.resolve(obj); }
|
static resolve(obj): Promise<any> { return Promise.resolve(obj); }
|
||||||
|
|
||||||
static reject(obj): Promise<any> { return Promise.reject(obj); }
|
static reject(obj, _): Promise<any> { return Promise.reject(obj); }
|
||||||
|
|
||||||
// Note: We can't rename this method into `catch`, as this is not a valid
|
// Note: We can't rename this method into `catch`, as this is not a valid
|
||||||
// method name in Dart.
|
// method name in Dart.
|
||||||
|
@ -29,7 +29,7 @@ export class PromiseWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
static then<T>(promise: Promise<T>, success: (value: any) => T | Thenable<T>,
|
static then<T>(promise: Promise<T>, success: (value: any) => T | Thenable<T>,
|
||||||
rejection: (error: any) => T | Thenable<T>): Promise<T> {
|
rejection: (error: any, stack?: any) => T | Thenable<T>): Promise<T> {
|
||||||
return promise.then(success, rejection);
|
return promise.then(success, rejection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ class _PendingRequest {
|
||||||
|
|
||||||
complete(response: string) {
|
complete(response: string) {
|
||||||
if (isBlank(response)) {
|
if (isBlank(response)) {
|
||||||
this.completer.reject(`Failed to load ${this.url}`);
|
this.completer.reject(`Failed to load ${this.url}`, null);
|
||||||
} else {
|
} else {
|
||||||
this.completer.resolve(response);
|
this.completer.resolve(response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:angular2/src/test_lib/test_bed.dart';
|
||||||
import 'package:angular2/test_lib.dart';
|
import 'package:angular2/test_lib.dart';
|
||||||
|
|
||||||
class MockException implements Error { var message; var stackTrace; }
|
class MockException implements Error { var message; var stackTrace; }
|
||||||
|
class NonError { var message; }
|
||||||
|
|
||||||
void functionThatThrows() {
|
void functionThatThrows() {
|
||||||
try { throw new MockException(); }
|
try { throw new MockException(); }
|
||||||
|
@ -19,6 +20,16 @@ void functionThatThrows() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void functionThatThrowsNonError() {
|
||||||
|
try { throw new NonError(); }
|
||||||
|
catch(e, stack) {
|
||||||
|
// If we lose the stack trace the message will no longer match
|
||||||
|
// the first line in the stack
|
||||||
|
e.message = stack.toString().split('\n')[0];
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
describe('TypeLiteral', () {
|
describe('TypeLiteral', () {
|
||||||
it('should publish via appInjector',
|
it('should publish via appInjector',
|
||||||
|
@ -37,7 +48,7 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Error handling', () {
|
describe('Error handling', () {
|
||||||
it('should preserve stack traces throws from components',
|
it('should preserve Error stack traces thrown from components',
|
||||||
inject([TestBed, AsyncTestCompleter], (tb, async) {
|
inject([TestBed, AsyncTestCompleter], (tb, async) {
|
||||||
tb.overrideView(Dummy, new View(
|
tb.overrideView(Dummy, new View(
|
||||||
template: '<throwing-component></throwing-component>',
|
template: '<throwing-component></throwing-component>',
|
||||||
|
@ -49,6 +60,19 @@ main() {
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should preserve non-Error stack traces thrown from components',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) {
|
||||||
|
tb.overrideView(Dummy, new View(
|
||||||
|
template: '<throwing-component2></throwing-component2>',
|
||||||
|
directives: [ThrowingComponent2]
|
||||||
|
));
|
||||||
|
|
||||||
|
tb.createView(Dummy).catchError((e, stack) {
|
||||||
|
expect(stack.toString().split('\n')[0]).toEqual(e.message);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,3 +105,13 @@ class ThrowingComponent {
|
||||||
functionThatThrows();
|
functionThatThrows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'throwing-component2'
|
||||||
|
)
|
||||||
|
@View(template: '')
|
||||||
|
class ThrowingComponent2 {
|
||||||
|
ThrowingComponent2() {
|
||||||
|
functionThatThrowsNonError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:angular2/test_lib.dart';
|
||||||
import 'package:angular2/src/facade/async.dart';
|
import 'package:angular2/src/facade/async.dart';
|
||||||
|
|
||||||
class MockException implements Error { var message; var stackTrace; }
|
class MockException implements Error { var message; var stackTrace; }
|
||||||
|
class NonError { var message; }
|
||||||
|
|
||||||
void functionThatThrows() {
|
void functionThatThrows() {
|
||||||
try { throw new MockException(); }
|
try { throw new MockException(); }
|
||||||
|
@ -18,7 +19,13 @@ void functionThatThrows() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void functionThatThrowsNonError() {
|
void functionThatThrowsNonError() {
|
||||||
throw 'this is an error';
|
try { throw new NonError(); }
|
||||||
|
catch(e, stack) {
|
||||||
|
// If we lose the stack trace the message will no longer match
|
||||||
|
// the first line in the stack
|
||||||
|
e.message = stack.toString().split('\n')[0];
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void expectFunctionThatThrowsWithStackTrace(
|
void expectFunctionThatThrowsWithStackTrace(
|
||||||
|
@ -29,72 +36,65 @@ void expectFunctionThatThrowsWithStackTrace(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void expectFunctionThatThrowsWithoutStackTrace(Future future,
|
|
||||||
AsyncTestCompleter async) {
|
|
||||||
PromiseWrapper.catchError(future, (err, StackTrace stack) {
|
|
||||||
expect(stack).toBe(null);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
describe('Completer', () {
|
describe('async facade', () {
|
||||||
|
describe('Completer', () {
|
||||||
|
|
||||||
it('should preserve error stack traces',
|
it('should preserve Error stack traces',
|
||||||
inject([AsyncTestCompleter], (async) {
|
|
||||||
var c = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
expectFunctionThatThrowsWithStackTrace(c.promise, async);
|
|
||||||
|
|
||||||
try {
|
|
||||||
functionThatThrows();
|
|
||||||
} catch(e) {
|
|
||||||
c.reject(e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// TODO: We might fix this one day; for now testing it to be explicit
|
|
||||||
it('CANNOT preserve error stack traces for non-Errors',
|
|
||||||
inject([AsyncTestCompleter], (async) {
|
|
||||||
var c = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
expectFunctionThatThrowsWithoutStackTrace(c.promise, async);
|
|
||||||
|
|
||||||
try {
|
|
||||||
functionThatThrowsNonError();
|
|
||||||
} catch(e) {
|
|
||||||
c.reject(e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('PromiseWrapper', () {
|
|
||||||
|
|
||||||
describe('reject', () {
|
|
||||||
|
|
||||||
it('should preserve error stack traces',
|
|
||||||
inject([AsyncTestCompleter], (async) {
|
inject([AsyncTestCompleter], (async) {
|
||||||
|
var c = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
expectFunctionThatThrowsWithStackTrace(c.promise, async);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
functionThatThrows();
|
functionThatThrows();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
var rejectedFuture = PromiseWrapper.reject(e);
|
c.reject(e, null);
|
||||||
expectFunctionThatThrowsWithStackTrace(rejectedFuture, async);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: We might fix this one day; for now testing it to be explicit
|
it('should preserve error stack traces for non-Errors',
|
||||||
it('CANNOT preserve stack traces for non-Errors',
|
|
||||||
inject([AsyncTestCompleter], (async) {
|
inject([AsyncTestCompleter], (async) {
|
||||||
|
var c = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
expectFunctionThatThrowsWithStackTrace(c.promise, async);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
functionThatThrowsNonError();
|
functionThatThrowsNonError();
|
||||||
} catch(e) {
|
} catch(e, s) {
|
||||||
var rejectedFuture = PromiseWrapper.reject(e);
|
c.reject(e, s);
|
||||||
expectFunctionThatThrowsWithoutStackTrace(rejectedFuture, async);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PromiseWrapper', () {
|
||||||
|
|
||||||
|
describe('reject', () {
|
||||||
|
|
||||||
|
it('should preserve Error stack traces',
|
||||||
|
inject([AsyncTestCompleter], (async) {
|
||||||
|
try {
|
||||||
|
functionThatThrows();
|
||||||
|
} catch(e) {
|
||||||
|
var rejectedFuture = PromiseWrapper.reject(e, null);
|
||||||
|
expectFunctionThatThrowsWithStackTrace(rejectedFuture, async);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should preserve stack traces for non-Errors',
|
||||||
|
inject([AsyncTestCompleter], (async) {
|
||||||
|
try {
|
||||||
|
functionThatThrowsNonError();
|
||||||
|
} catch(e, s) {
|
||||||
|
var rejectedFuture = PromiseWrapper.reject(e, s);
|
||||||
|
expectFunctionThatThrowsWithStackTrace(rejectedFuture, async);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ class FakeTemplateLoader extends TemplateLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PromiseWrapper.reject('Load failed');
|
return PromiseWrapper.reject('Load failed', null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ class FakeXHR extends XHR {
|
||||||
get(url: string): Promise<string> {
|
get(url: string): Promise<string> {
|
||||||
var response = MapWrapper.get(this._responses, url);
|
var response = MapWrapper.get(this._responses, url);
|
||||||
if (isBlank(response)) {
|
if (isBlank(response)) {
|
||||||
return PromiseWrapper.reject('xhr error');
|
return PromiseWrapper.reject('xhr error', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PromiseWrapper.resolve(response);
|
return PromiseWrapper.resolve(response);
|
||||||
|
|
|
@ -223,7 +223,7 @@ class FakeXHR extends XHR {
|
||||||
get(url: string): Promise<string> {
|
get(url: string): Promise<string> {
|
||||||
var response = MapWrapper.get(this._responses, url);
|
var response = MapWrapper.get(this._responses, url);
|
||||||
if (isBlank(response)) {
|
if (isBlank(response)) {
|
||||||
return PromiseWrapper.reject('xhr error');
|
return PromiseWrapper.reject('xhr error', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PromiseWrapper.resolve(response);
|
return PromiseWrapper.resolve(response);
|
||||||
|
|
|
@ -76,8 +76,8 @@ module.exports = function(gulp, plugins, config) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(yjbanov): fix ng2 code and remove the check below
|
// TODO: https://github.com/angular/ts2dart/issues/168
|
||||||
if (line.match(/_stack/)) {
|
if (line.match(/_stack' is not used/)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue