Skip to content

Commit 05ce944

Browse files
crisbetokirjs
authored andcommitted
refactor(compiler): support spread elements in output AST
Updates the output AST to account for spread elements.
1 parent 3583007 commit 05ce944

7 files changed

Lines changed: 83 additions & 88 deletions

File tree

packages/compiler/src/constant_pool.ts

Lines changed: 8 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -175,39 +175,6 @@ export class ConstantPool {
175175
return this.sharedConstants.get(key)!;
176176
}
177177

178-
getLiteralFactory(literal: o.LiteralArrayExpr | o.LiteralMapExpr): {
179-
literalFactory: o.Expression;
180-
literalFactoryArguments: o.Expression[];
181-
} {
182-
// Create a pure function that builds an array of a mix of constant and variable expressions
183-
if (literal instanceof o.LiteralArrayExpr) {
184-
const argumentsForKey = literal.entries.map((e) => (e.isConstant() ? e : UNKNOWN_VALUE_KEY));
185-
const key = GenericKeyFn.INSTANCE.keyOf(o.literalArr(argumentsForKey));
186-
return this._getLiteralFactory(key, literal.entries, (entries) => o.literalArr(entries));
187-
} else {
188-
const expressionForKey = o.literalMap(
189-
literal.entries.map((e) => ({
190-
key: e.key,
191-
value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
192-
quoted: e.quoted,
193-
})),
194-
);
195-
const key = GenericKeyFn.INSTANCE.keyOf(expressionForKey);
196-
return this._getLiteralFactory(
197-
key,
198-
literal.entries.map((e) => e.value),
199-
(entries) =>
200-
o.literalMap(
201-
entries.map((value, index) => ({
202-
key: literal.entries[index].key,
203-
value,
204-
quoted: literal.entries[index].quoted,
205-
})),
206-
),
207-
);
208-
}
209-
}
210-
211178
// TODO: useUniqueName(false) is necessary for naming compatibility with
212179
// TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default.
213180
getSharedFunctionReference(
@@ -246,35 +213,6 @@ export class ConstantPool {
246213
return o.variable(name);
247214
}
248215

249-
private _getLiteralFactory(
250-
key: string,
251-
values: o.Expression[],
252-
resultMap: (parameters: o.Expression[]) => o.Expression,
253-
): {literalFactory: o.Expression; literalFactoryArguments: o.Expression[]} {
254-
let literalFactory = this.literalFactories.get(key);
255-
const literalFactoryArguments = values.filter((e) => !e.isConstant());
256-
if (!literalFactory) {
257-
const resultExpressions = values.map((e, index) =>
258-
e.isConstant() ? this.getConstLiteral(e, true) : o.variable(`a${index}`),
259-
);
260-
const parameters = resultExpressions
261-
.filter(isVariable)
262-
.map((e) => new o.FnParam(e.name!, o.DYNAMIC_TYPE));
263-
const pureFunctionDeclaration = o.arrowFn(
264-
parameters,
265-
resultMap(resultExpressions),
266-
o.INFERRED_TYPE,
267-
);
268-
const name = this.freshName();
269-
this.statements.push(
270-
new o.DeclareVarStmt(name, pureFunctionDeclaration, o.INFERRED_TYPE, o.StmtModifier.Final),
271-
);
272-
literalFactory = o.variable(name);
273-
this.literalFactories.set(key, literalFactory);
274-
}
275-
return {literalFactory, literalFactoryArguments};
276-
}
277-
278216
/**
279217
* Produce a unique name in the context of this pool.
280218
*
@@ -322,11 +260,15 @@ export class GenericKeyFn implements ExpressionKeyFn {
322260
} else if (expr instanceof o.LiteralMapExpr) {
323261
const entries: string[] = [];
324262
for (const entry of expr.entries) {
325-
let key = entry.key;
326-
if (entry.quoted) {
327-
key = `"${key}"`;
263+
if (entry instanceof o.LiteralMapSpreadAssignment) {
264+
entries.push('...' + this.keyOf(entry.expression));
265+
} else {
266+
let key = entry.key;
267+
if (entry.quoted) {
268+
key = `"${key}"`;
269+
}
270+
entries.push(key + ':' + this.keyOf(entry.value));
328271
}
329-
entries.push(key + ':' + this.keyOf(entry.value));
330272
}
331273
return `{${entries.join(',')}}`;
332274
} else if (expr instanceof o.ExternalExpr) {

packages/compiler/src/output/abstract_emitter.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -456,11 +456,16 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
456456
ctx.print(ast, `{`);
457457
this.visitAllObjects(
458458
(entry) => {
459-
ctx.print(
460-
ast,
461-
`${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`,
462-
);
463-
entry.value.visitExpression(this, ctx);
459+
if (entry instanceof o.LiteralMapSpreadAssignment) {
460+
ctx.print(ast, '...');
461+
entry.expression.visitExpression(this, ctx);
462+
} else {
463+
ctx.print(
464+
ast,
465+
`${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`,
466+
);
467+
entry.value.visitExpression(this, ctx);
468+
}
464469
},
465470
ast.entries,
466471
ctx,

packages/compiler/src/output/output_ast.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,21 +1331,44 @@ export class LiteralArrayExpr extends Expression {
13311331
}
13321332
}
13331333

1334-
export class LiteralMapEntry {
1334+
export class LiteralMapPropertyAssignment {
13351335
constructor(
13361336
public key: string,
13371337
public value: Expression,
13381338
public quoted: boolean,
13391339
) {}
1340-
isEquivalent(e: LiteralMapEntry): boolean {
1340+
1341+
isEquivalent(e: LiteralMapPropertyAssignment): boolean {
13411342
return this.key === e.key && this.value.isEquivalent(e.value);
13421343
}
13431344

1344-
clone(): LiteralMapEntry {
1345-
return new LiteralMapEntry(this.key, this.value.clone(), this.quoted);
1345+
clone(): LiteralMapPropertyAssignment {
1346+
return new LiteralMapPropertyAssignment(this.key, this.value.clone(), this.quoted);
1347+
}
1348+
1349+
isConstant() {
1350+
return this.value.isConstant();
13461351
}
13471352
}
13481353

1354+
export class LiteralMapSpreadAssignment {
1355+
constructor(public expression: Expression) {}
1356+
1357+
isEquivalent(e: LiteralMapSpreadAssignment): boolean {
1358+
return e instanceof LiteralMapSpreadAssignment && this.expression.isEquivalent(e.expression);
1359+
}
1360+
1361+
clone(): LiteralMapSpreadAssignment {
1362+
return new LiteralMapSpreadAssignment(this.expression.clone());
1363+
}
1364+
1365+
isConstant() {
1366+
return this.expression.isConstant();
1367+
}
1368+
}
1369+
1370+
export type LiteralMapEntry = LiteralMapPropertyAssignment | LiteralMapSpreadAssignment;
1371+
13491372
export class LiteralMapExpr extends Expression {
13501373
public valueType: Type | null = null;
13511374
constructor(
@@ -1364,7 +1387,7 @@ export class LiteralMapExpr extends Expression {
13641387
}
13651388

13661389
override isConstant() {
1367-
return this.entries.every((e) => e.value.isConstant());
1390+
return this.entries.every((e) => e.isConstant());
13681391
}
13691392

13701393
override visitExpression(visitor: ExpressionVisitor, context: any): any {
@@ -1724,7 +1747,13 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
17241747
return this.visitExpression(ast, context);
17251748
}
17261749
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
1727-
ast.entries.forEach((entry) => entry.value.visitExpression(this, context));
1750+
ast.entries.forEach((entry) => {
1751+
if (entry instanceof LiteralMapSpreadAssignment) {
1752+
entry.expression.visitExpression(this, context);
1753+
} else {
1754+
entry.value.visitExpression(this, context);
1755+
}
1756+
});
17281757
return this.visitExpression(ast, context);
17291758
}
17301759
visitCommaExpr(ast: CommaExpr, context: any): any {
@@ -1847,7 +1876,7 @@ export function literalMap(
18471876
type: MapType | null = null,
18481877
): LiteralMapExpr {
18491878
return new LiteralMapExpr(
1850-
values.map((e) => new LiteralMapEntry(e.key, e.value, e.quoted)),
1879+
values.map((e) => new LiteralMapPropertyAssignment(e.key, e.value, e.quoted)),
18511880
type,
18521881
null,
18531882
);

packages/compiler/src/output/output_jit.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ export class JitEmitterVisitor extends AbstractJsEmitterVisitor {
115115
const stmt = new o.ReturnStatement(
116116
new o.LiteralMapExpr(
117117
this._evalExportedVars.map(
118-
(resultVar) => new o.LiteralMapEntry(resultVar, o.variable(resultVar), false),
118+
(resultVar) =>
119+
new o.LiteralMapPropertyAssignment(resultVar, o.variable(resultVar), false),
119120
),
120121
),
121122
);

packages/compiler/src/template/pipeline/ir/src/expression.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,12 +1275,12 @@ export function transformExpressionsInExpression(
12751275
expr.entries[i] = transformExpressionsInExpression(expr.entries[i], transform, flags);
12761276
}
12771277
} else if (expr instanceof o.LiteralMapExpr) {
1278-
for (let i = 0; i < expr.entries.length; i++) {
1279-
expr.entries[i].value = transformExpressionsInExpression(
1280-
expr.entries[i].value,
1281-
transform,
1282-
flags,
1283-
);
1278+
for (const entry of expr.entries) {
1279+
if (entry instanceof o.LiteralMapSpreadAssignment) {
1280+
entry.expression = transformExpressionsInExpression(entry.expression, transform, flags);
1281+
} else {
1282+
entry.value = transformExpressionsInExpression(entry.value, transform, flags);
1283+
}
12841284
}
12851285
} else if (expr instanceof o.ConditionalExpr) {
12861286
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);

packages/compiler/src/template/pipeline/src/phases/pure_literal_structures.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,32 @@ function transformLiteralMap(expr: o.LiteralMapExpr): o.Expression {
5353
let derivedEntries: o.LiteralMapEntry[] = [];
5454
const nonConstantArgs: o.Expression[] = [];
5555
for (const entry of expr.entries) {
56+
if (entry instanceof o.LiteralMapSpreadAssignment) {
57+
if (entry.expression.isConstant()) {
58+
derivedEntries.push(entry);
59+
} else {
60+
const idx = nonConstantArgs.length;
61+
nonConstantArgs.push(entry.expression);
62+
derivedEntries.push(
63+
new o.LiteralMapSpreadAssignment(new ir.PureFunctionParameterExpr(idx)),
64+
);
65+
}
66+
continue;
67+
}
68+
5669
if (entry.value.isConstant()) {
5770
derivedEntries.push(entry);
5871
} else {
5972
const idx = nonConstantArgs.length;
6073
nonConstantArgs.push(entry.value);
6174
derivedEntries.push(
62-
new o.LiteralMapEntry(entry.key, new ir.PureFunctionParameterExpr(idx), entry.quoted),
75+
new o.LiteralMapPropertyAssignment(
76+
entry.key,
77+
new ir.PureFunctionParameterExpr(idx),
78+
entry.quoted,
79+
),
6380
);
6481
}
6582
}
66-
return new ir.PureFunctionExpr(o.literalMap(derivedEntries), nonConstantArgs);
83+
return new ir.PureFunctionExpr(new o.LiteralMapExpr(derivedEntries), nonConstantArgs);
6784
}

packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/template_reference_visitor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ export class TemplateExpressionReferenceVisitor<
272272
// E.g. `{bla}` may be transformed to `{bla: bla()}`.
273273
override visitLiteralMap(ast: LiteralMap, context: any) {
274274
for (const [idx, key] of ast.keys.entries()) {
275-
this.isInsideObjectShorthandExpression = !!key.isShorthandInitialized;
275+
this.isInsideObjectShorthandExpression =
276+
key.kind === 'property' && !!key.isShorthandInitialized;
276277
(ast.values[idx] as AST).visit(this, context);
277278
this.isInsideObjectShorthandExpression = false;
278279
}

0 commit comments

Comments
 (0)