fix(perf): don’t use `try/catch` in production mode
The previous code that had `try/catch` statements in methods could not be optimized by Chrome. This change separates `AppView` (no `try/catch`) form `DebugAppView` (always `try/catch`). Our codegen will use `AppView` in production mode and `DebugAppView` in debug mode. Closes #8338
This commit is contained in:
parent
5297c9d9cc
commit
b1a9e445b3
|
@ -1,5 +1,5 @@
|
||||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||||
import {AppView} from 'angular2/src/core/linker/view';
|
import {AppView, DebugAppView} from 'angular2/src/core/linker/view';
|
||||||
import {StaticNodeDebugInfo, DebugContext} from 'angular2/src/core/linker/debug_context';
|
import {StaticNodeDebugInfo, DebugContext} from 'angular2/src/core/linker/debug_context';
|
||||||
import {
|
import {
|
||||||
ViewUtils,
|
ViewUtils,
|
||||||
|
@ -47,6 +47,7 @@ var CD_MODULE_URL = 'asset:angular2/lib/src/core/change_detection/change_detecti
|
||||||
// (only needed for Dart).
|
// (only needed for Dart).
|
||||||
var impViewUtils = ViewUtils;
|
var impViewUtils = ViewUtils;
|
||||||
var impAppView = AppView;
|
var impAppView = AppView;
|
||||||
|
var impDebugAppView = DebugAppView;
|
||||||
var impDebugContext = DebugContext;
|
var impDebugContext = DebugContext;
|
||||||
var impAppElement = AppElement;
|
var impAppElement = AppElement;
|
||||||
var impElementRef = ElementRef;
|
var impElementRef = ElementRef;
|
||||||
|
@ -80,6 +81,8 @@ export class Identifiers {
|
||||||
});
|
});
|
||||||
static AppView = new CompileIdentifierMetadata(
|
static AppView = new CompileIdentifierMetadata(
|
||||||
{name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: impAppView});
|
{name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: impAppView});
|
||||||
|
static DebugAppView = new CompileIdentifierMetadata(
|
||||||
|
{name: 'DebugAppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: impDebugAppView});
|
||||||
static AppElement = new CompileIdentifierMetadata({
|
static AppElement = new CompileIdentifierMetadata({
|
||||||
name: 'AppElement',
|
name: 'AppElement',
|
||||||
moduleUrl: 'asset:angular2/lib/src/core/linker/element' + MODULE_SUFFIX,
|
moduleUrl: 'asset:angular2/lib/src/core/linker/element' + MODULE_SUFFIX,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {AppView} from 'angular2/src/core/linker/view';
|
import {AppView, DebugAppView} from 'angular2/src/core/linker/view';
|
||||||
import {AppElement} from 'angular2/src/core/linker/element';
|
import {AppElement} from 'angular2/src/core/linker/element';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {InstanceFactory, DynamicInstance} from './output_interpreter';
|
import {InstanceFactory, DynamicInstance} from './output_interpreter';
|
||||||
|
@ -8,13 +8,19 @@ export class InterpretiveAppViewInstanceFactory implements InstanceFactory {
|
||||||
createInstance(superClass: any, clazz: any, args: any[], props: Map<string, any>,
|
createInstance(superClass: any, clazz: any, args: any[], props: Map<string, any>,
|
||||||
getters: Map<string, Function>, methods: Map<string, Function>): any {
|
getters: Map<string, Function>, methods: Map<string, Function>): any {
|
||||||
if (superClass === AppView) {
|
if (superClass === AppView) {
|
||||||
|
// We are always using DebugAppView as parent.
|
||||||
|
// However, in prod mode we generate a constructor call that does
|
||||||
|
// not have the argument for the debugNodeInfos.
|
||||||
|
args = args.concat([null]);
|
||||||
|
return new _InterpretiveAppView(args, props, getters, methods);
|
||||||
|
} else if (superClass === DebugAppView) {
|
||||||
return new _InterpretiveAppView(args, props, getters, methods);
|
return new _InterpretiveAppView(args, props, getters, methods);
|
||||||
}
|
}
|
||||||
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
|
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InterpretiveAppView extends AppView<any> implements DynamicInstance {
|
class _InterpretiveAppView extends DebugAppView<any> implements DynamicInstance {
|
||||||
constructor(args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
|
constructor(args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
|
||||||
public methods: Map<string, Function>) {
|
public methods: Map<string, Function>) {
|
||||||
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
||||||
|
|
|
@ -398,19 +398,20 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
|
||||||
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
||||||
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
||||||
];
|
];
|
||||||
var viewConstructor = new o.ClassMethod(null, viewConstructorArgs, [
|
var superConstructorArgs = [
|
||||||
o.SUPER_EXPR.callFn([
|
|
||||||
o.variable(view.className),
|
o.variable(view.className),
|
||||||
renderCompTypeVar,
|
renderCompTypeVar,
|
||||||
ViewTypeEnum.fromValue(view.viewType),
|
ViewTypeEnum.fromValue(view.viewType),
|
||||||
ViewConstructorVars.viewUtils,
|
ViewConstructorVars.viewUtils,
|
||||||
ViewConstructorVars.parentInjector,
|
ViewConstructorVars.parentInjector,
|
||||||
ViewConstructorVars.declarationEl,
|
ViewConstructorVars.declarationEl,
|
||||||
ChangeDetectionStrategyEnum.fromValue(getChangeDetectionMode(view)),
|
ChangeDetectionStrategyEnum.fromValue(getChangeDetectionMode(view))
|
||||||
nodeDebugInfosVar
|
];
|
||||||
])
|
if (view.genConfig.genDebugInfo) {
|
||||||
.toStmt()
|
superConstructorArgs.push(nodeDebugInfosVar);
|
||||||
]);
|
}
|
||||||
|
var viewConstructor = new o.ClassMethod(null, viewConstructorArgs,
|
||||||
|
[o.SUPER_EXPR.callFn(superConstructorArgs).toStmt()]);
|
||||||
|
|
||||||
var viewMethods = [
|
var viewMethods = [
|
||||||
new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||||||
|
@ -431,9 +432,10 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
|
||||||
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
||||||
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
|
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
|
||||||
].concat(view.eventHandlerMethods);
|
].concat(view.eventHandlerMethods);
|
||||||
var viewClass = new o.ClassStmt(
|
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
|
||||||
view.className, o.importExpr(Identifiers.AppView, [getContextType(view)]), view.fields,
|
var viewClass = new o.ClassStmt(view.className, o.importExpr(superClass, [getContextType(view)]),
|
||||||
view.getters, viewConstructor, viewMethods.filter((method) => method.body.length > 0));
|
view.fields, view.getters, viewConstructor,
|
||||||
|
viewMethods.filter((method) => method.body.length > 0));
|
||||||
return viewClass;
|
return viewClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {isPresent, isBlank, CONST} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Injector} from 'angular2/src/core/di';
|
import {Injector} from 'angular2/src/core/di';
|
||||||
import {RenderDebugInfo} from 'angular2/src/core/render/api';
|
import {RenderDebugInfo} from 'angular2/src/core/render/api';
|
||||||
import {AppView} from './view';
|
import {DebugAppView} from './view';
|
||||||
import {ViewType} from './view_type';
|
import {ViewType} from './view_type';
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
|
@ -12,7 +12,7 @@ export class StaticNodeDebugInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DebugContext implements RenderDebugInfo {
|
export class DebugContext implements RenderDebugInfo {
|
||||||
constructor(private _view: AppView<any>, private _nodeIndex: number, private _tplRow: number,
|
constructor(private _view: DebugAppView<any>, private _nodeIndex: number, private _tplRow: number,
|
||||||
private _tplCol: number) {}
|
private _tplCol: number) {}
|
||||||
|
|
||||||
private get _staticNodeInfo(): StaticNodeDebugInfo {
|
private get _staticNodeInfo(): StaticNodeDebugInfo {
|
||||||
|
@ -31,7 +31,7 @@ export class DebugContext implements RenderDebugInfo {
|
||||||
var componentView = this._view;
|
var componentView = this._view;
|
||||||
while (isPresent(componentView.declarationAppElement) &&
|
while (isPresent(componentView.declarationAppElement) &&
|
||||||
componentView.type !== ViewType.COMPONENT) {
|
componentView.type !== ViewType.COMPONENT) {
|
||||||
componentView = componentView.declarationAppElement.parentView;
|
componentView = <DebugAppView<any>>componentView.declarationAppElement.parentView;
|
||||||
}
|
}
|
||||||
return isPresent(componentView.declarationAppElement) ?
|
return isPresent(componentView.declarationAppElement) ?
|
||||||
componentView.declarationAppElement.nativeElement :
|
componentView.declarationAppElement.nativeElement :
|
||||||
|
|
|
@ -24,7 +24,12 @@ import {
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {Renderer, RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
import {
|
||||||
|
Renderer,
|
||||||
|
RootRenderer,
|
||||||
|
RenderComponentType,
|
||||||
|
RenderDebugInfo
|
||||||
|
} from 'angular2/src/core/render/api';
|
||||||
import {ViewRef_} from './view_ref';
|
import {ViewRef_} from './view_ref';
|
||||||
|
|
||||||
import {ViewType} from './view_type';
|
import {ViewType} from './view_type';
|
||||||
|
@ -78,16 +83,13 @@ export abstract class AppView<T> {
|
||||||
|
|
||||||
renderer: Renderer;
|
renderer: Renderer;
|
||||||
|
|
||||||
private _currentDebugContext: DebugContext = null;
|
|
||||||
|
|
||||||
private _hasExternalHostElement: boolean;
|
private _hasExternalHostElement: boolean;
|
||||||
|
|
||||||
public context: T;
|
public context: T;
|
||||||
|
|
||||||
constructor(public clazz: any, public componentType: RenderComponentType, public type: ViewType,
|
constructor(public clazz: any, public componentType: RenderComponentType, public type: ViewType,
|
||||||
public viewUtils: ViewUtils, public parentInjector: Injector,
|
public viewUtils: ViewUtils, public parentInjector: Injector,
|
||||||
public declarationAppElement: AppElement, public cdMode: ChangeDetectionStrategy,
|
public declarationAppElement: AppElement, public cdMode: ChangeDetectionStrategy) {
|
||||||
public staticNodeDebugInfos: StaticNodeDebugInfo[]) {
|
|
||||||
this.ref = new ViewRef_(this);
|
this.ref = new ViewRef_(this);
|
||||||
if (type === ViewType.COMPONENT || type === ViewType.HOST) {
|
if (type === ViewType.COMPONENT || type === ViewType.HOST) {
|
||||||
this.renderer = viewUtils.renderComponent(componentType);
|
this.renderer = viewUtils.renderComponent(componentType);
|
||||||
|
@ -115,17 +117,7 @@ export abstract class AppView<T> {
|
||||||
}
|
}
|
||||||
this._hasExternalHostElement = isPresent(rootSelectorOrNode);
|
this._hasExternalHostElement = isPresent(rootSelectorOrNode);
|
||||||
this.projectableNodes = projectableNodes;
|
this.projectableNodes = projectableNodes;
|
||||||
if (this.debugMode) {
|
|
||||||
this._resetDebug();
|
|
||||||
try {
|
|
||||||
return this.createInternal(rootSelectorOrNode);
|
return this.createInternal(rootSelectorOrNode);
|
||||||
} catch (e) {
|
|
||||||
this._rethrowWithContext(e, e.stack);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return this.createInternal(rootSelectorOrNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,28 +142,18 @@ export abstract class AppView<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOrCreateHostElement(elementName: string, rootSelectorOrNode: string | any,
|
selectOrCreateHostElement(elementName: string, rootSelectorOrNode: string | any,
|
||||||
debugCtx: DebugContext): any {
|
debugInfo: RenderDebugInfo): any {
|
||||||
var hostElement;
|
var hostElement;
|
||||||
if (isPresent(rootSelectorOrNode)) {
|
if (isPresent(rootSelectorOrNode)) {
|
||||||
hostElement = this.renderer.selectRootElement(rootSelectorOrNode, debugCtx);
|
hostElement = this.renderer.selectRootElement(rootSelectorOrNode, debugInfo);
|
||||||
} else {
|
} else {
|
||||||
hostElement = this.renderer.createElement(null, elementName, debugCtx);
|
hostElement = this.renderer.createElement(null, elementName, debugInfo);
|
||||||
}
|
}
|
||||||
return hostElement;
|
return hostElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
injectorGet(token: any, nodeIndex: number, notFoundResult: any): any {
|
injectorGet(token: any, nodeIndex: number, notFoundResult: any): any {
|
||||||
if (this.debugMode) {
|
|
||||||
this._resetDebug();
|
|
||||||
try {
|
|
||||||
return this.injectorGetInternal(token, nodeIndex, notFoundResult);
|
return this.injectorGetInternal(token, nodeIndex, notFoundResult);
|
||||||
} catch (e) {
|
|
||||||
this._rethrowWithContext(e, e.stack);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return this.injectorGetInternal(token, nodeIndex, notFoundResult);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -210,22 +192,12 @@ export abstract class AppView<T> {
|
||||||
for (var i = 0; i < children.length; i++) {
|
for (var i = 0; i < children.length; i++) {
|
||||||
children[i]._destroyRecurse();
|
children[i]._destroyRecurse();
|
||||||
}
|
}
|
||||||
if (this.debugMode) {
|
this.destroyLocal();
|
||||||
this._resetDebug();
|
|
||||||
try {
|
|
||||||
this._destroyLocal();
|
|
||||||
} catch (e) {
|
|
||||||
this._rethrowWithContext(e, e.stack);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._destroyLocal();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.destroyed = true;
|
this.destroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _destroyLocal() {
|
destroyLocal() {
|
||||||
var hostElement =
|
var hostElement =
|
||||||
this.type === ViewType.COMPONENT ? this.declarationAppElement.nativeElement : null;
|
this.type === ViewType.COMPONENT ? this.declarationAppElement.nativeElement : null;
|
||||||
for (var i = 0; i < this.disposables.length; i++) {
|
for (var i = 0; i < this.disposables.length; i++) {
|
||||||
|
@ -250,8 +222,6 @@ export abstract class AppView<T> {
|
||||||
*/
|
*/
|
||||||
destroyInternal(): void {}
|
destroyInternal(): void {}
|
||||||
|
|
||||||
get debugMode(): boolean { return isPresent(this.staticNodeDebugInfos); }
|
|
||||||
|
|
||||||
get changeDetectorRef(): ChangeDetectorRef { return this.ref; }
|
get changeDetectorRef(): ChangeDetectorRef { return this.ref; }
|
||||||
|
|
||||||
get parent(): AppView<any> {
|
get parent(): AppView<any> {
|
||||||
|
@ -293,17 +263,7 @@ export abstract class AppView<T> {
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
this.throwDestroyedError('detectChanges');
|
this.throwDestroyedError('detectChanges');
|
||||||
}
|
}
|
||||||
if (this.debugMode) {
|
|
||||||
this._resetDebug();
|
|
||||||
try {
|
|
||||||
this.detectChangesInternal(throwOnChange);
|
this.detectChangesInternal(throwOnChange);
|
||||||
} catch (e) {
|
|
||||||
this._rethrowWithContext(e, e.stack);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.detectChangesInternal(throwOnChange);
|
|
||||||
}
|
|
||||||
if (this.cdMode === ChangeDetectionStrategy.CheckOnce)
|
if (this.cdMode === ChangeDetectionStrategy.CheckOnce)
|
||||||
this.cdMode = ChangeDetectionStrategy.Checked;
|
this.cdMode = ChangeDetectionStrategy.Checked;
|
||||||
|
|
||||||
|
@ -355,6 +315,61 @@ export abstract class AppView<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eventHandler(cb: Function): Function { return cb; }
|
||||||
|
|
||||||
|
throwDestroyedError(details: string): void { throw new ViewDestroyedException(details); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DebugAppView<T> extends AppView<T> {
|
||||||
|
private _currentDebugContext: DebugContext = null;
|
||||||
|
|
||||||
|
constructor(clazz: any, componentType: RenderComponentType, type: ViewType, viewUtils: ViewUtils,
|
||||||
|
parentInjector: Injector, declarationAppElement: AppElement,
|
||||||
|
cdMode: ChangeDetectionStrategy, public staticNodeDebugInfos: StaticNodeDebugInfo[]) {
|
||||||
|
super(clazz, componentType, type, viewUtils, parentInjector, declarationAppElement, cdMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(context: T, givenProjectableNodes: Array<any | any[]>,
|
||||||
|
rootSelectorOrNode: string | any): AppElement {
|
||||||
|
this._resetDebug();
|
||||||
|
try {
|
||||||
|
return super.create(context, givenProjectableNodes, rootSelectorOrNode);
|
||||||
|
} catch (e) {
|
||||||
|
this._rethrowWithContext(e, e.stack);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
injectorGet(token: any, nodeIndex: number, notFoundResult: any): any {
|
||||||
|
this._resetDebug();
|
||||||
|
try {
|
||||||
|
return super.injectorGet(token, nodeIndex, notFoundResult);
|
||||||
|
} catch (e) {
|
||||||
|
this._rethrowWithContext(e, e.stack);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyLocal() {
|
||||||
|
this._resetDebug();
|
||||||
|
try {
|
||||||
|
super.destroyLocal();
|
||||||
|
} catch (e) {
|
||||||
|
this._rethrowWithContext(e, e.stack);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detectChanges(throwOnChange: boolean): void {
|
||||||
|
this._resetDebug();
|
||||||
|
try {
|
||||||
|
super.detectChanges(throwOnChange);
|
||||||
|
} catch (e) {
|
||||||
|
this._rethrowWithContext(e, e.stack);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _resetDebug() { this._currentDebugContext = null; }
|
private _resetDebug() { this._currentDebugContext = null; }
|
||||||
|
|
||||||
debug(nodeIndex: number, rowNum: number, colNum: number): DebugContext {
|
debug(nodeIndex: number, rowNum: number, colNum: number): DebugContext {
|
||||||
|
@ -373,24 +388,19 @@ export abstract class AppView<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
eventHandler(cb: Function): Function {
|
eventHandler(cb: Function): Function {
|
||||||
if (this.debugMode) {
|
var superHandler = super.eventHandler(cb);
|
||||||
return (event) => {
|
return (event) => {
|
||||||
this._resetDebug();
|
this._resetDebug();
|
||||||
try {
|
try {
|
||||||
return cb(event);
|
return superHandler(event);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._rethrowWithContext(e, e.stack);
|
this._rethrowWithContext(e, e.stack);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
return cb;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throwDestroyedError(details: string): void { throw new ViewDestroyedException(details); }
|
|
||||||
}
|
|
||||||
|
|
||||||
function _findLastRenderNode(node: any): any {
|
function _findLastRenderNode(node: any): any {
|
||||||
var lastNode;
|
var lastNode;
|
||||||
if (node instanceof AppElement) {
|
if (node instanceof AppElement) {
|
||||||
|
|
Loading…
Reference in New Issue