refactor(compiler): extract expression evaluation and writing to renderer from view_compiler
This is needed to that `DirectiveWrapper`s can also use them later on. Part of #11683
This commit is contained in:
@ -65,6 +65,8 @@ export class CompileMethod {
|
||||
this._newState = new _DebugState(nodeIndex, templateAst);
|
||||
}
|
||||
|
||||
push(...stmts: o.Statement[]) { this.addStmts(stmts); }
|
||||
|
||||
addStmt(stmt: o.Statement) {
|
||||
this._updateDebugContextIfNeeded();
|
||||
this._bodyStatements.push(stmt);
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
|
||||
import {CompilePipeMetadata} from '../compile_metadata';
|
||||
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
import {createPureProxy, getPropertyInView, injectFromViewParentInjector} from './util';
|
||||
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||
|
||||
export class CompilePipe {
|
||||
static call(view: CompileView, name: string, args: o.Expression[]): o.Expression {
|
||||
@ -65,7 +66,8 @@ export class CompilePipe {
|
||||
createPureProxy(
|
||||
pipeInstanceSeenFromPureProxy.prop('transform')
|
||||
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
|
||||
args.length, purePipeProxyInstance, callingView);
|
||||
args.length, purePipeProxyInstance,
|
||||
{fields: callingView.fields, ctorStmts: callingView.createMethod});
|
||||
return o.importExpr(resolveIdentifier(Identifiers.castByValue))
|
||||
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
|
||||
.callFn(args);
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
|
||||
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {MapWrapper} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {ClassBuilder} from '../output/class_builder';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ViewType} from '../private_import_core';
|
||||
|
||||
@ -21,11 +22,9 @@ import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompilePipe} from './compile_pipe';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
import {EventHandlerVars} from './constants';
|
||||
import {NameResolver} from './expression_converter';
|
||||
import {createPureProxy, getPropertyInView, getViewFactoryName} from './util';
|
||||
import {getPropertyInView, getViewFactoryName} from './util';
|
||||
|
||||
export class CompileView implements NameResolver, ClassBuilder {
|
||||
export class CompileView implements NameResolver {
|
||||
public viewType: ViewType;
|
||||
public viewQueries: Map<any, CompileQuery[]>;
|
||||
|
||||
@ -150,48 +149,6 @@ export class CompileView implements NameResolver, ClassBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
createLiteralArray(values: o.Expression[]): o.Expression {
|
||||
if (values.length === 0) {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
||||
}
|
||||
var proxyExpr = o.THIS_EXPR.prop(`_arr_${this.literalArrayCount++}`);
|
||||
var proxyParams: o.FnParam[] = [];
|
||||
var proxyReturnEntries: o.Expression[] = [];
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var paramName = `p${i}`;
|
||||
proxyParams.push(new o.FnParam(paramName));
|
||||
proxyReturnEntries.push(o.variable(paramName));
|
||||
}
|
||||
createPureProxy(
|
||||
o.fn(
|
||||
proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))],
|
||||
new o.ArrayType(o.DYNAMIC_TYPE)),
|
||||
values.length, proxyExpr, this);
|
||||
return proxyExpr.callFn(values);
|
||||
}
|
||||
|
||||
createLiteralMap(entries: [string, o.Expression][]): o.Expression {
|
||||
if (entries.length === 0) {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
||||
}
|
||||
const proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
|
||||
const proxyParams: o.FnParam[] = [];
|
||||
const proxyReturnEntries: [string, o.Expression][] = [];
|
||||
const values: o.Expression[] = [];
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
const paramName = `p${i}`;
|
||||
proxyParams.push(new o.FnParam(paramName));
|
||||
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
||||
values.push(<o.Expression>entries[i][1]);
|
||||
}
|
||||
createPureProxy(
|
||||
o.fn(
|
||||
proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))],
|
||||
new o.MapType(o.DYNAMIC_TYPE)),
|
||||
entries.length, proxyExpr, this);
|
||||
return proxyExpr.callFn(values);
|
||||
}
|
||||
|
||||
afterNodes() {
|
||||
MapWrapper.values(this.viewQueries)
|
||||
.forEach(
|
||||
|
@ -98,8 +98,6 @@ export class ViewProperties {
|
||||
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
||||
}
|
||||
|
||||
export class EventHandlerVars { static event = o.variable('$event'); }
|
||||
|
||||
export class InjectMethodVars {
|
||||
static token = o.variable('token');
|
||||
static requestNodeIndex = o.variable('requestNodeIndex');
|
||||
@ -110,5 +108,4 @@ export class DetectChangesVars {
|
||||
static throwOnChange = o.variable(`throwOnChange`);
|
||||
static changes = o.variable(`changes`);
|
||||
static changed = o.variable(`changed`);
|
||||
static valUnwrapper = o.variable(`valUnwrapper`);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {identifierToken} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
@ -15,8 +16,7 @@ import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||
import {CompileBinding} from './compile_binding';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {EventHandlerVars, ViewProperties} from './constants';
|
||||
import {NoLocalsNameResolver, convertCdStatementToIr} from './expression_converter';
|
||||
import {ViewProperties} from './constants';
|
||||
|
||||
export class CompileEventListener {
|
||||
private _method: CompileMethod;
|
||||
@ -61,24 +61,14 @@ export class CompileEventListener {
|
||||
}
|
||||
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
|
||||
var context = directiveInstance || this.compileElement.view.componentContext;
|
||||
var actionStmts = convertCdStatementToIr(
|
||||
directive ? new NoLocalsNameResolver(this.compileElement.view) : this.compileElement.view,
|
||||
context, hostEvent.handler, this.compileElement.nodeIndex);
|
||||
var lastIndex = actionStmts.length - 1;
|
||||
if (lastIndex >= 0) {
|
||||
var lastStatement = actionStmts[lastIndex];
|
||||
var returnExpr = convertStmtIntoExpression(lastStatement);
|
||||
var preventDefaultVar = o.variable(`pd_${this._actionResultExprs.length}`);
|
||||
this._actionResultExprs.push(preventDefaultVar);
|
||||
if (isPresent(returnExpr)) {
|
||||
// Note: We need to cast the result of the method call to dynamic,
|
||||
// as it might be a void method!
|
||||
actionStmts[lastIndex] =
|
||||
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||
}
|
||||
const view = this.compileElement.view;
|
||||
const evalResult = convertActionBinding(
|
||||
view, directive ? null : view, context, hostEvent.handler,
|
||||
`${this.compileElement.nodeIndex}_${this._actionResultExprs.length}`);
|
||||
if (evalResult.preventDefault) {
|
||||
this._actionResultExprs.push(evalResult.preventDefault);
|
||||
}
|
||||
this._method.addStmts(actionStmts);
|
||||
this._method.addStmts(evalResult.stmts);
|
||||
}
|
||||
|
||||
finishMethod() {
|
||||
|
@ -1,503 +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 * as cdAst from '../expression_parser/ast';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {EventHandlerVars} from './constants';
|
||||
|
||||
export interface NameResolver {
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||
getLocal(name: string): o.Expression;
|
||||
createLiteralArray(values: o.Expression[]): o.Expression;
|
||||
createLiteralMap(values: Array<Array<string|o.Expression>>): o.Expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around another NameResolver that removes all locals and pipes.
|
||||
*/
|
||||
export class NoLocalsNameResolver implements NameResolver {
|
||||
constructor(private _delegate: NameResolver) {}
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression { return null; }
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name == EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
createLiteralArray(values: o.Expression[]): o.Expression {
|
||||
return this._delegate.createLiteralArray(values);
|
||||
}
|
||||
createLiteralMap(values: Array<Array<string|o.Expression>>): o.Expression {
|
||||
return this._delegate.createLiteralMap(values);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpressionWithWrappedValueInfo {
|
||||
constructor(
|
||||
public expression: o.Expression, public needsValueUnwrapper: boolean,
|
||||
public temporaryCount: number) {}
|
||||
}
|
||||
|
||||
export function convertCdExpressionToIr(
|
||||
nameResolver: NameResolver, implicitReceiver: o.Expression, expression: cdAst.AST,
|
||||
valueUnwrapper: o.ReadVarExpr, bindingIndex: number): ExpressionWithWrappedValueInfo {
|
||||
const visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, valueUnwrapper, bindingIndex);
|
||||
const irAst: o.Expression = expression.visit(visitor, _Mode.Expression);
|
||||
return new ExpressionWithWrappedValueInfo(
|
||||
irAst, visitor.needsValueUnwrapper, visitor.temporaryCount);
|
||||
}
|
||||
|
||||
export function convertCdStatementToIr(
|
||||
nameResolver: NameResolver, implicitReceiver: o.Expression, stmt: cdAst.AST,
|
||||
bindingIndex: number): o.Statement[] {
|
||||
const visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, null, bindingIndex);
|
||||
let statements: o.Statement[] = [];
|
||||
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements);
|
||||
prependTemporaryDecls(visitor.temporaryCount, bindingIndex, statements);
|
||||
return statements;
|
||||
}
|
||||
|
||||
function temporaryName(bindingIndex: number, temporaryNumber: number): string {
|
||||
return `tmp_${bindingIndex}_${temporaryNumber}`;
|
||||
}
|
||||
|
||||
export function temporaryDeclaration(bindingIndex: number, temporaryNumber: number): o.Statement {
|
||||
return new o.DeclareVarStmt(temporaryName(bindingIndex, temporaryNumber), o.NULL_EXPR);
|
||||
}
|
||||
|
||||
function prependTemporaryDecls(
|
||||
temporaryCount: number, bindingIndex: number, statements: o.Statement[]) {
|
||||
for (let i = temporaryCount - 1; i >= 0; i--) {
|
||||
statements.unshift(temporaryDeclaration(bindingIndex, i));
|
||||
}
|
||||
}
|
||||
|
||||
enum _Mode {
|
||||
Statement,
|
||||
Expression
|
||||
}
|
||||
|
||||
function ensureStatementMode(mode: _Mode, ast: cdAst.AST) {
|
||||
if (mode !== _Mode.Statement) {
|
||||
throw new Error(`Expected a statement, but saw ${ast}`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureExpressionMode(mode: _Mode, ast: cdAst.AST) {
|
||||
if (mode !== _Mode.Expression) {
|
||||
throw new Error(`Expected an expression, but saw ${ast}`);
|
||||
}
|
||||
}
|
||||
|
||||
function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expression|o.Statement {
|
||||
if (mode === _Mode.Statement) {
|
||||
return expr.toStmt();
|
||||
} else {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
private _nodeMap = new Map<cdAst.AST, cdAst.AST>();
|
||||
private _resultMap = new Map<cdAst.AST, o.Expression>();
|
||||
private _currentTemporary: number = 0;
|
||||
public needsValueUnwrapper: boolean = false;
|
||||
public temporaryCount: number = 0;
|
||||
|
||||
constructor(
|
||||
private _nameResolver: NameResolver, private _implicitReceiver: o.Expression,
|
||||
private _valueUnwrapper: o.ReadVarExpr, private bindingIndex: number) {}
|
||||
|
||||
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
||||
var op: o.BinaryOperator;
|
||||
switch (ast.operation) {
|
||||
case '+':
|
||||
op = o.BinaryOperator.Plus;
|
||||
break;
|
||||
case '-':
|
||||
op = o.BinaryOperator.Minus;
|
||||
break;
|
||||
case '*':
|
||||
op = o.BinaryOperator.Multiply;
|
||||
break;
|
||||
case '/':
|
||||
op = o.BinaryOperator.Divide;
|
||||
break;
|
||||
case '%':
|
||||
op = o.BinaryOperator.Modulo;
|
||||
break;
|
||||
case '&&':
|
||||
op = o.BinaryOperator.And;
|
||||
break;
|
||||
case '||':
|
||||
op = o.BinaryOperator.Or;
|
||||
break;
|
||||
case '==':
|
||||
op = o.BinaryOperator.Equals;
|
||||
break;
|
||||
case '!=':
|
||||
op = o.BinaryOperator.NotEquals;
|
||||
break;
|
||||
case '===':
|
||||
op = o.BinaryOperator.Identical;
|
||||
break;
|
||||
case '!==':
|
||||
op = o.BinaryOperator.NotIdentical;
|
||||
break;
|
||||
case '<':
|
||||
op = o.BinaryOperator.Lower;
|
||||
break;
|
||||
case '>':
|
||||
op = o.BinaryOperator.Bigger;
|
||||
break;
|
||||
case '<=':
|
||||
op = o.BinaryOperator.LowerEquals;
|
||||
break;
|
||||
case '>=':
|
||||
op = o.BinaryOperator.BiggerEquals;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported operation ${ast.operation}`);
|
||||
}
|
||||
|
||||
return convertToStatementIfNeeded(
|
||||
mode,
|
||||
new o.BinaryOperatorExpr(
|
||||
op, this.visit(ast.left, _Mode.Expression), this.visit(ast.right, _Mode.Expression)));
|
||||
}
|
||||
|
||||
visitChain(ast: cdAst.Chain, mode: _Mode): any {
|
||||
ensureStatementMode(mode, ast);
|
||||
return this.visitAll(ast.expressions, mode);
|
||||
}
|
||||
|
||||
visitConditional(ast: cdAst.Conditional, mode: _Mode): any {
|
||||
const value: o.Expression = this.visit(ast.condition, _Mode.Expression);
|
||||
return convertToStatementIfNeeded(
|
||||
mode,
|
||||
value.conditional(
|
||||
this.visit(ast.trueExp, _Mode.Expression), this.visit(ast.falseExp, _Mode.Expression)));
|
||||
}
|
||||
|
||||
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
|
||||
const input = this.visit(ast.exp, _Mode.Expression);
|
||||
const args = this.visitAll(ast.args, _Mode.Expression);
|
||||
const value = this._nameResolver.callPipe(ast.name, input, args);
|
||||
if (!value) {
|
||||
throw new Error(`Illegal state: Pipe ${ast.name} is not allowed here!`);
|
||||
}
|
||||
this.needsValueUnwrapper = true;
|
||||
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
|
||||
}
|
||||
|
||||
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode,
|
||||
this.visit(ast.target, _Mode.Expression).callFn(this.visitAll(ast.args, _Mode.Expression)));
|
||||
}
|
||||
|
||||
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
|
||||
ensureExpressionMode(mode, ast);
|
||||
return this._implicitReceiver;
|
||||
}
|
||||
|
||||
visitInterpolation(ast: cdAst.Interpolation, mode: _Mode): any {
|
||||
ensureExpressionMode(mode, ast);
|
||||
const args = [o.literal(ast.expressions.length)];
|
||||
for (let i = 0; i < ast.strings.length - 1; i++) {
|
||||
args.push(o.literal(ast.strings[i]));
|
||||
args.push(this.visit(ast.expressions[i], _Mode.Expression));
|
||||
}
|
||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||
return o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn(args);
|
||||
}
|
||||
|
||||
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
||||
}
|
||||
|
||||
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
||||
const obj: o.Expression = this.visit(ast.obj, _Mode.Expression);
|
||||
const key: o.Expression = this.visit(ast.key, _Mode.Expression);
|
||||
const value: o.Expression = this.visit(ast.value, _Mode.Expression);
|
||||
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
||||
}
|
||||
|
||||
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
|
||||
}
|
||||
|
||||
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||
let parts: any[] = [];
|
||||
for (let i = 0; i < ast.keys.length; i++) {
|
||||
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
|
||||
}
|
||||
|
||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
||||
}
|
||||
|
||||
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||
if (leftMostSafe) {
|
||||
return this.convertSafeAccess(ast, leftMostSafe, mode);
|
||||
} else {
|
||||
const args = this.visitAll(ast.args, _Mode.Expression);
|
||||
let result: any = null;
|
||||
let receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||
if (receiver === this._implicitReceiver) {
|
||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||
if (isPresent(varExpr)) {
|
||||
result = varExpr.callFn(args);
|
||||
}
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
result = receiver.callMethod(ast.name, args);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, result);
|
||||
}
|
||||
}
|
||||
|
||||
visitPrefixNot(ast: cdAst.PrefixNot, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(mode, o.not(this.visit(ast.expression, _Mode.Expression)));
|
||||
}
|
||||
|
||||
visitPropertyRead(ast: cdAst.PropertyRead, mode: _Mode): any {
|
||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||
if (leftMostSafe) {
|
||||
return this.convertSafeAccess(ast, leftMostSafe, mode);
|
||||
} else {
|
||||
let result: any = null;
|
||||
var receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||
if (receiver === this._implicitReceiver) {
|
||||
result = this._nameResolver.getLocal(ast.name);
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
result = receiver.prop(ast.name);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, result);
|
||||
}
|
||||
}
|
||||
|
||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
||||
if (receiver === this._implicitReceiver) {
|
||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||
if (isPresent(varExpr)) {
|
||||
throw new Error('Cannot assign to a reference or variable!');
|
||||
}
|
||||
}
|
||||
return convertToStatementIfNeeded(
|
||||
mode, receiver.prop(ast.name).set(this.visit(ast.value, _Mode.Expression)));
|
||||
}
|
||||
|
||||
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
||||
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
||||
}
|
||||
|
||||
visitSafeMethodCall(ast: cdAst.SafeMethodCall, mode: _Mode): any {
|
||||
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
||||
}
|
||||
|
||||
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => this.visit(ast, mode)); }
|
||||
|
||||
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
|
||||
throw new Error('Quotes are not supported for evaluation!');
|
||||
}
|
||||
|
||||
private visit(ast: cdAst.AST, mode: _Mode): any {
|
||||
const result = this._resultMap.get(ast);
|
||||
if (result) return result;
|
||||
return (this._nodeMap.get(ast) || ast).visit(this, mode);
|
||||
}
|
||||
|
||||
private convertSafeAccess(
|
||||
ast: cdAst.AST, leftMostSafe: cdAst.SafeMethodCall|cdAst.SafePropertyRead, mode: _Mode): any {
|
||||
// If the expression contains a safe access node on the left it needs to be converted to
|
||||
// an expression that guards the access to the member by checking the receiver for blank. As
|
||||
// execution proceeds from left to right, the left most part of the expression must be guarded
|
||||
// first but, because member access is left associative, the right side of the expression is at
|
||||
// the top of the AST. The desired result requires lifting a copy of the the left part of the
|
||||
// expression up to test it for blank before generating the unguarded version.
|
||||
|
||||
// Consider, for example the following expression: a?.b.c?.d.e
|
||||
|
||||
// This results in the ast:
|
||||
// .
|
||||
// / \
|
||||
// ?. e
|
||||
// / \
|
||||
// . d
|
||||
// / \
|
||||
// ?. c
|
||||
// / \
|
||||
// a b
|
||||
|
||||
// The following tree should be generated:
|
||||
//
|
||||
// /---- ? ----\
|
||||
// / | \
|
||||
// a /--- ? ---\ null
|
||||
// / | \
|
||||
// . . null
|
||||
// / \ / \
|
||||
// . c . e
|
||||
// / \ / \
|
||||
// a b , d
|
||||
// / \
|
||||
// . c
|
||||
// / \
|
||||
// a b
|
||||
//
|
||||
// Notice that the first guard condition is the left hand of the left most safe access node
|
||||
// which comes in as leftMostSafe to this routine.
|
||||
|
||||
let guardedExpression = this.visit(leftMostSafe.receiver, _Mode.Expression);
|
||||
let temporary: o.ReadVarExpr;
|
||||
if (this.needsTemporary(leftMostSafe.receiver)) {
|
||||
// If the expression has method calls or pipes then we need to save the result into a
|
||||
// temporary variable to avoid calling stateful or impure code more than once.
|
||||
temporary = this.allocateTemporary();
|
||||
|
||||
// Preserve the result in the temporary variable
|
||||
guardedExpression = temporary.set(guardedExpression);
|
||||
|
||||
// Ensure all further references to the guarded expression refer to the temporary instead.
|
||||
this._resultMap.set(leftMostSafe.receiver, temporary);
|
||||
}
|
||||
const condition = guardedExpression.isBlank();
|
||||
|
||||
// Convert the ast to an unguarded access to the receiver's member. The map will substitute
|
||||
// leftMostNode with its unguarded version in the call to `this.visit()`.
|
||||
if (leftMostSafe instanceof cdAst.SafeMethodCall) {
|
||||
this._nodeMap.set(
|
||||
leftMostSafe,
|
||||
new cdAst.MethodCall(
|
||||
leftMostSafe.span, leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args));
|
||||
} else {
|
||||
this._nodeMap.set(
|
||||
leftMostSafe,
|
||||
new cdAst.PropertyRead(leftMostSafe.span, leftMostSafe.receiver, leftMostSafe.name));
|
||||
}
|
||||
|
||||
// Recursively convert the node now without the guarded member access.
|
||||
const access = this.visit(ast, _Mode.Expression);
|
||||
|
||||
// Remove the mapping. This is not strictly required as the converter only traverses each node
|
||||
// once but is safer if the conversion is changed to traverse the nodes more than once.
|
||||
this._nodeMap.delete(leftMostSafe);
|
||||
|
||||
// If we allcoated a temporary, release it.
|
||||
if (temporary) {
|
||||
this.releaseTemporary(temporary);
|
||||
}
|
||||
|
||||
// Produce the conditional
|
||||
return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access));
|
||||
}
|
||||
|
||||
// Given a expression of the form a?.b.c?.d.e the the left most safe node is
|
||||
// the (a?.b). The . and ?. are left associative thus can be rewritten as:
|
||||
// ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
|
||||
// safe method call as this needs be transform initially to:
|
||||
// a == null ? null : a.c.b.c?.d.e
|
||||
// then to:
|
||||
// a == null ? null : a.b.c == null ? null : a.b.c.d.e
|
||||
private leftMostSafeNode(ast: cdAst.AST): cdAst.SafePropertyRead|cdAst.SafeMethodCall {
|
||||
const visit = (visitor: cdAst.AstVisitor, ast: cdAst.AST): any => {
|
||||
return (this._nodeMap.get(ast) || ast).visit(visitor);
|
||||
};
|
||||
return ast.visit({
|
||||
visitBinary(ast: cdAst.Binary) { return null; },
|
||||
visitChain(ast: cdAst.Chain) { return null; },
|
||||
visitConditional(ast: cdAst.Conditional) { return null; },
|
||||
visitFunctionCall(ast: cdAst.FunctionCall) { return null; },
|
||||
visitImplicitReceiver(ast: cdAst.ImplicitReceiver) { return null; },
|
||||
visitInterpolation(ast: cdAst.Interpolation) { return null; },
|
||||
visitKeyedRead(ast: cdAst.KeyedRead) { return visit(this, ast.obj); },
|
||||
visitKeyedWrite(ast: cdAst.KeyedWrite) { return null; },
|
||||
visitLiteralArray(ast: cdAst.LiteralArray) { return null; },
|
||||
visitLiteralMap(ast: cdAst.LiteralMap) { return null; },
|
||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive) { return null; },
|
||||
visitMethodCall(ast: cdAst.MethodCall) { return visit(this, ast.receiver); },
|
||||
visitPipe(ast: cdAst.BindingPipe) { return null; },
|
||||
visitPrefixNot(ast: cdAst.PrefixNot) { return null; },
|
||||
visitPropertyRead(ast: cdAst.PropertyRead) { return visit(this, ast.receiver); },
|
||||
visitPropertyWrite(ast: cdAst.PropertyWrite) { return null; },
|
||||
visitQuote(ast: cdAst.Quote) { return null; },
|
||||
visitSafeMethodCall(ast: cdAst.SafeMethodCall) { return visit(this, ast.receiver) || ast; },
|
||||
visitSafePropertyRead(ast: cdAst.SafePropertyRead) {
|
||||
return visit(this, ast.receiver) || ast;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Returns true of the AST includes a method or a pipe indicating that, if the
|
||||
// expression is used as the target of a safe property or method access then
|
||||
// the expression should be stored into a temporary variable.
|
||||
private needsTemporary(ast: cdAst.AST): boolean {
|
||||
const visit = (visitor: cdAst.AstVisitor, ast: cdAst.AST): boolean => {
|
||||
return ast && (this._nodeMap.get(ast) || ast).visit(visitor);
|
||||
};
|
||||
const visitSome = (visitor: cdAst.AstVisitor, ast: cdAst.AST[]): boolean => {
|
||||
return ast.some(ast => visit(visitor, ast));
|
||||
};
|
||||
return ast.visit({
|
||||
visitBinary(ast: cdAst.Binary):
|
||||
boolean{return visit(this, ast.left) || visit(this, ast.right);},
|
||||
visitChain(ast: cdAst.Chain) { return false; },
|
||||
visitConditional(ast: cdAst.Conditional):
|
||||
boolean{return visit(this, ast.condition) || visit(this, ast.trueExp) ||
|
||||
visit(this, ast.falseExp);},
|
||||
visitFunctionCall(ast: cdAst.FunctionCall) { return true; },
|
||||
visitImplicitReceiver(ast: cdAst.ImplicitReceiver) { return false; },
|
||||
visitInterpolation(ast: cdAst.Interpolation) { return visitSome(this, ast.expressions); },
|
||||
visitKeyedRead(ast: cdAst.KeyedRead) { return false; },
|
||||
visitKeyedWrite(ast: cdAst.KeyedWrite) { return false; },
|
||||
visitLiteralArray(ast: cdAst.LiteralArray) { return true; },
|
||||
visitLiteralMap(ast: cdAst.LiteralMap) { return true; },
|
||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive) { return false; },
|
||||
visitMethodCall(ast: cdAst.MethodCall) { return true; },
|
||||
visitPipe(ast: cdAst.BindingPipe) { return true; },
|
||||
visitPrefixNot(ast: cdAst.PrefixNot) { return visit(this, ast.expression); },
|
||||
visitPropertyRead(ast: cdAst.PropertyRead) { return false; },
|
||||
visitPropertyWrite(ast: cdAst.PropertyWrite) { return false; },
|
||||
visitQuote(ast: cdAst.Quote) { return false; },
|
||||
visitSafeMethodCall(ast: cdAst.SafeMethodCall) { return true; },
|
||||
visitSafePropertyRead(ast: cdAst.SafePropertyRead) { return false; }
|
||||
});
|
||||
}
|
||||
|
||||
private allocateTemporary(): o.ReadVarExpr {
|
||||
const tempNumber = this._currentTemporary++;
|
||||
this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
|
||||
return new o.ReadVarExpr(temporaryName(this.bindingIndex, tempNumber));
|
||||
}
|
||||
|
||||
private releaseTemporary(temporary: o.ReadVarExpr) {
|
||||
this._currentTemporary--;
|
||||
if (temporary.name != temporaryName(this.bindingIndex, this._currentTemporary)) {
|
||||
throw new Error(`Temporary ${temporary.name} released out of order`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function flattenStatements(arg: any, output: o.Statement[]) {
|
||||
if (Array.isArray(arg)) {
|
||||
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
|
||||
} else {
|
||||
output.push(arg);
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@
|
||||
|
||||
import {SecurityContext} from '@angular/core';
|
||||
|
||||
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
|
||||
import {writeToRenderer} from '../compiler_util/render_util';
|
||||
import * as cdAst from '../expression_parser/ast';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
@ -22,60 +24,14 @@ import {CompileMethod} from './compile_method';
|
||||
import {CompileView} from './compile_view';
|
||||
import {DetectChangesVars, ViewProperties} from './constants';
|
||||
import {CompileEventListener} from './event_binder';
|
||||
import {NameResolver, NoLocalsNameResolver, convertCdExpressionToIr, temporaryDeclaration} from './expression_converter';
|
||||
|
||||
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
|
||||
return o.THIS_EXPR.prop(`_expr_${exprIndex}`);
|
||||
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
|
||||
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
|
||||
}
|
||||
|
||||
function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
|
||||
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
|
||||
}
|
||||
|
||||
class EvalResult {
|
||||
constructor(public forceUpdate: o.Expression) {}
|
||||
}
|
||||
|
||||
function evalCdAst(
|
||||
view: CompileView, currValExpr: o.ReadVarExpr, parsedExpression: cdAst.AST,
|
||||
context: o.Expression, nameResolver: NameResolver, method: CompileMethod,
|
||||
bindingIndex: number): EvalResult {
|
||||
var checkExpression = convertCdExpressionToIr(
|
||||
nameResolver, context, parsedExpression, DetectChangesVars.valUnwrapper, bindingIndex);
|
||||
if (!checkExpression.expression) {
|
||||
// e.g. an empty expression was given
|
||||
return null;
|
||||
}
|
||||
|
||||
if (checkExpression.temporaryCount) {
|
||||
for (let i = 0; i < checkExpression.temporaryCount; i++) {
|
||||
method.addStmt(temporaryDeclaration(bindingIndex, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (checkExpression.needsValueUnwrapper) {
|
||||
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
|
||||
method.addStmt(initValueUnwrapperStmt);
|
||||
}
|
||||
method.addStmt(
|
||||
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
if (checkExpression.needsValueUnwrapper) {
|
||||
return new EvalResult(DetectChangesVars.valUnwrapper.prop('hasWrappedValue'));
|
||||
} else {
|
||||
return new EvalResult(null);
|
||||
}
|
||||
}
|
||||
|
||||
function bind(
|
||||
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
||||
parsedExpression: cdAst.AST, context: o.Expression, nameResolver: NameResolver,
|
||||
actions: o.Statement[], method: CompileMethod, bindingIndex: number) {
|
||||
const evalResult =
|
||||
evalCdAst(view, currValExpr, parsedExpression, context, nameResolver, method, bindingIndex);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -83,30 +39,36 @@ function bind(
|
||||
.toStmt());
|
||||
|
||||
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
||||
DetectChangesVars.throwOnChange, fieldExpr, currValExpr
|
||||
DetectChangesVars.throwOnChange, fieldExpr, evalResult.currValExpr
|
||||
]);
|
||||
if (evalResult.forceUpdate) {
|
||||
condition = evalResult.forceUpdate.or(condition);
|
||||
}
|
||||
method.addStmt(new o.IfStmt(
|
||||
condition,
|
||||
actions.concat([<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(currValExpr).toStmt()])));
|
||||
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(
|
||||
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView) {
|
||||
var bindingIndex = view.bindings.length;
|
||||
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
|
||||
var bindingId = `${view.bindings.length}`;
|
||||
view.bindings.push(new CompileBinding(compileNode, boundText));
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
var valueField = createBindFieldExpr(bindingIndex);
|
||||
const evalResult =
|
||||
convertPropertyBinding(view, view, view.componentContext, boundText.value, bindingId);
|
||||
if (!evalResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var valueField = createBindFieldExpr(bindingId);
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
||||
|
||||
bind(
|
||||
view, currValExpr, valueField, boundText.value, view.componentContext, view,
|
||||
createCheckBindingStmt(
|
||||
view, evalResult, valueField,
|
||||
[o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setText', [compileNode.renderNode, currValExpr])
|
||||
.callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
|
||||
.toStmt()],
|
||||
view.detectChangesRenderPropertiesMethod, bindingIndex);
|
||||
view.detectChangesRenderPropertiesMethod);
|
||||
}
|
||||
|
||||
function bindAndWriteToRenderer(
|
||||
@ -115,52 +77,22 @@ function bindAndWriteToRenderer(
|
||||
var view = compileElement.view;
|
||||
var renderNode = compileElement.renderNode;
|
||||
boundProps.forEach((boundProp) => {
|
||||
var bindingIndex = view.bindings.length;
|
||||
const bindingId = `${view.bindings.length}`;
|
||||
view.bindings.push(new CompileBinding(compileElement, boundProp));
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
|
||||
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
|
||||
var fieldExpr = createBindFieldExpr(bindingId);
|
||||
const evalResult =
|
||||
convertPropertyBinding(view, isHostProp ? null : view, context, boundProp.value, bindingId);
|
||||
var updateStmts: o.Statement[] = [];
|
||||
var compileMethod = view.detectChangesRenderPropertiesMethod;
|
||||
switch (boundProp.type) {
|
||||
case PropertyBindingType.Property:
|
||||
if (view.genConfig.logBindingUpdate) {
|
||||
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, renderValue));
|
||||
}
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod(
|
||||
'setElementProperty', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Attribute:
|
||||
renderValue =
|
||||
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod(
|
||||
'setElementAttribute', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Class:
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setElementClass', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Style:
|
||||
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||
if (isPresent(boundProp.unit)) {
|
||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||
}
|
||||
|
||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setElementStyle', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
updateStmts.push(...writeToRenderer(
|
||||
o.THIS_EXPR, boundProp, renderNode, evalResult.currValExpr,
|
||||
view.genConfig.logBindingUpdate));
|
||||
break;
|
||||
case PropertyBindingType.Animation:
|
||||
compileMethod = view.animationBindingsMethod;
|
||||
@ -182,16 +114,17 @@ function bindAndWriteToRenderer(
|
||||
updateStmts.push(
|
||||
animationTransitionVar
|
||||
.set(animationFnExpr.callFn([
|
||||
o.THIS_EXPR, renderNode, oldRenderValue.equals(unitializedValue)
|
||||
.conditional(emptyStateValue, oldRenderValue),
|
||||
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
||||
o.THIS_EXPR, renderNode,
|
||||
fieldExpr.equals(unitializedValue).conditional(emptyStateValue, fieldExpr),
|
||||
evalResult.currValExpr.equals(unitializedValue)
|
||||
.conditional(emptyStateValue, evalResult.currValExpr)
|
||||
]))
|
||||
.toDeclStmt());
|
||||
|
||||
detachStmts.push(animationTransitionVar
|
||||
.set(animationFnExpr.callFn(
|
||||
[o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue]))
|
||||
.toDeclStmt());
|
||||
detachStmts.push(
|
||||
animationTransitionVar
|
||||
.set(animationFnExpr.callFn([o.THIS_EXPR, renderNode, fieldExpr, emptyStateValue]))
|
||||
.toDeclStmt());
|
||||
|
||||
eventListeners.forEach(listener => {
|
||||
if (listener.isAnimation && listener.eventName === animationName) {
|
||||
@ -206,43 +139,10 @@ function bindAndWriteToRenderer(
|
||||
break;
|
||||
}
|
||||
|
||||
bind(
|
||||
view, currValExpr, fieldExpr, boundProp.value, context,
|
||||
isHostProp ? new NoLocalsNameResolver(view) : view, updateStmts, compileMethod,
|
||||
view.bindings.length);
|
||||
createCheckBindingStmt(view, evalResult, fieldExpr, updateStmts, compileMethod);
|
||||
});
|
||||
}
|
||||
|
||||
function sanitizedValue(
|
||||
boundProp: BoundElementPropertyAst, renderValue: o.Expression): o.Expression {
|
||||
let enumValue: string;
|
||||
switch (boundProp.securityContext) {
|
||||
case SecurityContext.NONE:
|
||||
return renderValue; // No sanitization needed.
|
||||
case SecurityContext.HTML:
|
||||
enumValue = 'HTML';
|
||||
break;
|
||||
case SecurityContext.STYLE:
|
||||
enumValue = 'STYLE';
|
||||
break;
|
||||
case SecurityContext.SCRIPT:
|
||||
enumValue = 'SCRIPT';
|
||||
break;
|
||||
case SecurityContext.URL:
|
||||
enumValue = 'URL';
|
||||
break;
|
||||
case SecurityContext.RESOURCE_URL:
|
||||
enumValue = 'RESOURCE_URL';
|
||||
break;
|
||||
default:
|
||||
throw new Error(`internal error, unexpected SecurityContext ${boundProp.securityContext}.`);
|
||||
}
|
||||
let ctx = ViewProperties.viewUtils.prop('sanitizer');
|
||||
let args =
|
||||
[o.importExpr(resolveIdentifier(Identifiers.SecurityContext)).prop(enumValue), renderValue];
|
||||
return ctx.callMethod('sanitize', args);
|
||||
}
|
||||
|
||||
export function bindRenderInputs(
|
||||
boundProps: BoundElementPropertyAst[], compileElement: CompileElement,
|
||||
eventListeners: CompileEventListener[]): void {
|
||||
@ -265,24 +165,24 @@ export function bindDirectiveInputs(
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
|
||||
directiveAst.inputs.forEach((input) => {
|
||||
var bindingIndex = view.bindings.length;
|
||||
const bindingId = `${view.bindings.length}`;
|
||||
view.bindings.push(new CompileBinding(compileElement, input));
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
const evalResult = evalCdAst(
|
||||
view, currValExpr, input.value, view.componentContext, view, detectChangesInInputsMethod,
|
||||
bindingIndex);
|
||||
const evalResult =
|
||||
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
}
|
||||
detectChangesInInputsMethod.addStmt(directiveWrapperInstance
|
||||
.callMethod(
|
||||
`check_${input.directiveName}`,
|
||||
[
|
||||
currValExpr, DetectChangesVars.throwOnChange,
|
||||
evalResult.forceUpdate || o.literal(false)
|
||||
])
|
||||
.toStmt());
|
||||
detectChangesInInputsMethod.addStmts(evalResult.stmts);
|
||||
detectChangesInInputsMethod.addStmt(
|
||||
directiveWrapperInstance
|
||||
.callMethod(
|
||||
`check_${input.directiveName}`,
|
||||
[
|
||||
evalResult.currValExpr, DetectChangesVars.throwOnChange,
|
||||
evalResult.forceUpdate || o.literal(false)
|
||||
])
|
||||
.toStmt());
|
||||
});
|
||||
var isOnPushComp = directiveAst.directive.isComponent &&
|
||||
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
||||
@ -296,10 +196,3 @@ export function bindDirectiveInputs(
|
||||
directiveDetectChangesExpr.toStmt();
|
||||
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt);
|
||||
}
|
||||
|
||||
function logBindingUpdateStmt(
|
||||
renderNode: o.Expression, propName: string, value: o.Expression): o.Statement {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
|
||||
.callFn([o.THIS_EXPR.prop('renderer'), renderNode, o.literal(propName), value])
|
||||
.toStmt();
|
||||
}
|
||||
|
@ -91,16 +91,3 @@ export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createPureProxy(
|
||||
fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr, view: CompileView) {
|
||||
view.fields.push(new o.ClassField(pureProxyProp.name, null));
|
||||
var pureProxyId =
|
||||
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
||||
if (!pureProxyId) {
|
||||
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||
}
|
||||
view.createMethod.addStmt(o.THIS_EXPR.prop(pureProxyProp.name)
|
||||
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
|
||||
.toStmt());
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
||||
import {createDiTokenExpression, createFastArray} from '../compiler_util/identifier_util';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
@ -586,12 +587,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||||
DetectChangesVars.changes.set(o.NULL_EXPR)
|
||||
.toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))));
|
||||
}
|
||||
if (readVars.has(DetectChangesVars.valUnwrapper.name)) {
|
||||
varStmts.push(
|
||||
DetectChangesVars.valUnwrapper
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
varStmts.push(...createSharedBindingVariablesIfNeeded(stmts));
|
||||
return varStmts.concat(stmts);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user