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 CHANGES_FIELD_NAME = 'changes';
const CHANGED_FIELD_NAME = 'changed';
const EVENT_HANDLER_FIELD_NAME = 'eventHandler';
const CHANGES_FIELD_NAME = '_changes';
const CHANGED_FIELD_NAME = '_changed';
const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
const CURR_VALUE_VAR = o.variable('currValue');
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
@ -129,14 +129,15 @@ class DirectiveWrapperBuilder implements ClassBuilder {
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(CHANGED_FIELD_NAME, o.BOOL_TYPE),
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
];
const ctorStmts: o.Statement[] =
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
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);
}
@ -303,7 +304,11 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
}
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[] = [
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));
const subscriptionFieldName = `subscription${emitterIdx}`;
builder.fields.push(new o.ClassField(subscriptionFieldName, o.DYNAMIC_TYPE));
stmts.push(new o.IfStmt(
o.variable(paramName),
[o.THIS_EXPR.prop(subscriptionFieldName)
stmts.push(new o.IfStmt(o.variable(paramName), [
o.THIS_EXPR.prop(subscriptionFieldName)
.set(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
.prop(emitterPropName)
.callMethod(
o.BuiltinMethod.SubscribeObservable,
[o.variable(EVENT_HANDLER_FIELD_NAME)
.callMethod(
o.BuiltinMethod.Bind, [o.NULL_EXPR, o.literal(eventName)])]))
.toStmt()]));
.callMethod(o.BuiltinMethod.Bind, [VIEW_VAR, o.literal(eventName)])]))
.toStmt()
]));
builder.destroyStmts.push(
o.THIS_EXPR.prop(subscriptionFieldName)
.and(o.THIS_EXPR.prop(subscriptionFieldName).callMethod('unsubscribe', []))
@ -424,7 +428,7 @@ export class DirectiveWrapperExpressions {
}
static subscribe(
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 eventFlags: o.Expression[] = [];
Object.keys(dirMeta.outputs).forEach((propName) => {
@ -439,7 +443,9 @@ export class DirectiveWrapperExpressions {
}
});
if (needsSubscribe) {
return [dirWrapper.callMethod('subscribe', [eventListener].concat(eventFlags)).toStmt()];
return [
dirWrapper.callMethod('subscribe', [view, eventListener].concat(eventFlags)).toStmt()
];
} else {
return [];
}

View File

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

View File

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

View File

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