Skip to content

Commit e4e9dbe

Browse files
tboschmhevery
authored andcommitted
feat(compiler): integrate compiler with view engine - change detection tests work (angular#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 angular#14013 PR Close angular#14412
1 parent 1dc9be4 commit e4e9dbe

39 files changed

+953
-779
lines changed

modules/@angular/compiler/src/aot/compiler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,14 @@ export class AotCompiler {
205205
const pipes = ngModule.transitiveModule.pipes.map(
206206
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
207207

208-
const parsedTemplate = this._templateParser.parse(
208+
const {template: parsedTemplate, pipes: usedPipes} = this._templateParser.parse(
209209
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
210210
identifierName(compMeta.type));
211211
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
212212
const compiledAnimations =
213213
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
214214
const viewResult = this._viewCompiler.compileComponent(
215-
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
215+
compMeta, parsedTemplate, stylesExpr, usedPipes, compiledAnimations);
216216
if (componentStyles) {
217217
targetStatements.push(
218218
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));

modules/@angular/compiler/src/compiler_util/expression_converter.ts

Lines changed: 183 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -17,58 +17,9 @@ import {createPureProxy} from './identifier_util';
1717

1818
const VAL_UNWRAPPER_VAR = o.variable(`valUnwrapper`);
1919

20-
export interface NameResolver {
21-
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
22-
getLocal(name: string): o.Expression;
23-
}
24-
2520
export class EventHandlerVars { static event = o.variable('$event'); }
2621

27-
export class ConvertPropertyBindingResult {
28-
constructor(
29-
public stmts: o.Statement[], public currValExpr: o.Expression,
30-
public forceUpdate: o.Expression) {}
31-
}
32-
33-
/**
34-
* Converts the given expression AST into an executable output AST, assuming the expression is
35-
* used in a property binding.
36-
*/
37-
export function convertPropertyBinding(
38-
builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
39-
expression: cdAst.AST, bindingId: string): ConvertPropertyBindingResult {
40-
const currValExpr = createCurrValueExpr(bindingId);
41-
const stmts: o.Statement[] = [];
42-
if (!nameResolver) {
43-
nameResolver = new DefaultNameResolver();
44-
}
45-
const visitor = new _AstToIrVisitor(
46-
builder, nameResolver, implicitReceiver, VAL_UNWRAPPER_VAR, bindingId, false);
47-
const outputExpr: o.Expression = expression.visit(visitor, _Mode.Expression);
48-
49-
if (!outputExpr) {
50-
// e.g. an empty expression was given
51-
return null;
52-
}
53-
54-
if (visitor.temporaryCount) {
55-
for (let i = 0; i < visitor.temporaryCount; i++) {
56-
stmts.push(temporaryDeclaration(bindingId, i));
57-
}
58-
}
59-
60-
if (visitor.needsValueUnwrapper) {
61-
const initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
62-
stmts.push(initValueUnwrapperStmt);
63-
}
64-
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
65-
if (visitor.needsValueUnwrapper) {
66-
return new ConvertPropertyBindingResult(
67-
stmts, currValExpr, VAL_UNWRAPPER_VAR.prop('hasWrappedValue'));
68-
} else {
69-
return new ConvertPropertyBindingResult(stmts, currValExpr, null);
70-
}
71-
}
22+
export interface LocalResolver { getLocal(name: string): o.Expression; }
7223

7324
export class ConvertActionBindingResult {
7425
constructor(public stmts: o.Statement[], public allowDefault: o.ReadVarExpr) {}
@@ -79,15 +30,31 @@ export class ConvertActionBindingResult {
7930
* used in an action binding (e.g. an event handler).
8031
*/
8132
export function convertActionBinding(
82-
builder: ClassBuilder, nameResolver: NameResolver, implicitReceiver: o.Expression,
83-
action: cdAst.AST, bindingId: string): ConvertActionBindingResult {
84-
if (!nameResolver) {
85-
nameResolver = new DefaultNameResolver();
86-
}
87-
const visitor =
88-
new _AstToIrVisitor(builder, nameResolver, implicitReceiver, null, bindingId, true);
33+
localResolver: LocalResolver, implicitReceiver: o.Expression, action: cdAst.AST,
34+
bindingId: string): ConvertActionBindingResult {
35+
if (!localResolver) {
36+
localResolver = new DefaultLocalResolver();
37+
}
38+
const actionWithoutBuiltins = convertPropertyBindingBuiltins(
39+
{
40+
createLiteralArrayConverter: (argCount: number) => {
41+
// Note: no caching for literal arrays in actions.
42+
return (args: o.Expression[]) => o.literalArr(args);
43+
},
44+
createLiteralMapConverter: (keys: string[]) => {
45+
// Note: no caching for literal maps in actions.
46+
return (args: o.Expression[]) =>
47+
o.literalMap(<[string, o.Expression][]>keys.map((key, i) => [key, args[i]]));
48+
},
49+
createPipeConverter: (name: string) => {
50+
throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
51+
}
52+
},
53+
action);
54+
55+
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId);
8956
const actionStmts: o.Statement[] = [];
90-
flattenStatements(action.visit(visitor, _Mode.Statement), actionStmts);
57+
flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
9158
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
9259
const lastIndex = actionStmts.length - 1;
9360
let preventDefaultVar: o.ReadVarExpr = null;
@@ -106,11 +73,105 @@ export function convertActionBinding(
10673
return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
10774
}
10875

76+
export interface BuiltinConverter { (args: o.Expression[]): o.Expression; }
77+
78+
export interface BuiltinConverterFactory {
79+
createLiteralArrayConverter(argCount: number): BuiltinConverter;
80+
createLiteralMapConverter(keys: string[]): BuiltinConverter;
81+
createPipeConverter(name: string, argCount: number): BuiltinConverter;
82+
}
83+
84+
export function convertPropertyBindingBuiltins(
85+
converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
86+
return convertBuiltins(converterFactory, ast);
87+
}
88+
89+
export class ConvertPropertyBindingResult {
90+
constructor(public stmts: o.Statement[], public currValExpr: o.Expression) {}
91+
}
92+
93+
/**
94+
* Converts the given expression AST into an executable output AST, assuming the expression
95+
* is used in property binding. The expression has to be preprocessed via
96+
* `convertPropertyBindingBuiltins`.
97+
*/
98+
export function convertPropertyBinding(
99+
localResolver: LocalResolver, implicitReceiver: o.Expression,
100+
expressionWithoutBuiltins: cdAst.AST, bindingId: string): ConvertPropertyBindingResult {
101+
if (!localResolver) {
102+
localResolver = new DefaultLocalResolver();
103+
}
104+
const currValExpr = createCurrValueExpr(bindingId);
105+
const stmts: o.Statement[] = [];
106+
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId);
107+
const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
108+
109+
if (visitor.temporaryCount) {
110+
for (let i = 0; i < visitor.temporaryCount; i++) {
111+
stmts.push(temporaryDeclaration(bindingId, i));
112+
}
113+
}
114+
115+
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
116+
return new ConvertPropertyBindingResult(stmts, currValExpr);
117+
}
118+
119+
120+
export class LegacyConvertPropertyBindingResult implements ConvertPropertyBindingResult {
121+
constructor(
122+
public stmts: o.Statement[], public currValExpr: o.Expression,
123+
public forceUpdate: o.Expression) {}
124+
}
125+
126+
export interface LegacyNameResolver {
127+
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
128+
getLocal(name: string): o.Expression;
129+
}
130+
131+
/**
132+
* Converts the given expression AST into an executable output AST, assuming the expression is
133+
* used in a property binding.
134+
*/
135+
export function legacyConvertPropertyBinding(
136+
builder: ClassBuilder, nameResolver: LegacyNameResolver, implicitReceiver: o.Expression,
137+
expression: cdAst.AST, bindingId: string): LegacyConvertPropertyBindingResult {
138+
if (!nameResolver) {
139+
nameResolver = new LegacyDefaultNameResolver();
140+
}
141+
let needsValueUnwrapper = false;
142+
const expressionWithoutBuiltins = convertBuiltins(
143+
{
144+
createLiteralArrayConverter: (argCount: number) => {
145+
return (args: o.Expression[]) => legacyCreateCachedLiteralArray(builder, args);
146+
},
147+
createLiteralMapConverter: (keys: string[]) => {
148+
return (args: o.Expression[]) => legacyCreateCachedLiteralMap(
149+
builder, <[string, o.Expression][]>keys.map((key, i) => [key, args[i]]));
150+
},
151+
createPipeConverter: (name: string) => {
152+
needsValueUnwrapper = true;
153+
return (args: o.Expression[]) => VAL_UNWRAPPER_VAR.callMethod(
154+
'unwrap', [nameResolver.callPipe(name, args[0], args.slice(1))]);
155+
}
156+
},
157+
expression);
158+
159+
const {stmts, currValExpr} =
160+
convertPropertyBinding(nameResolver, implicitReceiver, expressionWithoutBuiltins, bindingId);
161+
let forceUpdate: o.Expression = null;
162+
if (needsValueUnwrapper) {
163+
const initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
164+
stmts.unshift(initValueUnwrapperStmt);
165+
forceUpdate = VAL_UNWRAPPER_VAR.prop('hasWrappedValue');
166+
}
167+
return new LegacyConvertPropertyBindingResult(stmts, currValExpr, forceUpdate);
168+
}
169+
109170
/**
110171
* Creates variables that are shared by multiple calls to `convertActionBinding` /
111172
* `convertPropertyBinding`
112173
*/
113-
export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
174+
export function legacyCreateSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
114175
const unwrapperStmts: o.Statement[] = [];
115176
const readVars = o.findReadVarNames(stmts);
116177
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
@@ -122,6 +183,11 @@ export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.St
122183
return unwrapperStmts;
123184
}
124185

186+
function convertBuiltins(converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
187+
const visitor = new _BuiltinAstConverter(converterFactory);
188+
return ast.visit(visitor);
189+
}
190+
125191
function temporaryName(bindingId: string, temporaryNumber: number): string {
126192
return `tmp_${bindingId}_${temporaryNumber}`;
127193
}
@@ -162,17 +228,34 @@ function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expressi
162228
}
163229
}
164230

231+
class _BuiltinAstConverter extends cdAst.AstTransformer {
232+
constructor(private _converterFactory: BuiltinConverterFactory) { super(); }
233+
visitPipe(ast: cdAst.BindingPipe, context: any): any {
234+
const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
235+
return new BuiltinFunctionCall(
236+
ast.span, args, this._converterFactory.createPipeConverter(ast.name, args.length));
237+
}
238+
visitLiteralArray(ast: cdAst.LiteralArray, context: any): any {
239+
const args = ast.expressions.map(ast => ast.visit(this, context));
240+
return new BuiltinFunctionCall(
241+
ast.span, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
242+
}
243+
visitLiteralMap(ast: cdAst.LiteralMap, context: any): any {
244+
const args = ast.values.map(ast => ast.visit(this, context));
245+
return new BuiltinFunctionCall(
246+
ast.span, args, this._converterFactory.createLiteralMapConverter(ast.keys));
247+
}
248+
}
249+
165250
class _AstToIrVisitor implements cdAst.AstVisitor {
166251
private _nodeMap = new Map<cdAst.AST, cdAst.AST>();
167252
private _resultMap = new Map<cdAst.AST, o.Expression>();
168253
private _currentTemporary: number = 0;
169-
public needsValueUnwrapper: boolean = false;
170254
public temporaryCount: number = 0;
171255

172256
constructor(
173-
private _builder: ClassBuilder, private _nameResolver: NameResolver,
174-
private _implicitReceiver: o.Expression, private _valueUnwrapper: o.ReadVarExpr,
175-
private bindingId: string, private isAction: boolean) {}
257+
private _localResolver: LocalResolver, private _implicitReceiver: o.Expression,
258+
private bindingId: string) {}
176259

177260
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
178261
let op: o.BinaryOperator;
@@ -246,20 +329,19 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
246329
}
247330

248331
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
249-
const input = this.visit(ast.exp, _Mode.Expression);
250-
const args = this.visitAll(ast.args, _Mode.Expression);
251-
const value = this._nameResolver.callPipe(ast.name, input, args);
252-
if (!value) {
253-
throw new Error(`Illegal state: Pipe ${ast.name} is not allowed here!`);
254-
}
255-
this.needsValueUnwrapper = true;
256-
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
332+
throw new Error(
333+
`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
257334
}
258335

259336
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
260-
return convertToStatementIfNeeded(
261-
mode,
262-
this.visit(ast.target, _Mode.Expression).callFn(this.visitAll(ast.args, _Mode.Expression)));
337+
const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
338+
let fnResult: o.Expression;
339+
if (ast instanceof BuiltinFunctionCall) {
340+
fnResult = ast.converter(convertedArgs);
341+
} else {
342+
fnResult = this.visit(ast.target, _Mode.Expression).callFn(convertedArgs);
343+
}
344+
return convertToStatementIfNeeded(mode, fnResult);
263345
}
264346

265347
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
@@ -301,32 +383,18 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
301383
}
302384

303385
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
304-
const parts = this.visitAll(ast.expressions, mode);
305-
const literalArr =
306-
this.isAction ? o.literalArr(parts) : createCachedLiteralArray(this._builder, parts);
307-
return convertToStatementIfNeeded(mode, literalArr);
386+
throw new Error(`Illegal State: literal arrays should have been converted into functions`);
308387
}
309388

310389
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
311-
const parts: any[] = [];
312-
for (let i = 0; i < ast.keys.length; i++) {
313-
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
314-
}
315-
const literalMap =
316-
this.isAction ? o.literalMap(parts) : createCachedLiteralMap(this._builder, parts);
317-
return convertToStatementIfNeeded(mode, literalMap);
390+
throw new Error(`Illegal State: literal maps should have been converted into functions`);
318391
}
319392

320393
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
321394
return convertToStatementIfNeeded(mode, o.literal(ast.value));
322395
}
323396

324-
private _getLocal(name: string): o.Expression {
325-
if (this.isAction && name == EventHandlerVars.event.name) {
326-
return EventHandlerVars.event;
327-
}
328-
return this._nameResolver.getLocal(name);
329-
}
397+
private _getLocal(name: string): o.Expression { return this._localResolver.getLocal(name); }
330398

331399
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
332400
const leftMostSafe = this.leftMostSafeNode(ast);
@@ -581,7 +649,8 @@ function flattenStatements(arg: any, output: o.Statement[]) {
581649
}
582650
}
583651

584-
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
652+
function legacyCreateCachedLiteralArray(
653+
builder: ClassBuilder, values: o.Expression[]): o.Expression {
585654
if (values.length === 0) {
586655
return o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
587656
}
@@ -601,7 +670,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
601670
return proxyExpr.callFn(values);
602671
}
603672

604-
function createCachedLiteralMap(
673+
function legacyCreateCachedLiteralMap(
605674
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
606675
if (entries.length === 0) {
607676
return o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
@@ -624,10 +693,23 @@ function createCachedLiteralMap(
624693
return proxyExpr.callFn(values);
625694
}
626695

696+
class DefaultLocalResolver implements LocalResolver {
697+
getLocal(name: string): o.Expression {
698+
if (name === EventHandlerVars.event.name) {
699+
return EventHandlerVars.event;
700+
}
701+
return null;
702+
}
703+
}
627704

628-
class DefaultNameResolver implements NameResolver {
705+
class LegacyDefaultNameResolver implements LegacyNameResolver {
629706
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression { return null; }
630-
getLocal(name: string): o.Expression { return null; }
707+
getLocal(name: string): o.Expression {
708+
if (name === EventHandlerVars.event.name) {
709+
return EventHandlerVars.event;
710+
}
711+
return null;
712+
}
631713
}
632714

633715
function createCurrValueExpr(bindingId: string): o.ReadVarExpr {
@@ -646,3 +728,9 @@ function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
646728
}
647729
return null;
648730
}
731+
732+
class BuiltinFunctionCall extends cdAst.FunctionCall {
733+
constructor(span: cdAst.ParseSpan, public args: cdAst.AST[], public converter: BuiltinConverter) {
734+
super(span, null, args);
735+
}
736+
}

0 commit comments

Comments
 (0)