refactor(compiler): extract `createCheckBindingStmt` into `compiler_util`

Part of #11683
This commit is contained in:
Tobias Bosch 2016-10-24 08:43:06 -07:00 committed by vsavkin
parent fe299f4dfc
commit f66ac821a2
7 changed files with 85 additions and 97 deletions

View File

@ -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.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
]))
];
}
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
}

View File

@ -9,6 +9,7 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata';
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import {Identifiers, resolveIdentifier} from './identifiers'; import {Identifiers, resolveIdentifier} from './identifiers';
import {ClassBuilder, createClassStmt} from './output/class_builder'; import {ClassBuilder, createClassStmt} from './output/class_builder';
@ -158,13 +159,7 @@ function addDetectChangesInternalMethod(builder: DirectiveWrapperBuilder) {
} }
function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) { function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
const fieldName = `_${input}`; const field = createCheckBindingField(builder);
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());
var onChangeStatements: o.Statement[] = [ var onChangeStatements: o.Statement[] = [
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(), 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(), 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) onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
.key(o.literal(input)) .key(o.literal(input))
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange)) .set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
.instantiate([fieldExpr, CURR_VALUE_VAR])) .instantiate([field.expression, CURR_VALUE_VAR]))
.toStmt()); .toStmt());
} }
onChangeStatements.push(fieldExpr.set(CURR_VALUE_VAR).toStmt());
var methodBody: o.Statement[] = [ var methodBody: o.Statement[] = createCheckBindingStmt(
new o.IfStmt( {currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression,
FORCE_UPDATE_VAR.or(o.importExpr(resolveIdentifier(Identifiers.checkBinding)) THROW_ON_CHANGE_VAR, onChangeStatements);
.callFn([THROW_ON_CHANGE_VAR, fieldExpr, CURR_VALUE_VAR])),
onChangeStatements),
];
builder.methods.push(new o.ClassMethod( builder.methods.push(new o.ClassMethod(
`check_${input}`, `check_${input}`,
[ [

View File

@ -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) {}
}

View File

@ -17,7 +17,6 @@ import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core'; import {ViewType} from '../private_import_core';
import {CompileBinding} from './compile_binding';
import {CompileElement, CompileNode} from './compile_element'; import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method'; import {CompileMethod} from './compile_method';
import {CompilePipe} from './compile_pipe'; import {CompilePipe} from './compile_pipe';
@ -32,8 +31,6 @@ export class CompileView implements NameResolver {
// root nodes or AppElements for ViewContainers // root nodes or AppElements for ViewContainers
public rootNodesOrAppElements: o.Expression[] = []; public rootNodesOrAppElements: o.Expression[] = [];
public bindings: CompileBinding[] = [];
public createMethod: CompileMethod; public createMethod: CompileMethod;
public animationBindingsMethod: CompileMethod; public animationBindingsMethod: CompileMethod;
public injectorGetMethod: CompileMethod; public injectorGetMethod: CompileMethod;

View File

@ -13,7 +13,6 @@ import {identifierToken} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast'; import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
import {CompileBinding} from './compile_binding';
import {CompileElement} from './compile_element'; import {CompileElement} from './compile_element';
import {CompileMethod} from './compile_method'; import {CompileMethod} from './compile_method';
import {ViewProperties} from './constants'; import {ViewProperties} from './constants';
@ -134,7 +133,6 @@ export function collectEventListeners(
const eventListeners: CompileEventListener[] = []; const eventListeners: CompileEventListener[] = [];
hostEvents.forEach((hostEvent) => { hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate( var listener = CompileEventListener.getOrCreate(
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
listener.addAction(hostEvent, null, null); listener.addAction(hostEvent, null, null);
@ -144,7 +142,6 @@ export function collectEventListeners(
var directiveInstance = var directiveInstance =
compileElement.instances.get(identifierToken(directiveAst.directive.type).reference); compileElement.instances.get(identifierToken(directiveAst.directive.type).reference);
directiveAst.hostEvents.forEach((hostEvent) => { directiveAst.hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate( var listener = CompileEventListener.getOrCreate(
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
listener.addAction(hostEvent, directiveAst.directive, directiveInstance); listener.addAction(hostEvent, directiveAst.directive, directiveInstance);

View File

@ -8,6 +8,7 @@
import {SecurityContext} from '@angular/core'; import {SecurityContext} from '@angular/core';
import {createCheckBindingField, createCheckBindingStmt} from '../compiler_util/binding_util';
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter'; import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
import {writeToRenderer} from '../compiler_util/render_util'; import {writeToRenderer} from '../compiler_util/render_util';
import * as cdAst from '../expression_parser/ast'; 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 {BoundElementPropertyAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
import {camelCaseToDashCase} from '../util'; import {camelCaseToDashCase} from '../util';
import {CompileBinding} from './compile_binding';
import {CompileElement, CompileNode} from './compile_element'; import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method'; import {CompileMethod} from './compile_method';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
import {DetectChangesVars, ViewProperties} from './constants'; import {DetectChangesVars, ViewProperties} from './constants';
import {CompileEventListener} from './event_binder'; 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.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
])));
}
export function bindRenderText( export function bindRenderText(
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void { boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
var bindingId = `${view.bindings.length}`; const valueField = createCheckBindingField(view);
view.bindings.push(new CompileBinding(compileNode, boundText)); const evalResult = convertPropertyBinding(
const evalResult = view, view, view.componentContext, boundText.value, valueField.bindingId);
convertPropertyBinding(view, view, view.componentContext, boundText.value, bindingId);
if (!evalResult) { if (!evalResult) {
return null; return null;
} }
var valueField = createBindFieldExpr(bindingId);
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText); view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
view.detectChangesRenderPropertiesMethod.addStmts(createCheckBindingStmt(
createCheckBindingStmt( evalResult, valueField.expression, DetectChangesVars.throwOnChange,
view, evalResult, valueField,
[o.THIS_EXPR.prop('renderer') [o.THIS_EXPR.prop('renderer')
.callMethod('setText', [compileNode.renderNode, evalResult.currValExpr]) .callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
.toStmt()], .toStmt()]));
view.detectChangesRenderPropertiesMethod);
} }
function bindAndWriteToRenderer( function bindAndWriteToRenderer(
@ -77,12 +48,10 @@ function bindAndWriteToRenderer(
var view = compileElement.view; var view = compileElement.view;
var renderNode = compileElement.renderNode; var renderNode = compileElement.renderNode;
boundProps.forEach((boundProp) => { boundProps.forEach((boundProp) => {
const bindingId = `${view.bindings.length}`; const bindingField = createCheckBindingField(view);
view.bindings.push(new CompileBinding(compileElement, boundProp));
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp); view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
var fieldExpr = createBindFieldExpr(bindingId); const evalResult = convertPropertyBinding(
const evalResult = view, isHostProp ? null : view, context, boundProp.value, bindingField.bindingId);
convertPropertyBinding(view, isHostProp ? null : view, context, boundProp.value, bindingId);
var updateStmts: o.Statement[] = []; var updateStmts: o.Statement[] = [];
var compileMethod = view.detectChangesRenderPropertiesMethod; var compileMethod = view.detectChangesRenderPropertiesMethod;
switch (boundProp.type) { switch (boundProp.type) {
@ -111,19 +80,20 @@ function bindAndWriteToRenderer(
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)); const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
const animationTransitionVar = o.variable('animationTransition_' + animationName); const animationTransitionVar = o.variable('animationTransition_' + animationName);
updateStmts.push( updateStmts.push(animationTransitionVar
animationTransitionVar .set(animationFnExpr.callFn([
.set(animationFnExpr.callFn([ o.THIS_EXPR, renderNode,
o.THIS_EXPR, renderNode, bindingField.expression.equals(unitializedValue)
fieldExpr.equals(unitializedValue).conditional(emptyStateValue, fieldExpr), .conditional(emptyStateValue, bindingField.expression),
evalResult.currValExpr.equals(unitializedValue) evalResult.currValExpr.equals(unitializedValue)
.conditional(emptyStateValue, evalResult.currValExpr) .conditional(emptyStateValue, evalResult.currValExpr)
])) ]))
.toDeclStmt()); .toDeclStmt());
detachStmts.push( detachStmts.push(
animationTransitionVar animationTransitionVar
.set(animationFnExpr.callFn([o.THIS_EXPR, renderNode, fieldExpr, emptyStateValue])) .set(animationFnExpr.callFn(
[o.THIS_EXPR, renderNode, bindingField.expression, emptyStateValue]))
.toDeclStmt()); .toDeclStmt());
eventListeners.forEach(listener => { eventListeners.forEach(listener => {
@ -138,8 +108,8 @@ function bindAndWriteToRenderer(
break; break;
} }
compileMethod.addStmts(createCheckBindingStmt(
createCheckBindingStmt(view, evalResult, fieldExpr, updateStmts, compileMethod); evalResult, bindingField.expression, DetectChangesVars.throwOnChange, updateStmts));
}); });
} }
@ -158,15 +128,15 @@ export function bindDirectiveHostProps(
} }
export function bindDirectiveInputs( export function bindDirectiveInputs(
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, dirIndex: number,
compileElement: CompileElement) { compileElement: CompileElement) {
var view = compileElement.view; var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod; var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst); detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
directiveAst.inputs.forEach((input) => { directiveAst.inputs.forEach((input, inputIdx) => {
const bindingId = `${view.bindings.length}`; // Note: We can't use `fields.length` here, as we are not adding a field!
view.bindings.push(new CompileBinding(compileElement, input)); const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input); detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
const evalResult = const evalResult =
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId); convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);

View File

@ -46,11 +46,11 @@ class ViewBinderVisitor implements TemplateAstVisitor {
}); });
bindRenderInputs(ast.inputs, compileElement, eventListeners); bindRenderInputs(ast.inputs, compileElement, eventListeners);
bindRenderOutputs(eventListeners); bindRenderOutputs(eventListeners);
ast.directives.forEach((directiveAst) => { ast.directives.forEach((directiveAst, dirIndex) => {
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference); var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
var directiveWrapperInstance = var directiveWrapperInstance =
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference); compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement); bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners); bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners); bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
@ -75,11 +75,11 @@ class ViewBinderVisitor implements TemplateAstVisitor {
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any { visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++]; var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement); 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 directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
var directiveWrapperInstance = var directiveWrapperInstance =
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference); compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement); bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners); bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
bindDirectiveAfterContentLifecycleCallbacks( bindDirectiveAfterContentLifecycleCallbacks(