perf: Don’t subclass Error; resulting in smaller binary (#14160)

Subclassing errors is problematic since Error returns a
new instance. All of the patching which we do than prevent
proper application of source maps.

PR Close #14160
This commit is contained in:
Miško Hevery
2017-01-27 13:19:00 -08:00
committed by Miško Hevery
parent 3c2842be96
commit c33fda2607
51 changed files with 407 additions and 500 deletions

View File

@ -20,7 +20,7 @@ import * as constants from './change_detection/constants';
import * as console from './console';
import * as debug from './debug/debug_renderer';
import * as reflective_provider from './di/reflective_provider';
import {ComponentStillLoadingError} from './linker/compiler';
import {ERROR_COMPONENT_TYPE} from './errors';
import * as component_factory from './linker/component_factory';
import * as component_factory_resolver from './linker/component_factory_resolver';
import * as debug_context from './linker/debug_context';
@ -104,12 +104,11 @@ export const __core_private__: {
DEFAULT_STATE: typeof DEFAULT_STATE_,
EMPTY_STATE: typeof EMPTY_STATE_,
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_,
_ComponentStillLoadingError?: ComponentStillLoadingError,
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise,
isObservable: typeof isObservable,
AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils,
ERROR_COMPONENT_TYPE: typeof ERROR_COMPONENT_TYPE,
} = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus,
@ -156,8 +155,8 @@ export const __core_private__: {
DEFAULT_STATE: DEFAULT_STATE_,
EMPTY_STATE: EMPTY_STATE_,
FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
ComponentStillLoadingError: ComponentStillLoadingError,
isPromise: isPromise,
isObservable: isObservable,
AnimationTransition: AnimationTransition
AnimationTransition: AnimationTransition,
ERROR_COMPONENT_TYPE: ERROR_COMPONENT_TYPE
};

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {BaseError, WrappedError} from '../facade/errors';
import {wrappedError} from '../error_handler';
import {ERROR_ORIGINAL_ERROR, getOriginalError} from '../errors';
import {stringify} from '../facade/lang';
import {Type} from '../type';
@ -35,38 +36,31 @@ function constructResolvingPath(keys: any[]): string {
return '';
}
/**
* Base class for all errors arising from misconfigured providers.
* @stable
*/
export class AbstractProviderError extends BaseError {
/** @internal */
message: string;
/** @internal */
export interface InjectionError extends Error {
keys: ReflectiveKey[];
/** @internal */
injectors: ReflectiveInjector[];
constructResolvingMessage: (this: InjectionError) => string;
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void;
}
/** @internal */
constructResolvingMessage: Function;
function injectionError(
injector: ReflectiveInjector, key: ReflectiveKey,
constructResolvingMessage: (this: InjectionError) => string,
originalError?: Error): InjectionError {
const error = (originalError ? wrappedError('', originalError) : Error()) as InjectionError;
error.addKey = addKey;
error.keys = [key];
error.injectors = [injector];
error.constructResolvingMessage = constructResolvingMessage;
error.message = error.constructResolvingMessage();
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
return error;
}
constructor(
injector: ReflectiveInjector, key: ReflectiveKey, constructResolvingMessage: Function) {
super('DI Error');
this.keys = [key];
this.injectors = [injector];
this.constructResolvingMessage = constructResolvingMessage;
this.message = this.constructResolvingMessage(this.keys);
}
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector);
this.keys.push(key);
this.message = this.constructResolvingMessage(this.keys);
}
function addKey(this: InjectionError, injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector);
this.keys.push(key);
this.message = this.constructResolvingMessage();
}
/**
@ -82,15 +76,12 @@ export class AbstractProviderError extends BaseError {
*
* expect(() => Injector.resolveAndCreate([A])).toThrowError();
* ```
* @stable
*/
export class NoProviderError extends AbstractProviderError {
constructor(injector: ReflectiveInjector, key: ReflectiveKey) {
super(injector, key, function(keys: any[]) {
const first = stringify(keys[0].token);
return `No provider for ${first}!${constructResolvingPath(keys)}`;
});
}
export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
return injectionError(injector, key, function(this: InjectionError) {
const first = stringify(this.keys[0].token);
return `No provider for ${first}!${constructResolvingPath(this.keys)}`;
});
}
/**
@ -108,14 +99,12 @@ export class NoProviderError extends AbstractProviderError {
* ```
*
* Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
* @stable
*/
export class CyclicDependencyError extends AbstractProviderError {
constructor(injector: ReflectiveInjector, key: ReflectiveKey) {
super(injector, key, function(keys: any[]) {
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
});
}
export function cyclicDependencyError(
injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
return injectionError(injector, key, function(this: InjectionError) {
return `Cannot instantiate cyclic dependency!${constructResolvingPath(this.keys)}`;
});
}
/**
@ -143,34 +132,14 @@ export class CyclicDependencyError extends AbstractProviderError {
* expect(e.originalStack).toBeDefined();
* }
* ```
* @stable
*/
export class InstantiationError extends WrappedError {
/** @internal */
keys: ReflectiveKey[];
/** @internal */
injectors: ReflectiveInjector[];
constructor(
injector: ReflectiveInjector, originalException: any, originalStack: any,
key: ReflectiveKey) {
super('DI Error', originalException);
this.keys = [key];
this.injectors = [injector];
}
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector);
this.keys.push(key);
}
get message(): string {
export function instantiationError(
injector: ReflectiveInjector, originalException: any, originalStack: any,
key: ReflectiveKey): InjectionError {
return injectionError(injector, key, function(this: InjectionError) {
const first = stringify(this.keys[0].token);
return `${this.originalError.message}: Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
}
get causeKey(): ReflectiveKey { return this.keys[0]; }
return `${getOriginalError(this).message}: Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
}, originalException);
}
/**
@ -182,12 +151,10 @@ export class InstantiationError extends WrappedError {
* ```typescript
* expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
* ```
* @stable
*/
export class InvalidProviderError extends BaseError {
constructor(provider: any) {
super(`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
}
export function invalidProviderError(provider: any) {
return Error(
`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
}
/**
@ -219,26 +186,21 @@ export class InvalidProviderError extends BaseError {
* ```
* @stable
*/
export class NoAnnotationError extends BaseError {
constructor(typeOrFunc: Type<any>|Function, params: any[][]) {
super(NoAnnotationError._genMessage(typeOrFunc, params));
}
private static _genMessage(typeOrFunc: Type<any>|Function, params: any[][]) {
const signature: string[] = [];
for (let i = 0, ii = params.length; i < ii; i++) {
const parameter = params[i];
if (!parameter || parameter.length == 0) {
signature.push('?');
} else {
signature.push(parameter.map(stringify).join(' '));
}
export function noAnnotationError(typeOrFunc: Type<any>| Function, params: any[][]): Error {
const signature: string[] = [];
for (let i = 0, ii = params.length; i < ii; i++) {
const parameter = params[i];
if (!parameter || parameter.length == 0) {
signature.push('?');
} else {
signature.push(parameter.map(stringify).join(' '));
}
return 'Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' +
signature.join(', ') + '). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
stringify(typeOrFunc) + '\' is decorated with Injectable.';
}
return Error(
'Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' +
signature.join(', ') + '). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
stringify(typeOrFunc) + '\' is decorated with Injectable.');
}
/**
@ -255,8 +217,8 @@ export class NoAnnotationError extends BaseError {
* ```
* @stable
*/
export class OutOfBoundsError extends BaseError {
constructor(index: number) { super(`Index ${index} is out-of-bounds.`); }
export function outOfBoundsError(index: number) {
return Error(`Index ${index} is out-of-bounds.`);
}
// TODO: add a working example after alpha38 is released
@ -272,10 +234,7 @@ export class OutOfBoundsError extends BaseError {
* ])).toThrowError();
* ```
*/
export class MixingMultiProvidersWithRegularProvidersError extends BaseError {
constructor(provider1: any, provider2: any) {
super(
'Cannot mix multi providers and regular providers, got: ' + provider1.toString() + ' ' +
provider2.toString());
}
export function mixingMultiProvidersWithRegularProvidersError(
provider1: any, provider2: any): Error {
return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`);
}

View File

@ -9,7 +9,7 @@
import {Injector, THROW_IF_NOT_FOUND} from './injector';
import {Self, SkipSelf} from './metadata';
import {Provider} from './provider';
import {AbstractProviderError, CyclicDependencyError, InstantiationError, NoProviderError, OutOfBoundsError} from './reflective_errors';
import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key';
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
@ -331,7 +331,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index < 0 || index >= this._providers.length) {
throw new OutOfBoundsError(index);
throw outOfBoundsError(index);
}
return this._providers[index];
}
@ -339,7 +339,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
/** @internal */
_new(provider: ResolvedReflectiveProvider): any {
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
throw new CyclicDependencyError(this, provider.key);
throw cyclicDependencyError(this, provider.key);
}
return this._instantiateProvider(provider);
}
@ -368,7 +368,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
deps =
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
} catch (e) {
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
if (e.addKey) {
e.addKey(this, provider.key);
}
throw e;
@ -378,7 +378,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
try {
obj = factory(...deps);
} catch (e) {
throw new InstantiationError(this, e, e.stack, provider.key);
throw instantiationError(this, e, e.stack, provider.key);
}
return obj;
@ -420,7 +420,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
if (notFoundValue !== THROW_IF_NOT_FOUND) {
return notFoundValue;
} else {
throw new NoProviderError(this, key);
throw noProviderError(this, key);
}
}

