fix(compiler): don’t double bind functions

This fixes a performance regressions introduced by 178fb79b5c.

Also makes properties in the directive wrapper private
so that closure compiler can minify them better.
This commit is contained in:
Tobias Bosch 2016-10-28 10:44:48 -07:00
parent 32feb8a532
commit e391cacdf9
4 changed files with 38 additions and 33 deletions

View File

@ -29,9 +29,9 @@ export class DirectiveWrapperCompileResult {
} }
const CONTEXT_FIELD_NAME = 'context'; const CONTEXT_FIELD_NAME = 'context';
const CHANGES_FIELD_NAME = 'changes'; const CHANGES_FIELD_NAME = '_changes';
const CHANGED_FIELD_NAME = 'changed'; const CHANGED_FIELD_NAME = '_changed';
const EVENT_HANDLER_FIELD_NAME = 'eventHandler'; const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
const CURR_VALUE_VAR = o.variable('currValue'); const CURR_VALUE_VAR = o.variable('currValue');
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange'); const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
@ -129,14 +129,15 @@ class DirectiveWrapperBuilder implements ClassBuilder {
const fields: o.ClassField[] = [ const fields: o.ClassField[] = [
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE), new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)), new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE), new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
]; ];
const ctorStmts: o.Statement[] = const ctorStmts: o.Statement[] =
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()]; [o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
if (this.genChanges) { if (this.genChanges) {
fields.push(new o.ClassField(CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE))); fields.push(new o.ClassField(
CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE), [o.StmtModifier.Private]));
ctorStmts.push(RESET_CHANGES_STMT); ctorStmts.push(RESET_CHANGES_STMT);
} }
@ -303,7 +304,11 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
} }
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) { function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
const methodParams: o.FnParam[] = [new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)]; const methodParams: o.FnParam[] = [
new o.FnParam(
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)
];
const stmts: o.Statement[] = [ const stmts: o.Statement[] = [
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME).set(o.variable(EVENT_HANDLER_FIELD_NAME)).toStmt() o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME).set(o.variable(EVENT_HANDLER_FIELD_NAME)).toStmt()
]; ];
@ -313,17 +318,16 @@ function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: Directiv
methodParams.push(new o.FnParam(paramName, o.BOOL_TYPE)); methodParams.push(new o.FnParam(paramName, o.BOOL_TYPE));
const subscriptionFieldName = `subscription${emitterIdx}`; const subscriptionFieldName = `subscription${emitterIdx}`;
builder.fields.push(new o.ClassField(subscriptionFieldName, o.DYNAMIC_TYPE)); builder.fields.push(new o.ClassField(subscriptionFieldName, o.DYNAMIC_TYPE));
stmts.push(new o.IfStmt( stmts.push(new o.IfStmt(o.variable(paramName), [
o.variable(paramName), o.THIS_EXPR.prop(subscriptionFieldName)
[o.THIS_EXPR.prop(subscriptionFieldName) .set(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
.set(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME) .prop(emitterPropName)
.prop(emitterPropName) .callMethod(
.callMethod( o.BuiltinMethod.SubscribeObservable,
o.BuiltinMethod.SubscribeObservable, [o.variable(EVENT_HANDLER_FIELD_NAME)
[o.variable(EVENT_HANDLER_FIELD_NAME) .callMethod(o.BuiltinMethod.Bind, [VIEW_VAR, o.literal(eventName)])]))
.callMethod( .toStmt()
o.BuiltinMethod.Bind, [o.NULL_EXPR, o.literal(eventName)])])) ]));
.toStmt()]));
builder.destroyStmts.push( builder.destroyStmts.push(
o.THIS_EXPR.prop(subscriptionFieldName) o.THIS_EXPR.prop(subscriptionFieldName)
.and(o.THIS_EXPR.prop(subscriptionFieldName).callMethod('unsubscribe', [])) .and(o.THIS_EXPR.prop(subscriptionFieldName).callMethod('unsubscribe', []))
@ -424,7 +428,7 @@ export class DirectiveWrapperExpressions {
} }
static subscribe( static subscribe(
dirMeta: CompileDirectiveMetadata, hostProps: BoundElementPropertyAst[], usedEvents: string[], dirMeta: CompileDirectiveMetadata, hostProps: BoundElementPropertyAst[], usedEvents: string[],
dirWrapper: o.Expression, eventListener: o.Expression): o.Statement[] { dirWrapper: o.Expression, view: o.Expression, eventListener: o.Expression): o.Statement[] {
let needsSubscribe = false; let needsSubscribe = false;
let eventFlags: o.Expression[] = []; let eventFlags: o.Expression[] = [];
Object.keys(dirMeta.outputs).forEach((propName) => { Object.keys(dirMeta.outputs).forEach((propName) => {
@ -439,7 +443,9 @@ export class DirectiveWrapperExpressions {
} }
}); });
if (needsSubscribe) { if (needsSubscribe) {
return [dirWrapper.callMethod('subscribe', [eventListener].concat(eventFlags)).toStmt()]; return [
dirWrapper.callMethod('subscribe', [view, eventListener].concat(eventFlags)).toStmt()
];
} else { } else {
return []; return [];
} }

View File

@ -59,8 +59,8 @@ function subscribeToRenderEvents(
compileElement.view.createMethod.addStmt( compileElement.view.createMethod.addStmt(
disposableVar disposableVar
.set(o.importExpr(resolveIdentifier(Identifiers.subscribeToRenderElement)).callFn([ .set(o.importExpr(resolveIdentifier(Identifiers.subscribeToRenderElement)).callFn([
ViewProperties.renderer, compileElement.renderNode, o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs),
createInlineArray(eventAndTargetExprs), handleEventClosure(compileElement) handleEventExpr(compileElement)
])) ]))
.toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private])); .toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
} }
@ -73,8 +73,8 @@ function subscribeToDirectiveEvents(
directives.forEach((dirAst) => { directives.forEach((dirAst) => {
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference); const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
compileElement.view.createMethod.addStmts(DirectiveWrapperExpressions.subscribe( compileElement.view.createMethod.addStmts(DirectiveWrapperExpressions.subscribe(
dirAst.directive, dirAst.hostProperties, usedEventNames, dirWrapper, dirAst.directive, dirAst.hostProperties, usedEventNames, dirWrapper, o.THIS_EXPR,
handleEventClosure(compileElement))); handleEventExpr(compileElement)));
}); });
} }
@ -127,11 +127,9 @@ function generateHandleEventMethod(
handleEventStmts.finish(), o.BOOL_TYPE)); handleEventStmts.finish(), o.BOOL_TYPE));
} }
function handleEventClosure(compileElement: CompileElement) { function handleEventExpr(compileElement: CompileElement) {
const handleEventMethodName = getHandleEventMethodName(compileElement.nodeIndex); const handleEventMethodName = getHandleEventMethodName(compileElement.nodeIndex);
return o.THIS_EXPR.callMethod( return o.THIS_EXPR.callMethod('eventHandler', [o.THIS_EXPR.prop(handleEventMethodName)]);
'eventHandler',
[o.THIS_EXPR.prop(handleEventMethodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
} }
type EventSummary = { type EventSummary = {

View File

@ -375,7 +375,7 @@ export class DebugAppView<T> extends AppView<T> {
return (eventName: string, event?: any) => { return (eventName: string, event?: any) => {
this._resetDebug(); this._resetDebug();
try { try {
return superHandler(eventName, event); return superHandler.call(this, eventName, event);
} catch (e) { } catch (e) {
this._rethrowWithContext(e); this._rethrowWithContext(e);
throw e; throw e;

View File

@ -17,6 +17,7 @@ import {Sanitizer} from '../security';
import {AppElement} from './element'; import {AppElement} from './element';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors'; import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
import {AppView} from './view';
@Injectable() @Injectable()
export class ViewUtils { export class ViewUtils {
@ -401,7 +402,7 @@ export function selectOrCreateRenderHostElement(
} }
export function subscribeToRenderElement( export function subscribeToRenderElement(
renderer: Renderer, element: any, eventNamesAndTargets: InlineArray<string>, view: AppView<any>, element: any, eventNamesAndTargets: InlineArray<string>,
listener: (eventName: string, event: any) => any) { listener: (eventName: string, event: any) => any) {
const disposables = createEmptyInlineArray(eventNamesAndTargets.length / 2); const disposables = createEmptyInlineArray(eventNamesAndTargets.length / 2);
for (var i = 0; i < eventNamesAndTargets.length; i += 2) { for (var i = 0; i < eventNamesAndTargets.length; i += 2) {
@ -409,10 +410,10 @@ export function subscribeToRenderElement(
const eventTarget = eventNamesAndTargets.get(i + 1); const eventTarget = eventNamesAndTargets.get(i + 1);
let disposable: Function; let disposable: Function;
if (eventTarget) { if (eventTarget) {
disposable = renderer.listenGlobal( disposable = view.renderer.listenGlobal(
eventTarget, eventName, listener.bind(null, `${eventTarget}:${eventName}`)); eventTarget, eventName, listener.bind(view, `${eventTarget}:${eventName}`));
} else { } else {
disposable = renderer.listen(element, eventName, listener.bind(null, eventName)); disposable = view.renderer.listen(element, eventName, listener.bind(view, eventName));
} }
disposables.set(i / 2, disposable); disposables.set(i / 2, disposable);
} }