feat(compiler): integrate compiler with view engine - change detection tests work (#14412)
Included refactoring: - make ViewData.parentIndex point to component provider index - split NodeType.Provider into Provider / Directive / Pipe - make purePipe take the real pipe as argument to detect changes - order change detection: 1) directive props 2) renderer props Part of #14013 PR Close #14412
This commit is contained in:

committed by
Miško Hevery

parent
1dc9be4b7d
commit
e4e9dbe33d
@ -205,14 +205,14 @@ export class AotCompiler {
|
||||
const pipes = ngModule.transitiveModule.pipes.map(
|
||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
const {template: parsedTemplate, pipes: usedPipes} = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||
identifierName(compMeta.type));
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const viewResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||
compMeta, parsedTemplate, stylesExpr, usedPipes, compiledAnimations);
|
||||
if (componentStyles) {
|
||||
targetStatements.push(
|
||||
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
|
||||
|
@ -17,58 +17,9 @@ import {createPureProxy} from './identifier_util';
|
||||
|
||||
const VAL_UNWRAPPER_VAR = o.variable(`valUnwrapper`);
|
||||
|
||||
export interface NameResolver {
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||
getLocal(name: string): 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) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given expression AST into an executable output AST, assuming the expression is
|
||||
* used in a property binding.
|
||||
*/
|
||||
export function convertPropertyBinding(
|
||||
builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
|
||||
expression: cdAst.AST, bindingId: string): ConvertPropertyBindingResult {
|
||||
const currValExpr = createCurrValueExpr(bindingId);
|
||||
const stmts: o.Statement[] = [];
|
||||
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;
|
||||
}
|
||||
|
||||
if (visitor.temporaryCount) {
|
||||
for (let i = 0; i < visitor.temporaryCount; i++) {
|
||||
stmts.push(temporaryDeclaration(bindingId, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (visitor.needsValueUnwrapper) {
|
||||
const 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 interface LocalResolver { getLocal(name: string): o.Expression; }
|
||||
|
||||
export class ConvertActionBindingResult {
|
||||
constructor(public stmts: o.Statement[], public allowDefault: o.ReadVarExpr) {}
|
||||
@ -79,15 +30,31 @@ export class ConvertActionBindingResult {
|
||||
* used in an action binding (e.g. an event handler).
|
||||
*/
|
||||
export function convertActionBinding(
|
||||
builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
|
||||
action: cdAst.AST, bindingId: string): ConvertActionBindingResult {
|
||||
if (!nameResolver) {
|
||||
nameResolver = new DefaultNameResolver();
|
||||
localResolver: LocalResolver, implicitReceiver: o.Expression, action: cdAst.AST,
|
||||
bindingId: string): ConvertActionBindingResult {
|
||||
if (!localResolver) {
|
||||
localResolver = new DefaultLocalResolver();
|
||||
}
|
||||
const visitor =
|
||||
new _AstToIrVisitor(builder, nameResolver, implicitReceiver, null, bindingId, true);
|
||||
const actionWithoutBuiltins = convertPropertyBindingBuiltins(
|
||||
{
|
||||
createLiteralArrayConverter: (argCount: number) => {
|
||||
// Note: no caching for literal arrays in actions.
|
||||
return (args: o.Expression[]) => o.literalArr(args);
|
||||
},
|
||||
createLiteralMapConverter: (keys: string[]) => {
|
||||
// Note: no caching for literal maps in actions.
|
||||
return (args: o.Expression[]) =>
|
||||
o.literalMap(<[string, o.Expression][]>keys.map((key, i) => [key, args[i]]));
|
||||
},
|
||||
createPipeConverter: (name: string) => {
|
||||
throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
||||
}
|
||||
},
|
||||
action);
|
||||
|
||||
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId);
|
||||
const actionStmts: o.Statement[] = [];
|
||||
flattenStatements(action.visit(visitor, _Mode.Statement), actionStmts);
|
||||
flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
|
||||
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
||||
const lastIndex = actionStmts.length - 1;
|
||||
let preventDefaultVar: o.ReadVarExpr = null;
|
||||
@ -106,11 +73,105 @@ export function convertActionBinding(
|
||||
return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
|
||||
}
|
||||
|
||||
export interface BuiltinConverter { (args: o.Expression[]): o.Expression; }
|
||||
|
||||
export interface BuiltinConverterFactory {
|
||||
createLiteralArrayConverter(argCount: number): BuiltinConverter;
|
||||
createLiteralMapConverter(keys: string[]): BuiltinConverter;
|
||||
createPipeConverter(name: string, argCount: number): BuiltinConverter;
|
||||
}
|
||||
|
||||
export function convertPropertyBindingBuiltins(
|
||||
converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
|
||||
return convertBuiltins(converterFactory, ast);
|
||||
}
|
||||
|
||||
export class ConvertPropertyBindingResult {
|
||||
constructor(public stmts: o.Statement[], public currValExpr: o.Expression) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given expression AST into an executable output AST, assuming the expression
|
||||
* is used in property binding. The expression has to be preprocessed via
|
||||
* `convertPropertyBindingBuiltins`.
|
||||
*/
|
||||
export function convertPropertyBinding(
|
||||
localResolver: LocalResolver, implicitReceiver: o.Expression,
|
||||
expressionWithoutBuiltins: cdAst.AST, bindingId: string): ConvertPropertyBindingResult {
|
||||
if (!localResolver) {
|
||||
localResolver = new DefaultLocalResolver();
|
||||
}
|
||||
const currValExpr = createCurrValueExpr(bindingId);
|
||||
const stmts: o.Statement[] = [];
|
||||
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId);
|
||||
const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
|
||||
|
||||
if (visitor.temporaryCount) {
|
||||
for (let i = 0; i < visitor.temporaryCount; i++) {
|
||||
stmts.push(temporaryDeclaration(bindingId, i));
|
||||
}
|
||||
}
|
||||
|
||||
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
return new ConvertPropertyBindingResult(stmts, currValExpr);
|
||||
}
|
||||
|
||||
|
||||
export class LegacyConvertPropertyBindingResult implements ConvertPropertyBindingResult {
|
||||
constructor(
|
||||
public stmts: o.Statement[], public currValExpr: o.Expression,
|
||||
public forceUpdate: o.Expression) {}
|
||||
}
|
||||
|
||||
export interface LegacyNameResolver {
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||
getLocal(name: string): o.Expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given expression AST into an executable output AST, assuming the expression is
|
||||
* used in a property binding.
|
||||
*/
|
||||
export function legacyConvertPropertyBinding(
|
||||
builder: ClassBuilder, nameResolver: LegacyNameResolver, implicitReceiver: o.Expression,
|
||||
expression: cdAst.AST, bindingId: string): LegacyConvertPropertyBindingResult {
|
||||
if (!nameResolver) {
|
||||
nameResolver = new LegacyDefaultNameResolver();
|
||||
}
|
||||
let needsValueUnwrapper = false;
|
||||
const expressionWithoutBuiltins = convertBuiltins(
|
||||
{
|
||||
createLiteralArrayConverter: (argCount: number) => {
|
||||
return (args: o.Expression[]) => legacyCreateCachedLiteralArray(builder, args);
|
||||
},
|
||||
createLiteralMapConverter: (keys: string[]) => {
|
||||
return (args: o.Expression[]) => legacyCreateCachedLiteralMap(
|
||||
builder, <[string, o.Expression][]>keys.map((key, i) => [key, args[i]]));
|
||||
},
|
||||
createPipeConverter: (name: string) => {
|
||||
needsValueUnwrapper = true;
|
||||
return (args: o.Expression[]) => VAL_UNWRAPPER_VAR.callMethod(
|
||||
'unwrap', [nameResolver.callPipe(name, args[0], args.slice(1))]);
|
||||
}
|
||||
},
|
||||
expression);
|
||||
|
||||
const {stmts, currValExpr} =
|
||||
convertPropertyBinding(nameResolver, implicitReceiver, expressionWithoutBuiltins, bindingId);
|
||||
let forceUpdate: o.Expression = null;
|
||||
if (needsValueUnwrapper) {
|
||||
const initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
|
||||
stmts.unshift(initValueUnwrapperStmt);
|
||||
forceUpdate = VAL_UNWRAPPER_VAR.prop('hasWrappedValue');
|
||||
}
|
||||
return new LegacyConvertPropertyBindingResult(stmts, currValExpr, forceUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates variables that are shared by multiple calls to `convertActionBinding` /
|
||||
* `convertPropertyBinding`
|
||||
*/
|
||||
export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
|
||||
export function legacyCreateSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
|
||||
const unwrapperStmts: o.Statement[] = [];
|
||||
const readVars = o.findReadVarNames(stmts);
|
||||
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
||||
@ -122,6 +183,11 @@ export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.St
|
||||
return unwrapperStmts;
|
||||
}
|
||||
|
||||
function convertBuiltins(converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
|
||||
const visitor = new _BuiltinAstConverter(converterFactory);
|
||||
return ast.visit(visitor);
|
||||
}
|
||||
|
||||
function temporaryName(bindingId: string, temporaryNumber: number): string {
|
||||
return `tmp_${bindingId}_${temporaryNumber}`;
|
||||
}
|
||||
@ -162,17 +228,34 @@ function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expressi
|
||||
}
|
||||
}
|
||||
|
||||
class _BuiltinAstConverter extends cdAst.AstTransformer {
|
||||
constructor(private _converterFactory: BuiltinConverterFactory) { super(); }
|
||||
visitPipe(ast: cdAst.BindingPipe, context: any): any {
|
||||
const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
|
||||
return new BuiltinFunctionCall(
|
||||
ast.span, args, this._converterFactory.createPipeConverter(ast.name, args.length));
|
||||
}
|
||||
visitLiteralArray(ast: cdAst.LiteralArray, context: any): any {
|
||||
const args = ast.expressions.map(ast => ast.visit(this, context));
|
||||
return new BuiltinFunctionCall(
|
||||
ast.span, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
|
||||
}
|
||||
visitLiteralMap(ast: cdAst.LiteralMap, context: any): any {
|
||||
const args = ast.values.map(ast => ast.visit(this, context));
|
||||
return new BuiltinFunctionCall(
|
||||
ast.span, args, this._converterFactory.createLiteralMapConverter(ast.keys));
|
||||
}
|
||||
}
|
||||
|
||||
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 _builder: ClassBuilder, private _nameResolver: NameResolver,
|
||||
private _implicitReceiver: o.Expression, private _valueUnwrapper: o.ReadVarExpr,
|
||||
private bindingId: string, private isAction: boolean) {}
|
||||
private _localResolver: LocalResolver, private _implicitReceiver: o.Expression,
|
||||
private bindingId: string) {}
|
||||
|
||||
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
||||
let op: o.BinaryOperator;
|
||||
@ -246,20 +329,19 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
}
|
||||
|
||||
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]));
|
||||
throw new Error(
|
||||
`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
|
||||
}
|
||||
|
||||
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode,
|
||||
this.visit(ast.target, _Mode.Expression).callFn(this.visitAll(ast.args, _Mode.Expression)));
|
||||
const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
|
||||
let fnResult: o.Expression;
|
||||
if (ast instanceof BuiltinFunctionCall) {
|
||||
fnResult = ast.converter(convertedArgs);
|
||||
} else {
|
||||
fnResult = this.visit(ast.target, _Mode.Expression).callFn(convertedArgs);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, fnResult);
|
||||
}
|
||||
|
||||
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
|
||||
@ -301,32 +383,18 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
}
|
||||
|
||||
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
||||
const parts = this.visitAll(ast.expressions, mode);
|
||||
const literalArr =
|
||||
this.isAction ? o.literalArr(parts) : createCachedLiteralArray(this._builder, parts);
|
||||
return convertToStatementIfNeeded(mode, literalArr);
|
||||
throw new Error(`Illegal State: literal arrays should have been converted into functions`);
|
||||
}
|
||||
|
||||
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||
const parts: any[] = [];
|
||||
for (let i = 0; i < ast.keys.length; i++) {
|
||||
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
|
||||
}
|
||||
const literalMap =
|
||||
this.isAction ? o.literalMap(parts) : createCachedLiteralMap(this._builder, parts);
|
||||
return convertToStatementIfNeeded(mode, literalMap);
|
||||
throw new Error(`Illegal State: literal maps should have been converted into functions`);
|
||||
}
|
||||
|
||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
||||
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);
|
||||
}
|
||||
private _getLocal(name: string): o.Expression { return this._localResolver.getLocal(name); }
|
||||
|
||||
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||
@ -581,7 +649,8 @@ function flattenStatements(arg: any, output: o.Statement[]) {
|
||||
}
|
||||
}
|
||||
|
||||
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
||||
function legacyCreateCachedLiteralArray(
|
||||
builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
||||
if (values.length === 0) {
|
||||
return o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
|
||||
}
|
||||
@ -601,7 +670,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
|
||||
return proxyExpr.callFn(values);
|
||||
}
|
||||
|
||||
function createCachedLiteralMap(
|
||||
function legacyCreateCachedLiteralMap(
|
||||
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
|
||||
if (entries.length === 0) {
|
||||
return o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
|
||||
@ -624,10 +693,23 @@ function createCachedLiteralMap(
|
||||
return proxyExpr.callFn(values);
|
||||
}
|
||||
|
||||
class DefaultLocalResolver implements LocalResolver {
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name === EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultNameResolver implements NameResolver {
|
||||
class LegacyDefaultNameResolver implements LegacyNameResolver {
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression { return null; }
|
||||
getLocal(name: string): o.Expression { return null; }
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name === EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function createCurrValueExpr(bindingId: string): o.ReadVarExpr {
|
||||
@ -646,3 +728,9 @@ function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class BuiltinFunctionCall extends cdAst.FunctionCall {
|
||||
constructor(span: cdAst.ParseSpan, public args: cdAst.AST[], public converter: BuiltinConverter) {
|
||||
super(span, null, args);
|
||||
}
|
||||
}
|
@ -13,12 +13,12 @@ import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||
|
||||
import {isFirstViewCheck} from './binding_util';
|
||||
import {ConvertPropertyBindingResult} from './expression_converter';
|
||||
import {LegacyConvertPropertyBindingResult} from './expression_converter';
|
||||
import {createEnumExpression} from './identifier_util';
|
||||
|
||||
export function createCheckRenderBindingStmt(
|
||||
view: o.Expression, renderElement: o.Expression, boundProp: BoundElementPropertyAst,
|
||||
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult,
|
||||
oldValue: o.ReadPropExpr, evalResult: LegacyConvertPropertyBindingResult,
|
||||
securityContextExpression?: o.Expression): o.Statement[] {
|
||||
const checkStmts: o.Statement[] = [...evalResult.stmts];
|
||||
const securityContext = calcSecurityContext(boundProp, securityContextExpression);
|
||||
@ -84,7 +84,7 @@ function calcSecurityContext(
|
||||
export function createCheckAnimationBindingStmts(
|
||||
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
||||
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
|
||||
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult) {
|
||||
oldValue: o.ReadPropExpr, evalResult: LegacyConvertPropertyBindingResult) {
|
||||
const detachStmts: o.Statement[] = [];
|
||||
const updateStmts: o.Statement[] = [];
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||
import {createCheckBindingField, isFirstViewCheck} from './compiler_util/binding_util';
|
||||
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||
import {EventHandlerVars, convertActionBinding, legacyConvertPropertyBinding} from './compiler_util/expression_converter';
|
||||
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from './compiler_util/render_util';
|
||||
import {CompilerConfig} from './config';
|
||||
import {Parser} from './expression_parser/parser';
|
||||
@ -253,7 +253,7 @@ function addCheckHostMethod(
|
||||
];
|
||||
hostProps.forEach((hostProp, hostPropIdx) => {
|
||||
const field = createCheckBindingField(builder);
|
||||
const evalResult = convertPropertyBinding(
|
||||
const evalResult = legacyConvertPropertyBinding(
|
||||
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostProp.value, field.bindingId);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
@ -285,8 +285,7 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
|
||||
const actionStmts: o.Statement[] = [resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)];
|
||||
hostListeners.forEach((hostListener, eventIdx) => {
|
||||
const evalResult = convertActionBinding(
|
||||
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler,
|
||||
`sub_${eventIdx}`);
|
||||
null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler, `sub_${eventIdx}`);
|
||||
const trueStmts = evalResult.stmts;
|
||||
if (evalResult.allowDefault) {
|
||||
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
|
||||
|
@ -400,11 +400,36 @@ export class Identifiers {
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.queryDef
|
||||
};
|
||||
static pureArrayDef: IdentifierSpec = {
|
||||
name: 'pureArrayDef',
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.pureArrayDef
|
||||
};
|
||||
static pureObjectDef: IdentifierSpec = {
|
||||
name: 'pureObjectDef',
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.pureObjectDef
|
||||
};
|
||||
static purePipeDef: IdentifierSpec = {
|
||||
name: 'purePipeDef',
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.purePipeDef
|
||||
};
|
||||
static pipeDef: IdentifierSpec = {
|
||||
name: 'pipeDef',
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.pipeDef
|
||||
};
|
||||
static nodeValue: IdentifierSpec = {
|
||||
name: 'nodeValue',
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.nodeValue
|
||||
};
|
||||
static unwrapValue: IdentifierSpec = {
|
||||
name: 'unwrapValue',
|
||||
moduleUrl: VIEW_ENGINE_MODULE_URL,
|
||||
runtime: viewEngine.unwrapValue
|
||||
};
|
||||
}
|
||||
|
||||
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
|
||||
|
@ -278,14 +278,14 @@ export class JitCompiler implements Compiler {
|
||||
template.directives.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
|
||||
const pipes = template.ngModule.transitiveModule.pipes.map(
|
||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
const {template: parsedTemplate, pipes: usedPipes} = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
|
||||
identifierName(compMeta.type));
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const compileResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||
pipes, compiledAnimations);
|
||||
usedPipes, compiledAnimations);
|
||||
const statements = stylesCompileResult.componentStylesheet.statements
|
||||
.concat(...compiledAnimations.map(ca => ca.statements))
|
||||
.concat(compileResult.statements);
|
||||
|
@ -51,6 +51,7 @@ export class BoundProperty {
|
||||
*/
|
||||
export class BindingParser {
|
||||
pipesByName: Map<string, CompilePipeSummary> = new Map();
|
||||
private _usedPipes: Map<string, CompilePipeSummary> = new Map();
|
||||
|
||||
constructor(
|
||||
private _exprParser: Parser, private _interpolationConfig: InterpolationConfig,
|
||||
@ -59,6 +60,8 @@ export class BindingParser {
|
||||
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
|
||||
}
|
||||
|
||||
getUsedPipes(): CompilePipeSummary[] { return Array.from(this._usedPipes.values()); }
|
||||
|
||||
createDirectiveHostPropertyAsts(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan):
|
||||
BoundElementPropertyAst[] {
|
||||
if (dirMeta.hostProperties) {
|
||||
@ -377,11 +380,14 @@ export class BindingParser {
|
||||
const collector = new PipeCollector();
|
||||
ast.visit(collector);
|
||||
collector.pipes.forEach((ast, pipeName) => {
|
||||
if (!this.pipesByName.has(pipeName)) {
|
||||
const pipeMeta = this.pipesByName.get(pipeName);
|
||||
if (!pipeMeta) {
|
||||
this._reportError(
|
||||
`The pipe '${pipeName}' could not be found`,
|
||||
new ParseSourceSpan(
|
||||
sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
|
||||
} else {
|
||||
this._usedPipes.set(pipeName, pipeMeta);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -7,7 +7,9 @@
|
||||
*/
|
||||
|
||||
import {Inject, InjectionToken, Optional, SchemaMetadata} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
||||
import {AST, ASTWithSource, EmptyExpr} from '../expression_parser/ast';
|
||||
import {Parser} from '../expression_parser/parser';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
||||
@ -25,10 +27,12 @@ import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
import {CssSelector, SelectorMatcher} from '../selector';
|
||||
import {isStyleUrlResolvable} from '../style_url_resolver';
|
||||
import {syntaxError} from '../util';
|
||||
|
||||
import {BindingParser, BoundProperty} from './binding_parser';
|
||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
|
||||
import {PreparsedElementType, preparseElement} from './template_preparser';
|
||||
|
||||
|
||||
// Group 1 = "bind-"
|
||||
// Group 2 = "let-"
|
||||
// Group 3 = "ref-/#"
|
||||
@ -76,7 +80,9 @@ export class TemplateParseError extends ParseError {
|
||||
}
|
||||
|
||||
export class TemplateParseResult {
|
||||
constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {}
|
||||
constructor(
|
||||
public templateAst?: TemplateAst[], public usedPipes?: CompilePipeSummary[],
|
||||
public errors?: ParseError[]) {}
|
||||
}
|
||||
|
||||
@CompilerInjectable()
|
||||
@ -88,7 +94,8 @@ export class TemplateParser {
|
||||
|
||||
parse(
|
||||
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveSummary[],
|
||||
pipes: CompilePipeSummary[], schemas: SchemaMetadata[], templateUrl: string): TemplateAst[] {
|
||||
pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
|
||||
templateUrl: string): {template: TemplateAst[], pipes: CompilePipeSummary[]} {
|
||||
const result = this.tryParse(component, template, directives, pipes, schemas, templateUrl);
|
||||
const warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
|
||||
const errors = result.errors.filter(error => error.level === ParseErrorLevel.FATAL);
|
||||
@ -102,7 +109,7 @@ export class TemplateParser {
|
||||
throw syntaxError(`Template parse errors:\n${errorString}`);
|
||||
}
|
||||
|
||||
return result.templateAst;
|
||||
return {template: result.templateAst, pipes: result.usedPipes};
|
||||
}
|
||||
|
||||
tryParse(
|
||||
@ -121,6 +128,7 @@ export class TemplateParser {
|
||||
templateUrl: string): TemplateParseResult {
|
||||
let result: TemplateAst[];
|
||||
const errors = htmlAstWithErrors.errors;
|
||||
const usedPipes: CompilePipeSummary[] = [];
|
||||
if (htmlAstWithErrors.rootNodes.length > 0) {
|
||||
const uniqDirectives = removeSummaryDuplicates(directives);
|
||||
const uniqPipes = removeSummaryDuplicates(pipes);
|
||||
@ -140,13 +148,14 @@ export class TemplateParser {
|
||||
errors);
|
||||
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
||||
errors.push(...providerViewContext.errors);
|
||||
usedPipes.push(...bindingParser.getUsedPipes());
|
||||
} else {
|
||||
result = [];
|
||||
}
|
||||
this._assertNoReferenceDuplicationOnTemplate(result, errors);
|
||||
|
||||
if (errors.length > 0) {
|
||||
return new TemplateParseResult(result, errors);
|
||||
return new TemplateParseResult(result, usedPipes, errors);
|
||||
}
|
||||
|
||||
if (this.transforms) {
|
||||
@ -154,7 +163,7 @@ export class TemplateParser {
|
||||
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
|
||||
}
|
||||
|
||||
return new TemplateParseResult(result, errors);
|
||||
return new TemplateParseResult(result, usedPipes, errors);
|
||||
}
|
||||
|
||||
expandHtml(htmlAstWithErrors: ParseTreeResult, forced: boolean = false): ParseTreeResult {
|
||||
@ -303,11 +312,12 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
const {directives: directiveMetas, matchElement} =
|
||||
this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
||||
const references: ReferenceAst[] = [];
|
||||
const boundDirectivePropNames = new Set<string>();
|
||||
const directiveAsts = this._createDirectiveAsts(
|
||||
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
|
||||
elementOrDirectiveRefs, element.sourceSpan, references);
|
||||
const elementProps: BoundElementPropertyAst[] =
|
||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
||||
elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames);
|
||||
const elementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||
element.name, elementOrDirectiveProps, boundDirectivePropNames);
|
||||
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||
|
||||
const providerContext = new ProviderElementContext(
|
||||
@ -372,11 +382,12 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
||||
const {directives: templateDirectiveMetas} =
|
||||
this._parseDirectives(this.selectorMatcher, templateCssSelector);
|
||||
const templateBoundDirectivePropNames = new Set<string>();
|
||||
const templateDirectiveAsts = this._createDirectiveAsts(
|
||||
true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [],
|
||||
element.sourceSpan, []);
|
||||
element.sourceSpan, [], templateBoundDirectivePropNames);
|
||||
const templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||
element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
|
||||
element.name, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
|
||||
this._assertNoComponentsNorElementBindingsOnTemplate(
|
||||
templateDirectiveAsts, templateElementProps, element.sourceSpan);
|
||||
const templateProviderContext = new ProviderElementContext(
|
||||
@ -544,7 +555,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
private _createDirectiveAsts(
|
||||
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveSummary[],
|
||||
props: BoundProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
|
||||
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[]): DirectiveAst[] {
|
||||
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[],
|
||||
targetBoundDirectivePropNames: Set<string>): DirectiveAst[] {
|
||||
const matchedReferences = new Set<string>();
|
||||
let component: CompileDirectiveSummary = null;
|
||||
|
||||
@ -557,13 +569,14 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
component = directive;
|
||||
}
|
||||
const directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||
const hostProperties =
|
||||
let hostProperties =
|
||||
this._bindingParser.createDirectiveHostPropertyAsts(directive, sourceSpan);
|
||||
// Note: We need to check the host properties here as well,
|
||||
// as we don't know the element name in the DirectiveWrapperCompiler yet.
|
||||
this._checkPropertiesInSchema(elementName, hostProperties);
|
||||
hostProperties = this._checkPropertiesInSchema(elementName, hostProperties);
|
||||
const hostEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan);
|
||||
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
|
||||
this._createDirectivePropertyAsts(
|
||||
directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
|
||||
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
||||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||
(directive.exportAs == elOrDirRef.value)) {
|
||||
@ -596,7 +609,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
|
||||
private _createDirectivePropertyAsts(
|
||||
directiveProperties: {[key: string]: string}, boundProps: BoundProperty[],
|
||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[],
|
||||
targetBoundDirectivePropNames: Set<string>) {
|
||||
if (directiveProperties) {
|
||||
const boundPropsByName = new Map<string, BoundProperty>();
|
||||
boundProps.forEach(boundProp => {
|
||||
@ -613,8 +627,11 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
|
||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||
if (boundProp) {
|
||||
targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(
|
||||
dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
|
||||
targetBoundDirectivePropNames.add(boundProp.name);
|
||||
if (!isEmptyExpression(boundProp.expression)) {
|
||||
targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(
|
||||
dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -622,23 +639,15 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
|
||||
private _createElementPropertyAsts(
|
||||
elementName: string, props: BoundProperty[],
|
||||
directives: DirectiveAst[]): BoundElementPropertyAst[] {
|
||||
boundDirectivePropNames: Set<string>): BoundElementPropertyAst[] {
|
||||
const boundElementProps: BoundElementPropertyAst[] = [];
|
||||
const boundDirectivePropsIndex = new Map<string, BoundDirectivePropertyAst>();
|
||||
|
||||
directives.forEach((directive: DirectiveAst) => {
|
||||
directive.inputs.forEach((prop: BoundDirectivePropertyAst) => {
|
||||
boundDirectivePropsIndex.set(prop.templateName, prop);
|
||||
});
|
||||
});
|
||||
|
||||
props.forEach((prop: BoundProperty) => {
|
||||
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
|
||||
if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
|
||||
boundElementProps.push(this._bindingParser.createElementPropertyAst(elementName, prop));
|
||||
}
|
||||
});
|
||||
this._checkPropertiesInSchema(elementName, boundElementProps);
|
||||
return boundElementProps;
|
||||
return this._checkPropertiesInSchema(elementName, boundElementProps);
|
||||
}
|
||||
|
||||
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
|
||||
@ -723,8 +732,11 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
});
|
||||
}
|
||||
|
||||
private _checkPropertiesInSchema(elementName: string, boundProps: BoundElementPropertyAst[]) {
|
||||
boundProps.forEach((boundProp) => {
|
||||
private _checkPropertiesInSchema(elementName: string, boundProps: BoundElementPropertyAst[]):
|
||||
BoundElementPropertyAst[] {
|
||||
// Note: We can't filter out empty expressions before this method,
|
||||
// as we still want to validate them!
|
||||
return boundProps.filter((boundProp) => {
|
||||
if (boundProp.type === PropertyBindingType.Property &&
|
||||
!this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
|
||||
let errorMsg =
|
||||
@ -741,6 +753,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
}
|
||||
this._reportError(errorMsg, boundProp.sourceSpan);
|
||||
}
|
||||
return !isEmptyExpression(boundProp.value);
|
||||
});
|
||||
}
|
||||
|
||||
@ -870,3 +883,10 @@ export function removeSummaryDuplicates<T extends{type: CompileTypeMetadata}>(it
|
||||
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
function isEmptyExpression(ast: AST): boolean {
|
||||
if (ast instanceof ASTWithSource) {
|
||||
ast = ast.ast;
|
||||
}
|
||||
return ast instanceof EmptyExpr;
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompilePipeSummary, tokenName, viewClassName} from '../compile_metadata';
|
||||
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
|
||||
import {EventHandlerVars, LegacyNameResolver} from '../compiler_util/expression_converter';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import * as o from '../output/output_ast';
|
||||
@ -33,7 +33,7 @@ export class CompileViewRootNode {
|
||||
public ngContentIndex?: number) {}
|
||||
}
|
||||
|
||||
export class CompileView implements NameResolver {
|
||||
export class CompileView implements LegacyNameResolver {
|
||||
public viewType: ViewType;
|
||||
public viewQueries: Map<any, CompileQuery[]>;
|
||||
|
||||
|
@ -102,8 +102,8 @@ function generateHandleEventMethod(
|
||||
});
|
||||
boundEvents.forEach((renderEvent, renderEventIdx) => {
|
||||
const evalResult = convertActionBinding(
|
||||
compileElement.view, compileElement.view, compileElement.view.componentContext,
|
||||
renderEvent.handler, `sub_${renderEventIdx}`);
|
||||
compileElement.view, compileElement.view.componentContext, renderEvent.handler,
|
||||
`sub_${renderEventIdx}`);
|
||||
const trueStmts = evalResult.stmts;
|
||||
if (evalResult.allowDefault) {
|
||||
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {SecurityContext} from '@angular/core';
|
||||
|
||||
import {createCheckBindingField} from '../compiler_util/binding_util';
|
||||
import {convertPropertyBinding} from '../compiler_util/expression_converter';
|
||||
import {legacyConvertPropertyBinding} from '../compiler_util/expression_converter';
|
||||
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from '../compiler_util/render_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
@ -26,7 +26,7 @@ import {getHandleEventMethodName} from './util';
|
||||
export function bindRenderText(
|
||||
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
|
||||
const valueField = createCheckBindingField(view);
|
||||
const evalResult = convertPropertyBinding(
|
||||
const evalResult = legacyConvertPropertyBinding(
|
||||
view, view, view.componentContext, boundText.value, valueField.bindingId);
|
||||
if (!evalResult) {
|
||||
return null;
|
||||
@ -53,7 +53,7 @@ export function bindRenderInputs(
|
||||
boundProps.forEach((boundProp) => {
|
||||
const bindingField = createCheckBindingField(view);
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||
const evalResult = convertPropertyBinding(
|
||||
const evalResult = legacyConvertPropertyBinding(
|
||||
view, view, compileElement.view.componentContext, boundProp.value, bindingField.bindingId);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
@ -123,7 +123,7 @@ export function bindDirectiveInputs(
|
||||
const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`;
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
||||
const evalResult =
|
||||
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
|
||||
legacyConvertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
||||
import {legacyCreateSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
||||
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
|
||||
@ -586,7 +586,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||||
stmts.push(new o.IfStmt(o.not(ViewProperties.throwOnChange), afterViewStmts));
|
||||
}
|
||||
|
||||
const varStmts = createSharedBindingVariablesIfNeeded(stmts);
|
||||
const varStmts = legacyCreateSharedBindingVariablesIfNeeded(stmts);
|
||||
return varStmts.concat(stmts);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {ChangeDetectionStrategy} from '@angular/core';
|
||||
|
||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenReference} from '../compile_metadata';
|
||||
import {EventHandlerVars, NameResolver, convertActionBinding, convertPropertyBinding} from '../compiler_util/expression_converter';
|
||||
import {BuiltinConverter, BuiltinConverterFactory, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
||||
import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
|
||||
@ -25,6 +25,7 @@ import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDep
|
||||
|
||||
const CLASS_ATTR = 'class';
|
||||
const STYLE_ATTR = 'style';
|
||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||
|
||||
@CompilerInjectable()
|
||||
export class ViewCompilerNext extends ViewCompiler {
|
||||
@ -35,7 +36,7 @@ export class ViewCompilerNext extends ViewCompiler {
|
||||
|
||||
compileComponent(
|
||||
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
|
||||
pipes: CompilePipeSummary[],
|
||||
usedPipes: CompilePipeSummary[],
|
||||
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
|
||||
const compName = identifierName(component.type) + (component.isHost ? `_Host` : '');
|
||||
|
||||
@ -44,7 +45,7 @@ export class ViewCompilerNext extends ViewCompiler {
|
||||
const viewBuilderFactory = (parent: ViewBuilder): ViewBuilder => {
|
||||
const embeddedViewIndex = embeddedViewCount++;
|
||||
const viewName = `view_${compName}_${embeddedViewIndex}`;
|
||||
return new ViewBuilder(parent, viewName, viewBuilderFactory);
|
||||
return new ViewBuilder(parent, viewName, usedPipes, viewBuilderFactory);
|
||||
};
|
||||
|
||||
const visitor = viewBuilderFactory(null);
|
||||
@ -80,20 +81,31 @@ const NODE_INDEX_VAR = o.variable('nodeIndex');
|
||||
const EVENT_NAME_VAR = o.variable('eventName');
|
||||
const ALLOW_DEFAULT_VAR = o.variable(`allowDefault`);
|
||||
|
||||
class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverterFactory {
|
||||
private nodeDefs: o.Expression[] = [];
|
||||
private purePipeNodeIndices: {[pipeName: string]: number} = {};
|
||||
private refNodeIndices: {[refName: string]: number} = {};
|
||||
private variables: VariableAst[] = [];
|
||||
private children: ViewBuilder[] = [];
|
||||
private updateExpressions: UpdateExpression[] = [];
|
||||
private updateDirectivesExpressions: UpdateExpression[] = [];
|
||||
private updateRendererExpressions: UpdateExpression[] = [];
|
||||
private handleEventExpressions: HandleEventExpression[] = [];
|
||||
|
||||
constructor(
|
||||
private parent: ViewBuilder, public viewName: string,
|
||||
private parent: ViewBuilder, public viewName: string, private usedPipes: CompilePipeSummary[],
|
||||
private viewBuilderFactory: ViewBuilderFactory) {}
|
||||
|
||||
visitAll(variables: VariableAst[], astNodes: TemplateAst[], elementDepth: number) {
|
||||
this.variables = variables;
|
||||
// create the pipes for the pure pipes immediately, so that we know their indices.
|
||||
if (!this.parent) {
|
||||
this.usedPipes.forEach((pipe) => {
|
||||
if (pipe.pure) {
|
||||
this.purePipeNodeIndices[pipe.name] = this._createPipe(pipe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
templateVisitAll(this, astNodes, {elementDepth});
|
||||
if (astNodes.length === 0 || (this.parent && hasViewContainer(astNodes[astNodes.length - 1]))) {
|
||||
// if the view is empty, or an embedded view has a view container as last root nde,
|
||||
@ -108,46 +120,16 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
const compType = o.importType(component.type);
|
||||
this.children.forEach((child) => { child.build(component, targetStatements); });
|
||||
|
||||
const updateStmts: o.Statement[] = [];
|
||||
let updateBindingCount = 0;
|
||||
this.updateExpressions
|
||||
.forEach(
|
||||
({expressions, nodeIndex}) => {
|
||||
const exprs = expressions.map(({context, value}) => {
|
||||
const bindingId = `${updateBindingCount++}`;
|
||||
const {stmts, currValExpr} =
|
||||
convertPropertyBinding(null, this, context, value, bindingId);
|
||||
updateStmts.push(...stmts);
|
||||
return currValExpr;
|
||||
});
|
||||
if (exprs.length > 10) {
|
||||
updateStmts.push(
|
||||
CHECK_VAR
|
||||
.callFn([
|
||||
VIEW_VAR, o.literal(nodeIndex),
|
||||
o.literal(viewEngine.ArgumentType.Dynamic), o.literalArr(exprs)
|
||||
])
|
||||
.toStmt());
|
||||
} else {
|
||||
updateStmts.push(
|
||||
CHECK_VAR.callFn((<o.Expression[]>[VIEW_VAR, o.literal(nodeIndex), o.literal(viewEngine.ArgumentType.Inline)]).concat(exprs)).toStmt());
|
||||
}
|
||||
});
|
||||
let updateFn: o.Expression;
|
||||
if (updateStmts.length > 0) {
|
||||
updateFn = o.fn(
|
||||
[new o.FnParam(CHECK_VAR.name), new o.FnParam(VIEW_VAR.name)],
|
||||
[COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(compType), ...updateStmts]);
|
||||
} else {
|
||||
updateFn = o.NULL_EXPR;
|
||||
}
|
||||
const updateDirectivesFn = this._createUpdateFn(this.updateDirectivesExpressions, compType);
|
||||
const updateRendererFn = this._createUpdateFn(this.updateRendererExpressions, compType);
|
||||
|
||||
const handleEventStmts: o.Statement[] = [];
|
||||
let handleEventBindingCount = 0;
|
||||
this.handleEventExpressions.forEach(({expression, context, nodeIndex, eventName}) => {
|
||||
const bindingId = `${handleEventBindingCount++}`;
|
||||
const nameResolver = context === COMP_VAR ? this : null;
|
||||
const {stmts, allowDefault} =
|
||||
convertActionBinding(null, this, context, expression, bindingId);
|
||||
convertActionBinding(nameResolver, context, expression, bindingId);
|
||||
const trueStmts = stmts;
|
||||
if (allowDefault) {
|
||||
trueStmts.push(ALLOW_DEFAULT_VAR.set(allowDefault.and(ALLOW_DEFAULT_VAR)).toStmt());
|
||||
@ -181,13 +163,39 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
const viewFactory = new o.DeclareFunctionStmt(
|
||||
this.viewName, [],
|
||||
[new o.ReturnStatement(o.importExpr(createIdentifier(Identifiers.viewDef)).callFn([
|
||||
o.literal(viewFlags), o.literalArr(this.nodeDefs), updateFn, handleEventFn
|
||||
o.literal(viewFlags), o.literalArr(this.nodeDefs), updateDirectivesFn, updateRendererFn,
|
||||
handleEventFn
|
||||
]))]);
|
||||
|
||||
targetStatements.push(viewFactory);
|
||||
return targetStatements;
|
||||
}
|
||||
|
||||
private _createUpdateFn(expressions: UpdateExpression[], compType: o.Type): o.Expression {
|
||||
const updateStmts: o.Statement[] = [];
|
||||
let updateBindingCount = 0;
|
||||
expressions.forEach(({expressions, nodeIndex}) => {
|
||||
const exprs = expressions.map(({context, value}) => {
|
||||
const bindingId = `${updateBindingCount++}`;
|
||||
const nameResolver = context === COMP_VAR ? this : null;
|
||||
const {stmts, currValExpr} =
|
||||
convertPropertyBinding(nameResolver, context, value, bindingId);
|
||||
updateStmts.push(...stmts);
|
||||
return currValExpr;
|
||||
});
|
||||
updateStmts.push(callCheckStmt(nodeIndex, exprs).toStmt());
|
||||
});
|
||||
let updateFn: o.Expression;
|
||||
if (updateStmts.length > 0) {
|
||||
updateFn = o.fn(
|
||||
[new o.FnParam(CHECK_VAR.name), new o.FnParam(VIEW_VAR.name)],
|
||||
[COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(compType), ...updateStmts]);
|
||||
} else {
|
||||
updateFn = o.NULL_EXPR;
|
||||
}
|
||||
return updateFn;
|
||||
}
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any {}
|
||||
|
||||
visitText(ast: TextAst, context: any): any {
|
||||
@ -199,17 +207,20 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
|
||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
||||
const nodeIndex = this.nodeDefs.length;
|
||||
// reserve the space in the nodeDefs array
|
||||
this.nodeDefs.push(null);
|
||||
|
||||
const astWithSource = <ASTWithSource>ast.value;
|
||||
const inter = <Interpolation>astWithSource.ast;
|
||||
this.updateExpressions.push({
|
||||
nodeIndex,
|
||||
expressions: inter.expressions.map((expr) => { return {context: COMP_VAR, value: expr}; })
|
||||
});
|
||||
|
||||
this._addUpdateExpressions(
|
||||
nodeIndex, inter.expressions.map((expr) => { return {context: COMP_VAR, value: expr}; }),
|
||||
this.updateRendererExpressions);
|
||||
|
||||
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
|
||||
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
|
||||
this.nodeDefs[nodeIndex] = o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
|
||||
o.NULL_EXPR, o.literalArr(inter.strings.map(s => o.literal(s)))
|
||||
]));
|
||||
]);
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: {elementDepth: number}): any {
|
||||
@ -219,11 +230,12 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
|
||||
const {flags, queryMatchesExpr} = this._visitElementOrTemplate(nodeIndex, ast, context);
|
||||
|
||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||
const childVisitor = this.viewBuilderFactory(this);
|
||||
this.children.push(childVisitor);
|
||||
childVisitor.visitAll(ast.variables, ast.children, context.elementDepth + 1);
|
||||
|
||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||
|
||||
// anchorDef(
|
||||
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
||||
// childCount: number, templateFactory?: ViewDefinitionFactory): NodeDef;
|
||||
@ -243,12 +255,9 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
|
||||
templateVisitAll(this, ast.children, {elementDepth: context.elementDepth + 1});
|
||||
|
||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||
|
||||
ast.inputs.forEach((inputAst) => {
|
||||
hostBindings.push({context: COMP_VAR, value: (<ASTWithSource>inputAst.value).ast});
|
||||
});
|
||||
this.updateExpressions.push({nodeIndex, expressions: hostBindings});
|
||||
ast.inputs.forEach(
|
||||
(inputAst) => { hostBindings.push({context: COMP_VAR, value: inputAst.value}); });
|
||||
this._addUpdateExpressions(nodeIndex, hostBindings, this.updateRendererExpressions);
|
||||
|
||||
const inputDefs = elementBindingDefs(ast.inputs);
|
||||
ast.directives.forEach(
|
||||
@ -258,6 +267,8 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
o.literal(eventName);
|
||||
});
|
||||
|
||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||
|
||||
// elementDef(
|
||||
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
||||
// childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
|
||||
@ -355,13 +366,10 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
ast.outputs.forEach(
|
||||
(outputAst) => { hostEvents.push({context: COMP_VAR, eventAst: outputAst}); });
|
||||
hostEvents.forEach((hostEvent) => {
|
||||
this.handleEventExpressions.push({
|
||||
nodeIndex,
|
||||
context: hostEvent.context,
|
||||
eventName:
|
||||
viewEngine.elementEventFullName(hostEvent.eventAst.target, hostEvent.eventAst.name),
|
||||
expression: (<ASTWithSource>hostEvent.eventAst.handler).ast
|
||||
});
|
||||
this._addHandleEventExpression(
|
||||
nodeIndex, hostEvent.context,
|
||||
viewEngine.elementEventFullName(hostEvent.eventAst.target, hostEvent.eventAst.name),
|
||||
hostEvent.eventAst.handler);
|
||||
});
|
||||
|
||||
return {
|
||||
@ -383,6 +391,22 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
// reserve the space in the nodeDefs array so we can add children
|
||||
this.nodeDefs.push(null);
|
||||
|
||||
directiveAst.directive.queries.forEach((query, queryIndex) => {
|
||||
const queryId: QueryId = {elementDepth, directiveIndex, queryIndex};
|
||||
const bindingType =
|
||||
query.first ? viewEngine.QueryBindingType.First : viewEngine.QueryBindingType.All;
|
||||
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.queryDef)).callFn([
|
||||
o.literal(viewEngine.NodeFlags.HasContentQuery), o.literal(calcQueryId(queryId)),
|
||||
new o.LiteralMapExpr([new o.LiteralMapEntry(query.propertyName, o.literal(bindingType))])
|
||||
]));
|
||||
});
|
||||
|
||||
// Note: the operation below might also create new nodeDefs,
|
||||
// but we don't want them to be a child of a directive,
|
||||
// as they might be a provider/pipe on their own.
|
||||
// I.e. we only allow queries as children of directives nodes.
|
||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||
|
||||
const {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
|
||||
this._visitProviderOrDirective(providerAst, queryMatches);
|
||||
|
||||
@ -415,11 +439,10 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
}
|
||||
});
|
||||
if (directiveAst.inputs.length) {
|
||||
this.updateExpressions.push({
|
||||
nodeIndex,
|
||||
expressions: directiveAst.inputs.map(
|
||||
input => { return {context: COMP_VAR, value: (<ASTWithSource>input.value).ast}; })
|
||||
});
|
||||
this._addUpdateExpressions(
|
||||
nodeIndex,
|
||||
directiveAst.inputs.map((input) => { return {context: COMP_VAR, value: input.value}; }),
|
||||
this.updateDirectivesExpressions);
|
||||
}
|
||||
|
||||
const dirContextExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
||||
@ -434,19 +457,6 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
const hostEvents = directiveAst.hostEvents.map(
|
||||
(hostEventAst) => { return {context: dirContextExpr, eventAst: hostEventAst}; });
|
||||
|
||||
const childCount = directiveAst.directive.queries.length;
|
||||
directiveAst.directive.queries.forEach((query, queryIndex) => {
|
||||
const queryId: QueryId = {elementDepth, directiveIndex, queryIndex};
|
||||
const bindingType =
|
||||
query.first ? viewEngine.QueryBindingType.First : viewEngine.QueryBindingType.All;
|
||||
// queryDef(
|
||||
// flags: NodeFlags, id: string, bindings: {[propName: string]: QueryBindingType}): NodeDef
|
||||
// {
|
||||
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.queryDef)).callFn([
|
||||
o.literal(viewEngine.NodeFlags.HasContentQuery), o.literal(calcQueryId(queryId)),
|
||||
new o.LiteralMapExpr([new o.LiteralMapEntry(query.propertyName, o.literal(bindingType))])
|
||||
]));
|
||||
});
|
||||
|
||||
// directiveDef(
|
||||
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor:
|
||||
@ -514,10 +524,6 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
return {flags, queryMatchExprs, providerExpr, providerType, depsExpr};
|
||||
}
|
||||
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression {
|
||||
throw new Error('Pipes are not yet supported!');
|
||||
}
|
||||
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name == EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
@ -536,12 +542,123 @@ class ViewBuilder implements TemplateAstVisitor, NameResolver {
|
||||
// check variables
|
||||
const varAst = currBuilder.variables.find((varAst) => varAst.name === name);
|
||||
if (varAst) {
|
||||
return currViewExpr.prop('context').prop(varAst.value);
|
||||
const varValue = varAst.value || IMPLICIT_TEMPLATE_VAR;
|
||||
return currViewExpr.prop('context').prop(varValue);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
createLiteralArrayConverter(argCount: number): BuiltinConverter {
|
||||
if (argCount === 0) {
|
||||
const valueExpr = o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
|
||||
return () => valueExpr;
|
||||
}
|
||||
|
||||
const nodeIndex = this.nodeDefs.length;
|
||||
// pureArrayDef(argCount: number): NodeDef;
|
||||
const nodeDef =
|
||||
o.importExpr(createIdentifier(Identifiers.pureArrayDef)).callFn([o.literal(argCount)]);
|
||||
this.nodeDefs.push(nodeDef);
|
||||
|
||||
return (args: o.Expression[]) => callCheckStmt(nodeIndex, args);
|
||||
}
|
||||
createLiteralMapConverter(keys: string[]): BuiltinConverter {
|
||||
if (keys.length === 0) {
|
||||
const valueExpr = o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
|
||||
return () => valueExpr;
|
||||
}
|
||||
|
||||
const nodeIndex = this.nodeDefs.length;
|
||||
// function pureObjectDef(propertyNames: string[]): NodeDef
|
||||
const nodeDef = o.importExpr(createIdentifier(Identifiers.pureObjectDef)).callFn([o.literalArr(
|
||||
keys.map(key => o.literal(key)))]);
|
||||
this.nodeDefs.push(nodeDef);
|
||||
|
||||
return (args: o.Expression[]) => callCheckStmt(nodeIndex, args);
|
||||
}
|
||||
createPipeConverter(name: string, argCount: number): BuiltinConverter {
|
||||
const pipe = this._findPipe(name);
|
||||
if (pipe.pure) {
|
||||
const nodeIndex = this.nodeDefs.length;
|
||||
// function purePipeDef(argCount: number): NodeDef;
|
||||
const nodeDef =
|
||||
o.importExpr(createIdentifier(Identifiers.purePipeDef)).callFn([o.literal(argCount)]);
|
||||
this.nodeDefs.push(nodeDef);
|
||||
|
||||
// find underlying pipe in the component view
|
||||
let compViewExpr: o.Expression = VIEW_VAR;
|
||||
let compBuilder: ViewBuilder = this;
|
||||
while (compBuilder.parent) {
|
||||
compBuilder = compBuilder.parent;
|
||||
compViewExpr = compViewExpr.prop('parent');
|
||||
}
|
||||
const pipeNodeIndex = compBuilder.purePipeNodeIndices[name];
|
||||
const pipeValueExpr: o.Expression =
|
||||
o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
||||
compViewExpr, o.literal(pipeNodeIndex)
|
||||
]);
|
||||
|
||||
return (args: o.Expression[]) =>
|
||||
callUnwrapValue(callCheckStmt(nodeIndex, [pipeValueExpr].concat(args)));
|
||||
} else {
|
||||
const nodeIndex = this._createPipe(pipe);
|
||||
const nodeValueExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
||||
VIEW_VAR, o.literal(nodeIndex)
|
||||
]);
|
||||
|
||||
return (args: o.Expression[]) => callUnwrapValue(nodeValueExpr.callMethod('transform', args));
|
||||
}
|
||||
}
|
||||
|
||||
private _findPipe(name: string): CompilePipeSummary {
|
||||
return this.usedPipes.find((pipeSummary) => pipeSummary.name === name);
|
||||
}
|
||||
|
||||
private _createPipe(pipe: CompilePipeSummary): number {
|
||||
const nodeIndex = this.nodeDefs.length;
|
||||
let flags = viewEngine.NodeFlags.None;
|
||||
pipe.type.lifecycleHooks.forEach((lifecycleHook) => {
|
||||
// for pipes, we only support ngOnDestroy
|
||||
if (lifecycleHook === LifecycleHooks.OnDestroy) {
|
||||
flags |= lifecycleHookToNodeFlag(lifecycleHook);
|
||||
}
|
||||
});
|
||||
|
||||
const depExprs = pipe.type.diDeps.map(depDef);
|
||||
// function pipeDef(
|
||||
// flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef
|
||||
const nodeDef = o.importExpr(createIdentifier(Identifiers.pipeDef)).callFn([
|
||||
o.literal(flags), o.importExpr(pipe.type), o.literalArr(depExprs)
|
||||
]);
|
||||
this.nodeDefs.push(nodeDef);
|
||||
return nodeIndex;
|
||||
}
|
||||
|
||||
// Attention: This might create new nodeDefs (for pipes and literal arrays and literal maps)!
|
||||
private _addUpdateExpressions(
|
||||
nodeIndex: number, expressions: {context: o.Expression, value: AST}[],
|
||||
target: UpdateExpression[]) {
|
||||
if (expressions.length === 0) {
|
||||
return;
|
||||
}
|
||||
const transformedExpressions = expressions.map(({context, value}) => {
|
||||
if (value instanceof ASTWithSource) {
|
||||
value = value.ast;
|
||||
}
|
||||
return {context, value: convertPropertyBindingBuiltins(this, value)};
|
||||
});
|
||||
target.push({nodeIndex, expressions: transformedExpressions});
|
||||
}
|
||||
|
||||
private _addHandleEventExpression(
|
||||
nodeIndex: number, context: o.Expression, eventName: string, expression: AST) {
|
||||
if (expression instanceof ASTWithSource) {
|
||||
expression = expression.ast;
|
||||
}
|
||||
this.handleEventExpressions.push({nodeIndex, context, eventName, expression});
|
||||
}
|
||||
|
||||
visitDirective(ast: DirectiveAst, context: {usedEvents: Set<string>}): any {}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
|
||||
visitReference(ast: ReferenceAst, context: any): any {}
|
||||
@ -736,3 +853,19 @@ function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: s
|
||||
return attrValue2;
|
||||
}
|
||||
}
|
||||
|
||||
function callCheckStmt(nodeIndex: number, exprs: o.Expression[]): o.Expression {
|
||||
if (exprs.length > 10) {
|
||||
return CHECK_VAR.callFn([
|
||||
VIEW_VAR, o.literal(nodeIndex), o.literal(viewEngine.ArgumentType.Dynamic),
|
||||
o.literalArr(exprs)
|
||||
]);
|
||||
} else {
|
||||
return CHECK_VAR.callFn(
|
||||
[VIEW_VAR, o.literal(nodeIndex), o.literal(viewEngine.ArgumentType.Inline), ...exprs]);
|
||||
}
|
||||
}
|
||||
|
||||
function callUnwrapValue(expr: o.Expression): o.Expression {
|
||||
return o.importExpr(createIdentifier(Identifiers.unwrapValue)).callFn([expr]);
|
||||
}
|
Reference in New Issue
Block a user