View File

@ -12,7 +12,7 @@ import {Type} from '../type';
import {resolveForwardRef} from './forward_ref';
import {Host, Inject, Optional, Self, SkipSelf} from './metadata';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider';
import {InvalidProviderError, MixingMultiProvidersWithRegularProvidersError, NoAnnotationError} from './reflective_errors';
import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key';
@ -154,7 +154,7 @@ export function mergeResolvedReflectiveProviders(
const existing = normalizedProvidersMap.get(provider.key.id);
if (existing) {
if (provider.multiProvider !== existing.multiProvider) {
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
}
if (provider.multiProvider) {
for (let j = 0; j < provider.resolvedFactories.length; j++) {
@ -189,7 +189,7 @@ function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[]
_normalizeProviders(b, res);
} else {
throw new InvalidProviderError(b);
throw invalidProviderError(b);
}
});
@ -211,7 +211,7 @@ function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
if (!params) return [];
if (params.some(p => p == null)) {
throw new NoAnnotationError(typeOrFunc, params);
throw noAnnotationError(typeOrFunc, params);
}
return params.map(p => _extractToken(typeOrFunc, p, params));
}
@ -253,7 +253,7 @@ function _extractToken(
if (token != null) {
return _createDependency(token, optional, visibility);
} else {
throw new NoAnnotationError(typeOrFunc, params);
throw noAnnotationError(typeOrFunc, params);
}
}

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {WrappedError} from './facade/errors';
import {ERROR_ORIGINAL_ERROR, getDebugContext, getOriginalError} from './errors';
/**
* @whatItDoes Provides a hook for centralized exception handling.
@ -48,24 +49,26 @@ export class ErrorHandler {
constructor(rethrowError: boolean = true) { this.rethrowError = rethrowError; }
handleError(error: any): void {
const originalError = this._findOriginalError(error);
const originalStack = this._findOriginalStack(error);
const context = this._findContext(error);
this._console.error(`EXCEPTION: ${this._extractMessage(error)}`);
if (originalError) {
this._console.error(`ORIGINAL EXCEPTION: ${this._extractMessage(originalError)}`);
}
if (error instanceof Error) {
const originalError = this._findOriginalError(error);
const originalStack = this._findOriginalStack(error);
const context = this._findContext(error);
if (originalStack) {
this._console.error('ORIGINAL STACKTRACE:');
this._console.error(originalStack);
}
if (originalError) {
this._console.error(`ORIGINAL EXCEPTION: ${this._extractMessage(originalError)}`);
}
if (context) {
this._console.error('ERROR CONTEXT:');
this._console.error(context);
if (originalStack) {
this._console.error('ORIGINAL STACKTRACE:');
this._console.error(originalStack);
}
if (context) {
this._console.error('ERROR CONTEXT:');
this._console.error(context);
}
}
// We rethrow exceptions, so operations like 'bootstrap' will result in an error
@ -81,31 +84,29 @@ export class ErrorHandler {
/** @internal */
_findContext(error: any): any {
if (error) {
return error.context ? error.context :
this._findContext((error as WrappedError).originalError);
return getDebugContext(error) ? getDebugContext(error) :
this._findContext(getOriginalError(error));
}
return null;
}
/** @internal */
_findOriginalError(error: any): any {
let e = (error as WrappedError).originalError;
while (e && (e as WrappedError).originalError) {
e = (e as WrappedError).originalError;
_findOriginalError(error: Error): any {
let e = getOriginalError(error);
while (e && getOriginalError(e)) {
e = getOriginalError(e);
}
return e;
}
/** @internal */
_findOriginalStack(error: any): string {
if (!(error instanceof Error)) return null;
_findOriginalStack(error: Error): string {
let e: any = error;
let stack: string = e.stack;
while (e instanceof Error && (e as WrappedError).originalError) {
e = (e as WrappedError).originalError;
while (e instanceof Error && getOriginalError(e)) {
e = getOriginalError(e);
if (e instanceof Error && e.stack) {
stack = e.stack;
}
@ -114,3 +115,11 @@ export class ErrorHandler {
return stack;
}
}
export function wrappedError(message: string, originalError: any): Error {
const msg =
`${message} caused by: ${originalError instanceof Error ? originalError.message: originalError }`;
const error = Error(msg);
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
return error;
}

View File

@ -0,0 +1,28 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from './type';
import {DebugContext} from './view';
export const ERROR_TYPE = 'ngType';
export const ERROR_COMPONENT_TYPE = 'ngComponentType';
export const ERROR_DEBUG_CONTEXT = 'ngDebugContext';
export const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
export function getType(error: Error): Function {
return (error as any)[ERROR_TYPE];
}
export function getDebugContext(error: Error): DebugContext {
return (error as any)[ERROR_DEBUG_CONTEXT];
}
export function getOriginalError(error: Error): Error {
return (error as any)[ERROR_ORIGINAL_ERROR];
}

View File

@ -7,7 +7,6 @@
*/
import {Injectable, InjectionToken} from '../di';
import {BaseError} from '../facade/errors';
import {stringify} from '../facade/lang';
import {MissingTranslationStrategy} from '../i18n/tokens';
import {ViewEncapsulation} from '../metadata';
@ -16,19 +15,6 @@ import {Type} from '../type';
import {ComponentFactory} from './component_factory';
import {NgModuleFactory} from './ng_module_factory';
/**
* Indicates that a component is still being loaded in a synchronous compile.
*
* @stable
*/
export class ComponentStillLoadingError extends BaseError {
constructor(public compType: Type<any>) {
super(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`);
}
}
/**
* Combination of NgModuleFactory and ComponentFactorys.
*
@ -59,8 +45,7 @@ function _throwError() {
export class Compiler {
/**
* Compiles the given NgModule and all of its components. All templates of the components listed
* in `entryComponents`
* have to be inlined. Otherwise throws a {@link ComponentStillLoadingError}.
* in `entryComponents` have to be inlined.
*/
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> { throw _throwError(); }

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {BaseError} from '../facade/errors';
import {stringify} from '../facade/lang';
import {Type} from '../type';
@ -14,19 +13,23 @@ import {ComponentFactory} from './component_factory';
/**
* @stable
*/
export class NoComponentFactoryError extends BaseError {
constructor(public component: Function) {
super(
`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
}
export function noComponentFactoryError(component: Function) {
const error = Error(
`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
(error as any)[ERROR_COMPONENT] = component;
return error;
}
const ERROR_COMPONENT = 'ngComponent';
export function getComponent(error: Error): Type<any> {
return (error as any)[ERROR_COMPONENT];
}
class _NullComponentFactoryResolver implements ComponentFactoryResolver {
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
throw new NoComponentFactoryError(component);
throw noComponentFactoryError(component);
}
}

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {BaseError, WrappedError} from '../facade/errors';
import {wrappedError} from '../error_handler';
import {ERROR_DEBUG_CONTEXT, ERROR_TYPE} from '../errors';
import {DebugContext} from './debug_context';
@ -41,19 +41,19 @@ import {DebugContext} from './debug_context';
* }
* }
* ```
* @stable
*/
export class ExpressionChangedAfterItHasBeenCheckedError extends BaseError {
constructor(oldValue: any, currValue: any, isFirstCheck: boolean) {
let msg =
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
if (isFirstCheck) {
msg +=
` It seems like the view has been created after its parent and its children have been dirty checked.` +
` Has it been created in a change detection hook ?`;
}
super(msg);
export function expressionChangedAfterItHasBeenCheckedError(
oldValue: any, currValue: any, isFirstCheck: boolean) {
let msg =
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
if (isFirstCheck) {
msg +=
` It seems like the view has been created after its parent and its children have been dirty checked.` +
` Has it been created in a change detection hook ?`;
}
const error = Error(msg);
(error as any)[ERROR_TYPE] = expressionChangedAfterItHasBeenCheckedError;
return error;
}
/**
@ -61,28 +61,21 @@ export class ExpressionChangedAfterItHasBeenCheckedError extends BaseError {
*
* This error wraps the original exception to attach additional contextual information that can
* be useful for debugging.
* @stable
*/
export class ViewWrappedError extends WrappedError {
/**
* DebugContext
*/
context: DebugContext;
constructor(originalError: any, context: DebugContext) {
super(`Error in ${context.source}`, originalError);
this.context = context;
}
export function viewWrappedError(originalError: any, context: DebugContext): Error {
const error = wrappedError(`Error in ${context.source}`, originalError);
(error as any)[ERROR_DEBUG_CONTEXT] = context;
(error as any)[ERROR_TYPE] = viewWrappedError;
return error;
}
/**
* Thrown when a destroyed view is used.
*
* This error indicates a bug in the framework.
*
* This is an internal Angular error.
* @stable
*/
export class ViewDestroyedError extends BaseError {
constructor(details: string) { super(`Attempt to use a destroyed view: ${details}`); }
* Thrown when a destroyed view is used.
*
* This error indicates a bug in the framework.
*
* This is an internal Angular error.
*/
export function viewDestroyedError(details: string) {
return Error(`Attempt to use a destroyed view: ${details}`);
}

View File

@ -6,9 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ApplicationRef} from '../application_ref';
import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/change_detection';
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {getType} from '../errors';
import {isPresent} from '../facade/lang';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
import {DirectRenderer, RenderComponentType, Renderer} from '../render/api';
@ -17,7 +19,7 @@ import {AnimationViewContext} from './animation_view_context';
import {ComponentRef} from './component_factory';
import {DebugContext, StaticNodeDebugInfo} from './debug_context';
import {ElementInjector} from './element_injector';
import {ExpressionChangedAfterItHasBeenCheckedError, ViewDestroyedError, ViewWrappedError} from './errors';
import {expressionChangedAfterItHasBeenCheckedError, viewDestroyedError, viewWrappedError} from './errors';
import {ViewContainer} from './view_container';
import {ViewRef_} from './view_ref';
import {ViewType} from './view_type';
@ -360,7 +362,7 @@ export abstract class AppView<T> {
return cb;
}
throwDestroyedError(details: string): void { throw new ViewDestroyedError(details); }
throwDestroyedError(details: string): void { throw viewDestroyedError(details); }
}
export class DebugAppView<T> extends AppView<T> {
@ -445,12 +447,12 @@ export class DebugAppView<T> extends AppView<T> {
}
private _rethrowWithContext(e: any) {
if (!(e instanceof ViewWrappedError)) {
if (!(e instanceof ExpressionChangedAfterItHasBeenCheckedError)) {
if (!(getType(e) == viewWrappedError)) {
if (!(getType(e) == expressionChangedAfterItHasBeenCheckedError)) {
this.cdMode = ChangeDetectorStatus.Errored;
}
if (isPresent(this._currentDebugContext)) {
throw new ViewWrappedError(e, this._currentDebugContext);
throw viewWrappedError(e, this._currentDebugContext);
}
}
}

View File

@ -17,7 +17,7 @@ import {Type} from '../type';
import {VERSION} from '../version';
import {ComponentFactory} from './component_factory';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {AppView} from './view';
@Injectable()
@ -104,7 +104,7 @@ export function checkBinding(
const isFirstCheck = view.numberOfChecks === 0;
if (view.throwOnChange) {
if (isFirstCheck || !devModeEqual(oldValue, newValue)) {
throw new ExpressionChangedAfterItHasBeenCheckedError(oldValue, newValue, isFirstCheck);
throw expressionChangedAfterItHasBeenCheckedError(oldValue, newValue, isFirstCheck);
}
return false;
} else {

View File

@ -6,12 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {BaseError, WrappedError} from '../facade/errors';
import {ERROR_DEBUG_CONTEXT, ERROR_ORIGINAL_ERROR, getDebugContext} from '../errors';
import {DebugContext, EntryAction, ViewState} from './types';
export function expressionChangedAfterItHasBeenCheckedError(
context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): ViewDebugError {
context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): Error {
let msg =
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
if (isFirstCheck) {
@ -22,25 +21,22 @@ export function expressionChangedAfterItHasBeenCheckedError(
return viewDebugError(msg, context);
}
export function viewWrappedDebugError(originalError: any, context: DebugContext): WrappedError&
ViewDebugError {
const err = viewDebugError(originalError.message, context) as WrappedError & ViewDebugError;
err.originalError = originalError;
export function viewWrappedDebugError(originalError: any, context: DebugContext): Error {
const err = viewDebugError(originalError.message, context);
(err as any)[ERROR_ORIGINAL_ERROR] = originalError;
return err;
}
export interface ViewDebugError { context: DebugContext; }
export function viewDebugError(msg: string, context: DebugContext): ViewDebugError {
const err = new Error(msg) as any;
err.context = context;
export function viewDebugError(msg: string, context: DebugContext): Error {
const err = new Error(msg);
(err as any)[ERROR_DEBUG_CONTEXT] = context;
err.stack = context.source;
context.view.state |= ViewState.Errored;
return err;
}
export function isViewDebugError(err: any): boolean {
return err.context;
export function isViewDebugError(err: Error): boolean {
return !!getDebugContext(err);
}
export function viewDestroyedError(action: EntryAction): Error {

View File

@ -6,9 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isDevMode} from '../application_ref';
import {RenderComponentType, Renderer} from '../render/api';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {appendNgContent} from './ng_content';

View File

@ -9,6 +9,7 @@
import {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core';
import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector';
import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {getOriginalError} from '@angular/core/src/errors';
import {expect} from '@angular/platform-browser/testing/matchers';
import {isPresent, stringify} from '../../src/facade/lang';
@ -293,8 +294,8 @@ export function main() {
} catch (e) {
expect(e.message).toContain(
`Error during instantiation of Engine! (${stringify(Car)} -> Engine)`);
expect(e.originalError instanceof Error).toBeTruthy();
expect(e.causeKey.token).toEqual(Engine);
expect(getOriginalError(e) instanceof Error).toBeTruthy();
expect(e.keys[0].token).toEqual(Engine);
}
});

View File

@ -6,22 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/
import {WrappedError} from '@angular/core/src/facade/errors';
import {ERROR_DEBUG_CONTEXT} from '@angular/core/src/errors';
import {DebugContext} from '@angular/core/src/linker/debug_context';
import {ViewWrappedError} from '@angular/core/src/linker/errors';
import {viewWrappedError} from '@angular/core/src/linker/errors';
import {ErrorHandler} from '../src/error_handler';
import {ErrorHandler, wrappedError} from '../src/error_handler';
class MockConsole {
res: any[] = [];
error(s: any): void { this.res.push(s); }
}
class _CustomException {
context = 'some context';
toString(): string { return 'custom'; }
}
export function main() {
function errorToString(error: any) {
const logger = new MockConsole();
@ -58,12 +53,9 @@ export function main() {
it('should print nested context', () => {
const cause = new Error('message!');
const stack = getStack(cause);
const context = {
source: 'context!',
toString() { return 'Context'; }
} as any as DebugContext;
const original = new ViewWrappedError(cause, context);
const e = errorToString(new WrappedError('message', original));
const context = { source: 'context!', toString() { return 'Context'; } } as any;
const original = viewWrappedError(cause, context);
const e = errorToString(wrappedError('message', original));
expect(e).toEqual(
stack ? `EXCEPTION: message caused by: Error in context! caused by: message!
ORIGINAL EXCEPTION: message!
@ -81,15 +73,15 @@ Context`);
describe('original exception', () => {
it('should print original exception message if available (original is Error)', () => {
const realOriginal = new Error('inner');
const original = new WrappedError('wrapped', realOriginal);
const e = errorToString(new WrappedError('wrappedwrapped', original));
const original = wrappedError('wrapped', realOriginal);
const e = errorToString(wrappedError('wrappedwrapped', original));
expect(e).toContain('inner');
});
it('should print original exception message if available (original is not Error)', () => {
const realOriginal = new _CustomException();
const original = new WrappedError('wrapped', realOriginal);
const e = errorToString(new WrappedError('wrappedwrapped', original));
const realOriginal = new Error('custom');
const original = wrappedError('wrapped', realOriginal);
const e = errorToString(wrappedError('wrappedwrapped', original));
expect(e).toContain('custom');
});
});
@ -99,8 +91,8 @@ Context`);
const realOriginal = new Error('inner');
const stack = getStack(realOriginal);
if (stack) {
const original = new WrappedError('wrapped', realOriginal);
const e = errorToString(new WrappedError('wrappedwrapped', original));
const original = wrappedError('wrapped', realOriginal);
const e = errorToString(wrappedError('wrappedwrapped', original));
expect(e).toContain(stack);
}
});

View File

@ -7,7 +7,7 @@
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver} from '@angular/core';
import {NoComponentFactoryError} from '@angular/core/src/linker/component_factory_resolver';
import {noComponentFactoryError} from '@angular/core/src/linker/component_factory_resolver';
import {TestBed} from '@angular/core/testing';
import {Console} from '../../src/console';
@ -77,7 +77,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp);
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
.toThrow(new NoComponentFactoryError(NestedChildComp));
.toThrow(noComponentFactoryError(NestedChildComp));
});
});

View File

@ -9,6 +9,7 @@
import {CommonModule} from '@angular/common';
import {ComponentFactory, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
import {getDebugContext} from '@angular/core/src/errors';
import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver';
import {ElementRef} from '@angular/core/src/linker/element_ref';
import {QueryList} from '@angular/core/src/linker/query_list';
@ -1295,7 +1296,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.createComponent(MyComp);
throw 'Should throw';
} catch (e) {
const c = e.context;
const c = getDebugContext(e);
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
expect((<Injector>c.injector).get).toBeTruthy();
}
@ -1310,7 +1311,7 @@ function declareTests({useJit}: {useJit: boolean}) {
fixture.detectChanges();
throw 'Should throw';
} catch (e) {
const c = e.context;
const c = getDebugContext(e);
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('INPUT');
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
expect((<Injector>c.injector).get).toBeTruthy();
@ -1330,7 +1331,7 @@ function declareTests({useJit}: {useJit: boolean}) {
fixture.detectChanges();
throw 'Should throw';
} catch (e) {
const c = e.context;
const c = getDebugContext(e);
expect(c.renderNode).toBeTruthy();
expect(c.source).toContain(':0:5');
}
@ -1353,7 +1354,7 @@ function declareTests({useJit}: {useJit: boolean}) {
try {
tc.injector.get(DirectiveEmittingEvent).fireEvent('boom');
} catch (e) {
const c = e.context;
const c = getDebugContext(e);
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('SPAN');
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
expect((<Injector>c.injector).get).toBeTruthy();

View File

@ -7,6 +7,7 @@
*/
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -363,7 +364,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
}
expect(err).toBeTruthy();
expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(0);
});

View File

@ -7,6 +7,7 @@
*/
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, providerDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -135,7 +136,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
}
expect(err).toBeTruthy();
expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBeTruthy();
// errors should point to the already existing element
expect(debugCtx.nodeIndex).toBe(0);
@ -447,7 +448,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
err = e;
}
expect(err).toBeTruthy();
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
// events are emitted with the index of the element, not the index of the provider.
expect(debugCtx.nodeIndex).toBe(0);
@ -576,7 +577,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
}
expect(err).toBeTruthy();
expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(1);
});
@ -599,7 +600,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
}
expect(err).toBeTruthy();
expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(1);
});

View File

@ -7,6 +7,7 @@
*/
import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -395,7 +396,7 @@ export function main() {
expect(err.message)
.toBe(
`Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(2);
});
@ -421,10 +422,10 @@ export function main() {
}
expect(err).toBeTruthy();
expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context;
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(2);
});
});
});
}
}

View File

@ -10,7 +10,6 @@ import {NgZone} from '@angular/core/src/zone/ng_zone';
import {async, fakeAsync, flushMicrotasks} from '@angular/core/testing';
import {AsyncTestCompleter, Log, beforeEach, describe, expect, inject, it, xit} from '@angular/core/testing/testing_internal';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {BaseError} from '../../src/facade/errors';
import {isPresent, scheduleMicroTask} from '../../src/facade/lang';
const needsLongerTimers = browserDetection.isSlow || browserDetection.isEdge;
@ -94,7 +93,7 @@ export function main() {
setTimeout(() => {
setTimeout(() => {
resolve(null);
throw new BaseError('ccc');
throw new Error('ccc');
}, 0);
}, 0);
});
@ -117,7 +116,7 @@ export function main() {
scheduleMicroTask(() => {
scheduleMicroTask(() => {
resolve(null);
throw new BaseError('ddd');
throw new Error('ddd');
});
});
});
@ -152,7 +151,7 @@ export function main() {
setTimeout(() => {
setTimeout(() => {
resolve(null);
throw new BaseError('ccc');
throw new Error('ccc');
}, 0);
}, 0);
});
@ -719,7 +718,7 @@ function commonTests() {
it('should call the on error callback when it is invoked via zone.runGuarded',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
macroTask(() => {
const exception = new BaseError('sync');
const exception = new Error('sync');
_zone.runGuarded(() => { throw exception; });
@ -732,7 +731,7 @@ function commonTests() {
it('should not call the on error callback but rethrow when it is invoked via zone.run',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
macroTask(() => {
const exception = new BaseError('sync');
const exception = new Error('sync');
expect(() => _zone.run(() => { throw exception; })).toThrowError('sync');
expect(_errors.length).toBe(0);
@ -742,7 +741,7 @@ function commonTests() {
it('should call onError for errors from microtasks',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
const exception = new BaseError('async');
const exception = new Error('async');
macroTask(() => { _zone.run(() => { scheduleMicroTask(() => { throw exception; }); }); });

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type} from '@angular/core';
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, __core_private__} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture';
import {stringify} from './facade/lang';
@ -266,9 +265,9 @@ export class TestBed implements Injector {
this._moduleWithComponentFactories =
this._compiler.compileModuleAndAllComponentsSync(moduleType);
} catch (e) {
if (e.compType) {
if (getComponentType(e)) {
throw new Error(
`This test module uses the component ${stringify(e.compType)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`This test module uses the component ${stringify(getComponentType(e))} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`Please call "TestBed.compileComponents" before your test.`);
} else {
throw e;
@ -471,3 +470,7 @@ export function withModule(moduleDef: TestModuleMetadata, fn: Function = null):
}
return new InjectSetupWrapper(() => moduleDef);
}
function getComponentType(error: Error): Function {
return (error as any)[__core_private__.ERROR_COMPONENT_TYPE];
}

View File

@ -8,9 +8,11 @@
import {Compiler, CompilerOptions, Component, Directive, Injector, NgModule, Pipe, Type} from '@angular/core';
import {unimplemented} from './facade/errors';
import {MetadataOverride} from './metadata_override';
function unimplemented(): any {
throw Error('unimplemented');
}
/**
* Special interface to the compiler only used by testing