From f66ac821a2f1cdf77333729a330785bdbf8f3912 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Mon, 24 Oct 2016 08:43:06 -0700 Subject: [PATCH] refactor(compiler): extract `createCheckBindingStmt` into `compiler_util` Part of #11683 --- .../src/compiler_util/binding_util.ts | 48 +++++++++++ .../src/directive_wrapper_compiler.ts | 21 ++--- .../src/view_compiler/compile_binding.ts | 15 ---- .../src/view_compiler/compile_view.ts | 3 - .../src/view_compiler/event_binder.ts | 3 - .../src/view_compiler/property_binder.ts | 84 ++++++------------- .../compiler/src/view_compiler/view_binder.ts | 8 +- 7 files changed, 85 insertions(+), 97 deletions(-) create mode 100644 modules/@angular/compiler/src/compiler_util/binding_util.ts delete mode 100644 modules/@angular/compiler/src/view_compiler/compile_binding.ts diff --git a/modules/@angular/compiler/src/compiler_util/binding_util.ts b/modules/@angular/compiler/src/compiler_util/binding_util.ts new file mode 100644 index 0000000000..205fea6e74 --- /dev/null +++ b/modules/@angular/compiler/src/compiler_util/binding_util.ts @@ -0,0 +1,48 @@ +/** + * @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 {Identifiers, resolveIdentifier} from '../identifiers'; +import {ClassBuilder} from '../output/class_builder'; +import * as o from '../output/output_ast'; + +import {ConvertPropertyBindingResult} from './expression_converter'; + +export class CheckBindingField { + constructor(public expression: o.ReadPropExpr, public bindingId: string) {} +} + +export function createCheckBindingField(builder: ClassBuilder): CheckBindingField { + const bindingId = `${builder.fields.length}`; + const fieldExpr = createBindFieldExpr(bindingId); + // private is fine here as no child view will reference the cached value... + builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private])); + builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name) + .set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED))) + .toStmt()); + return new CheckBindingField(fieldExpr, bindingId); +} + +export function createCheckBindingStmt( + evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr, + throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] { + var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([ + throwOnChangeVar, fieldExpr, evalResult.currValExpr + ]); + if (evalResult.forceUpdate) { + condition = evalResult.forceUpdate.or(condition); + } + return [ + ...evalResult.stmts, new o.IfStmt(condition, actions.concat([ + o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt() + ])) + ]; +} + +function createBindFieldExpr(bindingId: string): o.ReadPropExpr { + return o.THIS_EXPR.prop(`_expr_${bindingId}`); +} diff --git a/modules/@angular/compiler/src/directive_wrapper_compiler.ts b/modules/@angular/compiler/src/directive_wrapper_compiler.ts index e0b8ec4626..4c598b8b9f 100644 --- a/modules/@angular/compiler/src/directive_wrapper_compiler.ts +++ b/modules/@angular/compiler/src/directive_wrapper_compiler.ts @@ -9,6 +9,7 @@ import {Injectable} from '@angular/core'; import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata'; +import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util'; import {CompilerConfig} from './config'; import {Identifiers, resolveIdentifier} from './identifiers'; import {ClassBuilder, createClassStmt} from './output/class_builder'; @@ -158,13 +159,7 @@ function addDetectChangesInternalMethod(builder: DirectiveWrapperBuilder) { } function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) { - const fieldName = `_${input}`; - const fieldExpr = o.THIS_EXPR.prop(fieldName); - // private is fine here as no child view will reference the cached value... - builder.fields.push(new o.ClassField(fieldName, null, [o.StmtModifier.Private])); - builder.ctorStmts.push(o.THIS_EXPR.prop(fieldName) - .set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED))) - .toStmt()); + const field = createCheckBindingField(builder); var onChangeStatements: o.Statement[] = [ o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(), o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(), @@ -173,17 +168,13 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) { onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME) .key(o.literal(input)) .set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange)) - .instantiate([fieldExpr, CURR_VALUE_VAR])) + .instantiate([field.expression, CURR_VALUE_VAR])) .toStmt()); } - onChangeStatements.push(fieldExpr.set(CURR_VALUE_VAR).toStmt()); - var methodBody: o.Statement[] = [ - new o.IfStmt( - FORCE_UPDATE_VAR.or(o.importExpr(resolveIdentifier(Identifiers.checkBinding)) - .callFn([THROW_ON_CHANGE_VAR, fieldExpr, CURR_VALUE_VAR])), - onChangeStatements), - ]; + var methodBody: o.Statement[] = createCheckBindingStmt( + {currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression, + THROW_ON_CHANGE_VAR, onChangeStatements); builder.methods.push(new o.ClassMethod( `check_${input}`, [ diff --git a/modules/@angular/compiler/src/view_compiler/compile_binding.ts b/modules/@angular/compiler/src/view_compiler/compile_binding.ts deleted file mode 100644 index 53633a47cb..0000000000 --- a/modules/@angular/compiler/src/view_compiler/compile_binding.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @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 {TemplateAst} from '../template_parser/template_ast'; - -import {CompileNode} from './compile_element'; - -export class CompileBinding { - constructor(public node: CompileNode, public sourceAst: TemplateAst) {} -} diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index a3bcb1e007..30db560529 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -17,7 +17,6 @@ import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; import {ViewType} from '../private_import_core'; -import {CompileBinding} from './compile_binding'; import {CompileElement, CompileNode} from './compile_element'; import {CompileMethod} from './compile_method'; import {CompilePipe} from './compile_pipe'; @@ -32,8 +31,6 @@ export class CompileView implements NameResolver { // root nodes or AppElements for ViewContainers public rootNodesOrAppElements: o.Expression[] = []; - public bindings: CompileBinding[] = []; - public createMethod: CompileMethod; public animationBindingsMethod: CompileMethod; public injectorGetMethod: CompileMethod; diff --git a/modules/@angular/compiler/src/view_compiler/event_binder.ts b/modules/@angular/compiler/src/view_compiler/event_binder.ts index d3bfcf2a70..0aa2d9de88 100644 --- a/modules/@angular/compiler/src/view_compiler/event_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/event_binder.ts @@ -13,7 +13,6 @@ import {identifierToken} from '../identifiers'; import * as o from '../output/output_ast'; import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast'; -import {CompileBinding} from './compile_binding'; import {CompileElement} from './compile_element'; import {CompileMethod} from './compile_method'; import {ViewProperties} from './constants'; @@ -134,7 +133,6 @@ export function collectEventListeners( const eventListeners: CompileEventListener[] = []; hostEvents.forEach((hostEvent) => { - compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); var listener = CompileEventListener.getOrCreate( compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); listener.addAction(hostEvent, null, null); @@ -144,7 +142,6 @@ export function collectEventListeners( var directiveInstance = compileElement.instances.get(identifierToken(directiveAst.directive.type).reference); directiveAst.hostEvents.forEach((hostEvent) => { - compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); var listener = CompileEventListener.getOrCreate( compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); listener.addAction(hostEvent, directiveAst.directive, directiveInstance); diff --git a/modules/@angular/compiler/src/view_compiler/property_binder.ts b/modules/@angular/compiler/src/view_compiler/property_binder.ts index 9b50773e3f..1d66730b49 100644 --- a/modules/@angular/compiler/src/view_compiler/property_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/property_binder.ts @@ -8,6 +8,7 @@ import {SecurityContext} from '@angular/core'; +import {createCheckBindingField, createCheckBindingStmt} from '../compiler_util/binding_util'; import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter'; import {writeToRenderer} from '../compiler_util/render_util'; import * as cdAst from '../expression_parser/ast'; @@ -18,57 +19,27 @@ import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDet import {BoundElementPropertyAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast'; import {camelCaseToDashCase} from '../util'; -import {CompileBinding} from './compile_binding'; import {CompileElement, CompileNode} from './compile_element'; import {CompileMethod} from './compile_method'; import {CompileView} from './compile_view'; import {DetectChangesVars, ViewProperties} from './constants'; import {CompileEventListener} from './event_binder'; -function createBindFieldExpr(bindingId: string): o.ReadPropExpr { - return o.THIS_EXPR.prop(`_expr_${bindingId}`); -} - -function createCheckBindingStmt( - view: CompileView, evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr, - actions: o.Statement[], method: CompileMethod) { - // private is fine here as no child view will reference the cached value... - view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private])); - view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name) - .set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED))) - .toStmt()); - - var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([ - DetectChangesVars.throwOnChange, fieldExpr, evalResult.currValExpr - ]); - if (evalResult.forceUpdate) { - condition = evalResult.forceUpdate.or(condition); - } - method.addStmts(evalResult.stmts); - method.addStmt(new o.IfStmt(condition, actions.concat([ - o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt() - ]))); -} - export function bindRenderText( boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void { - var bindingId = `${view.bindings.length}`; - view.bindings.push(new CompileBinding(compileNode, boundText)); - const evalResult = - convertPropertyBinding(view, view, view.componentContext, boundText.value, bindingId); + const valueField = createCheckBindingField(view); + const evalResult = convertPropertyBinding( + view, view, view.componentContext, boundText.value, valueField.bindingId); if (!evalResult) { return null; } - var valueField = createBindFieldExpr(bindingId); view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText); - - createCheckBindingStmt( - view, evalResult, valueField, + view.detectChangesRenderPropertiesMethod.addStmts(createCheckBindingStmt( + evalResult, valueField.expression, DetectChangesVars.throwOnChange, [o.THIS_EXPR.prop('renderer') .callMethod('setText', [compileNode.renderNode, evalResult.currValExpr]) - .toStmt()], - view.detectChangesRenderPropertiesMethod); + .toStmt()])); } function bindAndWriteToRenderer( @@ -77,12 +48,10 @@ function bindAndWriteToRenderer( var view = compileElement.view; var renderNode = compileElement.renderNode; boundProps.forEach((boundProp) => { - const bindingId = `${view.bindings.length}`; - view.bindings.push(new CompileBinding(compileElement, boundProp)); + const bindingField = createCheckBindingField(view); view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp); - var fieldExpr = createBindFieldExpr(bindingId); - const evalResult = - convertPropertyBinding(view, isHostProp ? null : view, context, boundProp.value, bindingId); + const evalResult = convertPropertyBinding( + view, isHostProp ? null : view, context, boundProp.value, bindingField.bindingId); var updateStmts: o.Statement[] = []; var compileMethod = view.detectChangesRenderPropertiesMethod; switch (boundProp.type) { @@ -111,19 +80,20 @@ function bindAndWriteToRenderer( const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)); const animationTransitionVar = o.variable('animationTransition_' + animationName); - updateStmts.push( - animationTransitionVar - .set(animationFnExpr.callFn([ - o.THIS_EXPR, renderNode, - fieldExpr.equals(unitializedValue).conditional(emptyStateValue, fieldExpr), - evalResult.currValExpr.equals(unitializedValue) - .conditional(emptyStateValue, evalResult.currValExpr) - ])) - .toDeclStmt()); + updateStmts.push(animationTransitionVar + .set(animationFnExpr.callFn([ + o.THIS_EXPR, renderNode, + bindingField.expression.equals(unitializedValue) + .conditional(emptyStateValue, bindingField.expression), + evalResult.currValExpr.equals(unitializedValue) + .conditional(emptyStateValue, evalResult.currValExpr) + ])) + .toDeclStmt()); detachStmts.push( animationTransitionVar - .set(animationFnExpr.callFn([o.THIS_EXPR, renderNode, fieldExpr, emptyStateValue])) + .set(animationFnExpr.callFn( + [o.THIS_EXPR, renderNode, bindingField.expression, emptyStateValue])) .toDeclStmt()); eventListeners.forEach(listener => { @@ -138,8 +108,8 @@ function bindAndWriteToRenderer( break; } - - createCheckBindingStmt(view, evalResult, fieldExpr, updateStmts, compileMethod); + compileMethod.addStmts(createCheckBindingStmt( + evalResult, bindingField.expression, DetectChangesVars.throwOnChange, updateStmts)); }); } @@ -158,15 +128,15 @@ export function bindDirectiveHostProps( } export function bindDirectiveInputs( - directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, + directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, dirIndex: number, compileElement: CompileElement) { var view = compileElement.view; var detectChangesInInputsMethod = view.detectChangesInInputsMethod; detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst); - directiveAst.inputs.forEach((input) => { - const bindingId = `${view.bindings.length}`; - view.bindings.push(new CompileBinding(compileElement, input)); + directiveAst.inputs.forEach((input, inputIdx) => { + // Note: We can't use `fields.length` here, as we are not adding a field! + const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`; detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input); const evalResult = convertPropertyBinding(view, view, view.componentContext, input.value, bindingId); diff --git a/modules/@angular/compiler/src/view_compiler/view_binder.ts b/modules/@angular/compiler/src/view_compiler/view_binder.ts index 9e65859331..691de894ea 100644 --- a/modules/@angular/compiler/src/view_compiler/view_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_binder.ts @@ -46,11 +46,11 @@ class ViewBinderVisitor implements TemplateAstVisitor { }); bindRenderInputs(ast.inputs, compileElement, eventListeners); bindRenderOutputs(eventListeners); - ast.directives.forEach((directiveAst) => { + ast.directives.forEach((directiveAst, dirIndex) => { var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference); var directiveWrapperInstance = compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference); - bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement); + bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement); bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners); bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners); @@ -75,11 +75,11 @@ class ViewBinderVisitor implements TemplateAstVisitor { visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any { var compileElement = this.view.nodes[this._nodeIndex++]; var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement); - ast.directives.forEach((directiveAst) => { + ast.directives.forEach((directiveAst, dirIndex) => { var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference); var directiveWrapperInstance = compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference); - bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement); + bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement); bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners); bindDirectiveAfterContentLifecycleCallbacks(