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:
Tobias Bosch 2016-10-19 13:18:33 -07:00 committed by vsavkin
parent cb7643ccea
commit 4cac650675
12 changed files with 374 additions and 305 deletions

View File

@ -10,73 +10,130 @@
import * as cdAst from '../expression_parser/ast'; import * as cdAst from '../expression_parser/ast';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {EventHandlerVars} from './constants';
import {createPureProxy} from './identifier_util';
const VAL_UNWRAPPER_VAR = o.variable(`valUnwrapper`);
export interface NameResolver { export interface NameResolver {
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression; callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
getLocal(name: string): o.Expression; getLocal(name: string): o.Expression;
createLiteralArray(values: o.Expression[]): o.Expression; }
createLiteralMap(values: Array<Array<string|o.Expression>>): o.Expression;
export class EventHandlerVars { static event = o.variable('$event'); }
export class ConvertPropertyBindingResult {
constructor(
public stmts: o.Statement[], public currValExpr: o.Expression,
public forceUpdate: o.Expression) {}
} }
/** /**
* A wrapper around another NameResolver that removes all locals and pipes. * Converts the given expression AST into an executable output AST, assuming the expression is
* used in a property binding.
*/ */
export class NoLocalsNameResolver implements NameResolver { export function convertPropertyBinding(
constructor(private _delegate: NameResolver) {} builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression { return null; } expression: cdAst.AST, bindingId: string): ConvertPropertyBindingResult {
getLocal(name: string): o.Expression { const currValExpr = createCurrValueExpr(bindingId);
if (name == EventHandlerVars.event.name) { const stmts: o.Statement[] = [];
return EventHandlerVars.event; if (!nameResolver) {
} nameResolver = new DefaultNameResolver();
}
const visitor = new _AstToIrVisitor(
builder, nameResolver, implicitReceiver, VAL_UNWRAPPER_VAR, bindingId, false);
const outputExpr: o.Expression = expression.visit(visitor, _Mode.Expression);
if (!outputExpr) {
// e.g. an empty expression was given
return null; return null;
} }
createLiteralArray(values: o.Expression[]): o.Expression {
return this._delegate.createLiteralArray(values); if (visitor.temporaryCount) {
for (let i = 0; i < visitor.temporaryCount; i++) {
stmts.push(temporaryDeclaration(bindingId, i));
}
} }
createLiteralMap(values: Array<Array<string|o.Expression>>): o.Expression {
return this._delegate.createLiteralMap(values); if (visitor.needsValueUnwrapper) {
var initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
stmts.push(initValueUnwrapperStmt);
}
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
if (visitor.needsValueUnwrapper) {
return new ConvertPropertyBindingResult(
stmts, currValExpr, VAL_UNWRAPPER_VAR.prop('hasWrappedValue'));
} else {
return new ConvertPropertyBindingResult(stmts, currValExpr, null);
} }
} }
export class ExpressionWithWrappedValueInfo { export class ConvertActionBindingResult {
constructor( constructor(public stmts: o.Statement[], public preventDefault: o.Expression) {}
public expression: o.Expression, public needsValueUnwrapper: boolean,
public temporaryCount: number) {}
} }
export function convertCdExpressionToIr( /**
nameResolver: NameResolver, implicitReceiver: o.Expression, expression: cdAst.AST, * Converts the given expression AST into an executable output AST, assuming the expression is
valueUnwrapper: o.ReadVarExpr, bindingIndex: number): ExpressionWithWrappedValueInfo { * used in an action binding (e.g. an event handler).
const visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, valueUnwrapper, bindingIndex); */
const irAst: o.Expression = expression.visit(visitor, _Mode.Expression); export function convertActionBinding(
return new ExpressionWithWrappedValueInfo( builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
irAst, visitor.needsValueUnwrapper, visitor.temporaryCount); action: cdAst.AST, bindingId: string): ConvertActionBindingResult {
if (!nameResolver) {
nameResolver = new DefaultNameResolver();
}
const visitor =
new _AstToIrVisitor(builder, nameResolver, implicitReceiver, null, bindingId, true);
let actionStmts: o.Statement[] = [];
flattenStatements(action.visit(visitor, _Mode.Statement), actionStmts);
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
var lastIndex = actionStmts.length - 1;
var preventDefaultVar: o.ReadVarExpr = null;
if (lastIndex >= 0) {
var lastStatement = actionStmts[lastIndex];
var returnExpr = convertStmtIntoExpression(lastStatement);
if (returnExpr) {
// Note: We need to cast the result of the method call to dynamic,
// as it might be a void method!
preventDefaultVar = createPreventDefaultVar(bindingId);
actionStmts[lastIndex] =
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
.toDeclStmt(null, [o.StmtModifier.Final]);
}
}
return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
} }
export function convertCdStatementToIr( /**
nameResolver: NameResolver, implicitReceiver: o.Expression, stmt: cdAst.AST, * Creates variables that are shared by multiple calls to `convertActionBinding` /
bindingIndex: number): o.Statement[] { * `convertPropertyBinding`
const visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, null, bindingIndex); */
let statements: o.Statement[] = []; export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements); const unwrapperStmts: o.Statement[] = [];
prependTemporaryDecls(visitor.temporaryCount, bindingIndex, statements); var readVars = o.findReadVarNames(stmts);
return statements; if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
unwrapperStmts.push(
VAL_UNWRAPPER_VAR
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
.toDeclStmt(null, [o.StmtModifier.Final]));
}
return unwrapperStmts;
} }
function temporaryName(bindingIndex: number, temporaryNumber: number): string { function temporaryName(bindingId: string, temporaryNumber: number): string {
return `tmp_${bindingIndex}_${temporaryNumber}`; return `tmp_${bindingId}_${temporaryNumber}`;
} }
export function temporaryDeclaration(bindingIndex: number, temporaryNumber: number): o.Statement { export function temporaryDeclaration(bindingId: string, temporaryNumber: number): o.Statement {
return new o.DeclareVarStmt(temporaryName(bindingIndex, temporaryNumber), o.NULL_EXPR); return new o.DeclareVarStmt(temporaryName(bindingId, temporaryNumber), o.NULL_EXPR);
} }
function prependTemporaryDecls( function prependTemporaryDecls(
temporaryCount: number, bindingIndex: number, statements: o.Statement[]) { temporaryCount: number, bindingId: string, statements: o.Statement[]) {
for (let i = temporaryCount - 1; i >= 0; i--) { for (let i = temporaryCount - 1; i >= 0; i--) {
statements.unshift(temporaryDeclaration(bindingIndex, i)); statements.unshift(temporaryDeclaration(bindingId, i));
} }
} }
@ -113,8 +170,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
public temporaryCount: number = 0; public temporaryCount: number = 0;
constructor( constructor(
private _nameResolver: NameResolver, private _implicitReceiver: o.Expression, private _builder: ClassBuilder, private _nameResolver: NameResolver,
private _valueUnwrapper: o.ReadVarExpr, private bindingIndex: number) {} private _implicitReceiver: o.Expression, private _valueUnwrapper: o.ReadVarExpr,
private bindingId: string, private isAction: boolean) {}
visitBinary(ast: cdAst.Binary, mode: _Mode): any { visitBinary(ast: cdAst.Binary, mode: _Mode): any {
var op: o.BinaryOperator; var op: o.BinaryOperator;
@ -233,8 +291,10 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
} }
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any { visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
return convertToStatementIfNeeded( const parts = this.visitAll(ast.expressions, mode);
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode))); const literalArr =
this.isAction ? o.literalArr(parts) : createCachedLiteralArray(this._builder, parts);
return convertToStatementIfNeeded(mode, literalArr);
} }
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any { visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
@ -242,13 +302,22 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
for (let i = 0; i < ast.keys.length; i++) { for (let i = 0; i < ast.keys.length; i++) {
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]); parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
} }
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts)); const literalMap =
this.isAction ? o.literalMap(parts) : createCachedLiteralMap(this._builder, parts);
return convertToStatementIfNeeded(mode, literalMap);
} }
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any { visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
return convertToStatementIfNeeded(mode, o.literal(ast.value)); return convertToStatementIfNeeded(mode, o.literal(ast.value));
} }
private _getLocal(name: string): o.Expression {
if (this.isAction && name == EventHandlerVars.event.name) {
return EventHandlerVars.event;
}
return this._nameResolver.getLocal(name);
}
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any { visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
const leftMostSafe = this.leftMostSafeNode(ast); const leftMostSafe = this.leftMostSafeNode(ast);
if (leftMostSafe) { if (leftMostSafe) {
@ -258,7 +327,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
let result: any = null; let result: any = null;
let receiver = this.visit(ast.receiver, _Mode.Expression); let receiver = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) { if (receiver === this._implicitReceiver) {
var varExpr = this._nameResolver.getLocal(ast.name); var varExpr = this._getLocal(ast.name);
if (isPresent(varExpr)) { if (isPresent(varExpr)) {
result = varExpr.callFn(args); result = varExpr.callFn(args);
} }
@ -282,7 +351,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
let result: any = null; let result: any = null;
var receiver = this.visit(ast.receiver, _Mode.Expression); var receiver = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) { if (receiver === this._implicitReceiver) {
result = this._nameResolver.getLocal(ast.name); result = this._getLocal(ast.name);
} }
if (isBlank(result)) { if (isBlank(result)) {
result = receiver.prop(ast.name); result = receiver.prop(ast.name);
@ -294,7 +363,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any { visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression); let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) { if (receiver === this._implicitReceiver) {
var varExpr = this._nameResolver.getLocal(ast.name); var varExpr = this._getLocal(ast.name);
if (isPresent(varExpr)) { if (isPresent(varExpr)) {
throw new Error('Cannot assign to a reference or variable!'); throw new Error('Cannot assign to a reference or variable!');
} }
@ -483,12 +552,12 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
private allocateTemporary(): o.ReadVarExpr { private allocateTemporary(): o.ReadVarExpr {
const tempNumber = this._currentTemporary++; const tempNumber = this._currentTemporary++;
this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount); this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
return new o.ReadVarExpr(temporaryName(this.bindingIndex, tempNumber)); return new o.ReadVarExpr(temporaryName(this.bindingId, tempNumber));
} }
private releaseTemporary(temporary: o.ReadVarExpr) { private releaseTemporary(temporary: o.ReadVarExpr) {
this._currentTemporary--; this._currentTemporary--;
if (temporary.name != temporaryName(this.bindingIndex, this._currentTemporary)) { if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
throw new Error(`Temporary ${temporary.name} released out of order`); throw new Error(`Temporary ${temporary.name} released out of order`);
} }
} }
@ -501,3 +570,69 @@ function flattenStatements(arg: any, output: o.Statement[]) {
output.push(arg); output.push(arg);
} }
} }
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
if (values.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
}
var proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
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, builder);
return proxyExpr.callFn(values);
}
function createCachedLiteralMap(
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
if (entries.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
}
const proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
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, builder);
return proxyExpr.callFn(values);
}
class DefaultNameResolver implements NameResolver {
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression { return null; }
getLocal(name: string): o.Expression { return null; }
}
function createCurrValueExpr(bindingId: string): o.ReadVarExpr {
return o.variable(`currVal_${bindingId}`); // fix syntax highlighting: `
}
function createPreventDefaultVar(bindingId: string): o.ReadVarExpr {
return o.variable(`pd_${bindingId}`);
}
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
if (stmt instanceof o.ExpressionStatement) {
return stmt.expr;
} else if (stmt instanceof o.ReturnStatement) {
return stmt.value;
}
return null;
}

View File

@ -34,3 +34,17 @@ export function createFastArray(values: o.Expression[]): o.Expression {
<o.Expression>o.literal(values.length) <o.Expression>o.literal(values.length)
].concat(values)); ].concat(values));
} }
export function createPureProxy(
fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
builder: {fields: o.ClassField[], ctorStmts: {push: (stmt: o.Statement) => void}}) {
builder.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}`);
}
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
.toStmt());
}

View File

@ -0,0 +1,96 @@
/**
* @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 {SecurityContext} from '@angular/core';
import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {BoundElementPropertyAst, PropertyBindingType} from '../template_parser/template_ast';
export function writeToRenderer(
view: o.Expression, boundProp: BoundElementPropertyAst, renderNode: o.Expression,
renderValue: o.Expression, logBindingUpdate: boolean): o.Statement[] {
const updateStmts: o.Statement[] = [];
const renderer = view.prop('renderer');
renderValue = sanitizedValue(view, boundProp, renderValue);
switch (boundProp.type) {
case PropertyBindingType.Property:
if (logBindingUpdate) {
updateStmts.push(o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
.callFn([renderer, renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
}
updateStmts.push(
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(
renderer
.callMethod(
'setElementAttribute', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Class:
updateStmts.push(
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(
renderer
.callMethod('setElementStyle', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Animation:
throw new Error('Illegal state: Should not come here!');
}
return updateStmts;
}
function sanitizedValue(
view: o.Expression, 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 = view.prop('viewUtils').prop('sanitizer');
let args =
[o.importExpr(resolveIdentifier(Identifiers.SecurityContext)).prop(enumValue), renderValue];
return ctx.callMethod('sanitize', args);
}

View File

@ -27,10 +27,10 @@ export function createClassStmt(config: {
return new o.ClassStmt( return new o.ClassStmt(
config.name, config.parent, builder.fields, builder.getters, ctor, builder.methods, config.name, config.parent, builder.fields, builder.getters, ctor, builder.methods,
config.modifiers || []) config.modifiers || []);
} }
function concatClassBuilderParts(builders: ClassBuilderPart[]): ClassBuilder { function concatClassBuilderParts(builders: ClassBuilderPart[]) {
return { return {
fields: [].concat(...builders.map(builder => builder.fields || [])), fields: [].concat(...builders.map(builder => builder.fields || [])),
methods: [].concat(...builders.map(builder => builder.methods || [])), methods: [].concat(...builders.map(builder => builder.methods || [])),
@ -52,9 +52,9 @@ export interface ClassBuilderPart {
/** /**
* Collects data for a generated class. * Collects data for a generated class.
*/ */
export interface ClassBuilder extends ClassBuilderPart { export interface ClassBuilder {
fields: o.ClassField[]; fields: o.ClassField[];
methods: o.ClassMethod[]; methods: o.ClassMethod[];
getters: o.ClassGetter[]; getters: o.ClassGetter[];
ctorStmts: o.Statement[]; ctorStmts: o.Statement[];
} }

View File

@ -65,6 +65,8 @@ export class CompileMethod {
this._newState = new _DebugState(nodeIndex, templateAst); this._newState = new _DebugState(nodeIndex, templateAst);
} }
push(...stmts: o.Statement[]) { this.addStmts(stmts); }
addStmt(stmt: o.Statement) { addStmt(stmt: o.Statement) {
this._updateDebugContextIfNeeded(); this._updateDebugContextIfNeeded();
this._bodyStatements.push(stmt); this._bodyStatements.push(stmt);

View File

@ -8,11 +8,12 @@
import {CompilePipeMetadata} from '../compile_metadata'; import {CompilePipeMetadata} from '../compile_metadata';
import {createPureProxy} from '../compiler_util/identifier_util';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
import {createPureProxy, getPropertyInView, injectFromViewParentInjector} from './util'; import {getPropertyInView, injectFromViewParentInjector} from './util';
export class CompilePipe { export class CompilePipe {
static call(view: CompileView, name: string, args: o.Expression[]): o.Expression { static call(view: CompileView, name: string, args: o.Expression[]): o.Expression {
@ -65,7 +66,8 @@ export class CompilePipe {
createPureProxy( createPureProxy(
pipeInstanceSeenFromPureProxy.prop('transform') pipeInstanceSeenFromPureProxy.prop('transform')
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]), .callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
args.length, purePipeProxyInstance, callingView); args.length, purePipeProxyInstance,
{fields: callingView.fields, ctorStmts: callingView.createMethod});
return o.importExpr(resolveIdentifier(Identifiers.castByValue)) return o.importExpr(resolveIdentifier(Identifiers.castByValue))
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')]) .callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
.callFn(args); .callFn(args);

View File

@ -8,11 +8,12 @@
import {AnimationEntryCompileResult} from '../animation/animation_compiler'; import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata'; 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 {CompilerConfig} from '../config';
import {MapWrapper} from '../facade/collection'; import {MapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
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';
@ -21,11 +22,9 @@ 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';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {EventHandlerVars} from './constants'; import {getPropertyInView, getViewFactoryName} from './util';
import {NameResolver} from './expression_converter';
import {createPureProxy, getPropertyInView, getViewFactoryName} from './util';
export class CompileView implements NameResolver, ClassBuilder { export class CompileView implements NameResolver {
public viewType: ViewType; public viewType: ViewType;
public viewQueries: Map<any, CompileQuery[]>; 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() { afterNodes() {
MapWrapper.values(this.viewQueries) MapWrapper.values(this.viewQueries)
.forEach( .forEach(

View File

@ -98,8 +98,6 @@ export class ViewProperties {
static viewUtils = o.THIS_EXPR.prop('viewUtils'); static viewUtils = o.THIS_EXPR.prop('viewUtils');
} }
export class EventHandlerVars { static event = o.variable('$event'); }
export class InjectMethodVars { export class InjectMethodVars {
static token = o.variable('token'); static token = o.variable('token');
static requestNodeIndex = o.variable('requestNodeIndex'); static requestNodeIndex = o.variable('requestNodeIndex');
@ -110,5 +108,4 @@ export class DetectChangesVars {
static throwOnChange = o.variable(`throwOnChange`); static throwOnChange = o.variable(`throwOnChange`);
static changes = o.variable(`changes`); static changes = o.variable(`changes`);
static changed = o.variable(`changed`); static changed = o.variable(`changed`);
static valUnwrapper = o.variable(`valUnwrapper`);
} }

View File

@ -7,6 +7,7 @@
*/ */
import {CompileDirectiveMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata} from '../compile_metadata';
import {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {identifierToken} from '../identifiers'; import {identifierToken} from '../identifiers';
import * as o from '../output/output_ast'; 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 {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 {EventHandlerVars, ViewProperties} from './constants'; import {ViewProperties} from './constants';
import {NoLocalsNameResolver, convertCdStatementToIr} from './expression_converter';
export class CompileEventListener { export class CompileEventListener {
private _method: CompileMethod; private _method: CompileMethod;
@ -61,24 +61,14 @@ export class CompileEventListener {
} }
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent); this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
var context = directiveInstance || this.compileElement.view.componentContext; var context = directiveInstance || this.compileElement.view.componentContext;
var actionStmts = convertCdStatementToIr( const view = this.compileElement.view;
directive ? new NoLocalsNameResolver(this.compileElement.view) : this.compileElement.view, const evalResult = convertActionBinding(
context, hostEvent.handler, this.compileElement.nodeIndex); view, directive ? null : view, context, hostEvent.handler,
var lastIndex = actionStmts.length - 1; `${this.compileElement.nodeIndex}_${this._actionResultExprs.length}`);
if (lastIndex >= 0) { if (evalResult.preventDefault) {
var lastStatement = actionStmts[lastIndex]; this._actionResultExprs.push(evalResult.preventDefault);
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]);
}
} }
this._method.addStmts(actionStmts); this._method.addStmts(evalResult.stmts);
} }
finishMethod() { finishMethod() {

View File

@ -8,6 +8,8 @@
import {SecurityContext} from '@angular/core'; 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 * as cdAst from '../expression_parser/ast';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, resolveIdentifier} from '../identifiers';
@ -22,60 +24,14 @@ 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';
import {NameResolver, NoLocalsNameResolver, convertCdExpressionToIr, temporaryDeclaration} from './expression_converter';
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr { function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
return o.THIS_EXPR.prop(`_expr_${exprIndex}`); return o.THIS_EXPR.prop(`_expr_${bindingId}`);
} }
function createCurrValueExpr(exprIndex: number): o.ReadVarExpr { function createCheckBindingStmt(
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: ` view: CompileView, evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
} actions: o.Statement[], method: CompileMethod) {
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;
}
// private is fine here as no child view will reference the cached value... // 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.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name) view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name)
@ -83,30 +39,36 @@ function bind(
.toStmt()); .toStmt());
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([ var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
DetectChangesVars.throwOnChange, fieldExpr, currValExpr DetectChangesVars.throwOnChange, fieldExpr, evalResult.currValExpr
]); ]);
if (evalResult.forceUpdate) { if (evalResult.forceUpdate) {
condition = evalResult.forceUpdate.or(condition); condition = evalResult.forceUpdate.or(condition);
} }
method.addStmt(new o.IfStmt( method.addStmts(evalResult.stmts);
condition, method.addStmt(new o.IfStmt(condition, actions.concat([
actions.concat([<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(currValExpr).toStmt()]))); <o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
])));
} }
export function bindRenderText( export function bindRenderText(
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView) { boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
var bindingIndex = view.bindings.length; var bindingId = `${view.bindings.length}`;
view.bindings.push(new CompileBinding(compileNode, boundText)); view.bindings.push(new CompileBinding(compileNode, boundText));
var currValExpr = createCurrValueExpr(bindingIndex); const evalResult =
var valueField = createBindFieldExpr(bindingIndex); convertPropertyBinding(view, view, view.componentContext, boundText.value, bindingId);
if (!evalResult) {
return null;
}
var valueField = createBindFieldExpr(bindingId);
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText); view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
bind( createCheckBindingStmt(
view, currValExpr, valueField, boundText.value, view.componentContext, view, view, evalResult, valueField,
[o.THIS_EXPR.prop('renderer') [o.THIS_EXPR.prop('renderer')
.callMethod('setText', [compileNode.renderNode, currValExpr]) .callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
.toStmt()], .toStmt()],
view.detectChangesRenderPropertiesMethod, bindingIndex); view.detectChangesRenderPropertiesMethod);
} }
function bindAndWriteToRenderer( function bindAndWriteToRenderer(
@ -115,52 +77,22 @@ function bindAndWriteToRenderer(
var view = compileElement.view; var view = compileElement.view;
var renderNode = compileElement.renderNode; var renderNode = compileElement.renderNode;
boundProps.forEach((boundProp) => { boundProps.forEach((boundProp) => {
var bindingIndex = view.bindings.length; const bindingId = `${view.bindings.length}`;
view.bindings.push(new CompileBinding(compileElement, boundProp)); view.bindings.push(new CompileBinding(compileElement, boundProp));
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp); view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
var fieldExpr = createBindFieldExpr(bindingIndex); var fieldExpr = createBindFieldExpr(bindingId);
var currValExpr = createCurrValueExpr(bindingIndex); const evalResult =
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr); convertPropertyBinding(view, isHostProp ? null : view, context, boundProp.value, bindingId);
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
var updateStmts: o.Statement[] = []; var updateStmts: o.Statement[] = [];
var compileMethod = view.detectChangesRenderPropertiesMethod; var compileMethod = view.detectChangesRenderPropertiesMethod;
switch (boundProp.type) { switch (boundProp.type) {
case PropertyBindingType.Property: 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: 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: case PropertyBindingType.Class:
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod('setElementClass', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Style: case PropertyBindingType.Style:
var strValue: o.Expression = renderValue.callMethod('toString', []); updateStmts.push(...writeToRenderer(
if (isPresent(boundProp.unit)) { o.THIS_EXPR, boundProp, renderNode, evalResult.currValExpr,
strValue = strValue.plus(o.literal(boundProp.unit)); view.genConfig.logBindingUpdate));
}
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod('setElementStyle', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
break; break;
case PropertyBindingType.Animation: case PropertyBindingType.Animation:
compileMethod = view.animationBindingsMethod; compileMethod = view.animationBindingsMethod;
@ -182,16 +114,17 @@ function bindAndWriteToRenderer(
updateStmts.push( updateStmts.push(
animationTransitionVar animationTransitionVar
.set(animationFnExpr.callFn([ .set(animationFnExpr.callFn([
o.THIS_EXPR, renderNode, oldRenderValue.equals(unitializedValue) o.THIS_EXPR, renderNode,
.conditional(emptyStateValue, oldRenderValue), fieldExpr.equals(unitializedValue).conditional(emptyStateValue, fieldExpr),
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue) evalResult.currValExpr.equals(unitializedValue)
.conditional(emptyStateValue, evalResult.currValExpr)
])) ]))
.toDeclStmt()); .toDeclStmt());
detachStmts.push(animationTransitionVar detachStmts.push(
.set(animationFnExpr.callFn( animationTransitionVar
[o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue])) .set(animationFnExpr.callFn([o.THIS_EXPR, renderNode, fieldExpr, emptyStateValue]))
.toDeclStmt()); .toDeclStmt());
eventListeners.forEach(listener => { eventListeners.forEach(listener => {
if (listener.isAnimation && listener.eventName === animationName) { if (listener.isAnimation && listener.eventName === animationName) {
@ -206,43 +139,10 @@ function bindAndWriteToRenderer(
break; break;
} }
bind( createCheckBindingStmt(view, evalResult, fieldExpr, updateStmts, compileMethod);
view, currValExpr, fieldExpr, boundProp.value, context,
isHostProp ? new NoLocalsNameResolver(view) : view, updateStmts, compileMethod,
view.bindings.length);
}); });
} }
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( export function bindRenderInputs(
boundProps: BoundElementPropertyAst[], compileElement: CompileElement, boundProps: BoundElementPropertyAst[], compileElement: CompileElement,
eventListeners: CompileEventListener[]): void { eventListeners: CompileEventListener[]): void {
@ -265,24 +165,24 @@ export function bindDirectiveInputs(
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst); detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
directiveAst.inputs.forEach((input) => { directiveAst.inputs.forEach((input) => {
var bindingIndex = view.bindings.length; const bindingId = `${view.bindings.length}`;
view.bindings.push(new CompileBinding(compileElement, input)); view.bindings.push(new CompileBinding(compileElement, input));
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input); detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
var currValExpr = createCurrValueExpr(bindingIndex); const evalResult =
const evalResult = evalCdAst( convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
view, currValExpr, input.value, view.componentContext, view, detectChangesInInputsMethod,
bindingIndex);
if (!evalResult) { if (!evalResult) {
return; return;
} }
detectChangesInInputsMethod.addStmt(directiveWrapperInstance detectChangesInInputsMethod.addStmts(evalResult.stmts);
.callMethod( detectChangesInInputsMethod.addStmt(
`check_${input.directiveName}`, directiveWrapperInstance
[ .callMethod(
currValExpr, DetectChangesVars.throwOnChange, `check_${input.directiveName}`,
evalResult.forceUpdate || o.literal(false) [
]) evalResult.currValExpr, DetectChangesVars.throwOnChange,
.toStmt()); evalResult.forceUpdate || o.literal(false)
])
.toStmt());
}); });
var isOnPushComp = directiveAst.directive.isComponent && var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection); !isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
@ -296,10 +196,3 @@ export function bindDirectiveInputs(
directiveDetectChangesExpr.toStmt(); directiveDetectChangesExpr.toStmt();
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt); 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();
}

View File

@ -91,16 +91,3 @@ export function createFlatArray(expressions: o.Expression[]): o.Expression {
} }
return result; 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());
}

View File

@ -9,6 +9,7 @@
import {ViewEncapsulation} from '@angular/core'; import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
import {createDiTokenExpression, createFastArray} from '../compiler_util/identifier_util'; import {createDiTokenExpression, createFastArray} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
@ -586,12 +587,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
DetectChangesVars.changes.set(o.NULL_EXPR) DetectChangesVars.changes.set(o.NULL_EXPR)
.toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange))))); .toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))));
} }
if (readVars.has(DetectChangesVars.valUnwrapper.name)) { varStmts.push(...createSharedBindingVariablesIfNeeded(stmts));
varStmts.push(
DetectChangesVars.valUnwrapper
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
.toDeclStmt(null, [o.StmtModifier.Final]));
}
return varStmts.concat(stmts); return varStmts.concat(stmts);
} }