@angular/compiler 16.1.1 → 16.2.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/compiler_util/expression_converter.mjs +2 -1
- package/esm2022/src/constant_pool.mjs +4 -1
- package/esm2022/src/output/output_ast.mjs +81 -1
- package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
- package/esm2022/src/render3/partial/directive.mjs +1 -1
- package/esm2022/src/render3/partial/factory.mjs +1 -1
- package/esm2022/src/render3/partial/injectable.mjs +1 -1
- package/esm2022/src/render3/partial/injector.mjs +1 -1
- package/esm2022/src/render3/partial/ng_module.mjs +1 -1
- package/esm2022/src/render3/partial/pipe.mjs +1 -1
- package/esm2022/src/template/pipeline/ir/src/element.mjs +5 -2
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +21 -1
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +146 -1
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +13 -1
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +18 -3
- package/esm2022/src/template/pipeline/src/emit.mjs +19 -13
- package/esm2022/src/template/pipeline/src/ingest.mjs +68 -62
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +74 -0
- package/esm2022/src/template/pipeline/src/phases/expand_safe_reads.mjs +86 -0
- package/esm2022/src/template/pipeline/src/phases/nullish_coalescing.mjs +26 -0
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +1700 -1253
- package/fesm2022/compiler.mjs.map +1 -1
- package/fesm2022/testing.mjs +1 -1
- package/index.d.ts +28 -1
- package/package.json +2 -2
- package/testing/index.d.ts +1 -1
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v16.
|
|
2
|
+
* @license Angular v16.2.0-next.0
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -1222,6 +1222,9 @@ class ReadVarExpr extends Expression {
|
|
|
1222
1222
|
visitExpression(visitor, context) {
|
|
1223
1223
|
return visitor.visitReadVarExpr(this, context);
|
|
1224
1224
|
}
|
|
1225
|
+
clone() {
|
|
1226
|
+
return new ReadVarExpr(this.name, this.type, this.sourceSpan);
|
|
1227
|
+
}
|
|
1225
1228
|
set(value) {
|
|
1226
1229
|
return new WriteVarExpr(this.name, value, null, this.sourceSpan);
|
|
1227
1230
|
}
|
|
@@ -1240,6 +1243,9 @@ class TypeofExpr extends Expression {
|
|
|
1240
1243
|
isConstant() {
|
|
1241
1244
|
return this.expr.isConstant();
|
|
1242
1245
|
}
|
|
1246
|
+
clone() {
|
|
1247
|
+
return new TypeofExpr(this.expr.clone());
|
|
1248
|
+
}
|
|
1243
1249
|
}
|
|
1244
1250
|
class WrappedNodeExpr extends Expression {
|
|
1245
1251
|
constructor(node, type, sourceSpan) {
|
|
@@ -1255,6 +1261,9 @@ class WrappedNodeExpr extends Expression {
|
|
|
1255
1261
|
visitExpression(visitor, context) {
|
|
1256
1262
|
return visitor.visitWrappedNodeExpr(this, context);
|
|
1257
1263
|
}
|
|
1264
|
+
clone() {
|
|
1265
|
+
return new WrappedNodeExpr(this.node, this.type, this.sourceSpan);
|
|
1266
|
+
}
|
|
1258
1267
|
}
|
|
1259
1268
|
class WriteVarExpr extends Expression {
|
|
1260
1269
|
constructor(name, value, type, sourceSpan) {
|
|
@@ -1271,6 +1280,9 @@ class WriteVarExpr extends Expression {
|
|
|
1271
1280
|
visitExpression(visitor, context) {
|
|
1272
1281
|
return visitor.visitWriteVarExpr(this, context);
|
|
1273
1282
|
}
|
|
1283
|
+
clone() {
|
|
1284
|
+
return new WriteVarExpr(this.name, this.value.clone(), this.type, this.sourceSpan);
|
|
1285
|
+
}
|
|
1274
1286
|
toDeclStmt(type, modifiers) {
|
|
1275
1287
|
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
|
1276
1288
|
}
|
|
@@ -1295,6 +1307,9 @@ class WriteKeyExpr extends Expression {
|
|
|
1295
1307
|
visitExpression(visitor, context) {
|
|
1296
1308
|
return visitor.visitWriteKeyExpr(this, context);
|
|
1297
1309
|
}
|
|
1310
|
+
clone() {
|
|
1311
|
+
return new WriteKeyExpr(this.receiver.clone(), this.index.clone(), this.value.clone(), this.type, this.sourceSpan);
|
|
1312
|
+
}
|
|
1298
1313
|
}
|
|
1299
1314
|
class WritePropExpr extends Expression {
|
|
1300
1315
|
constructor(receiver, name, value, type, sourceSpan) {
|
|
@@ -1313,6 +1328,9 @@ class WritePropExpr extends Expression {
|
|
|
1313
1328
|
visitExpression(visitor, context) {
|
|
1314
1329
|
return visitor.visitWritePropExpr(this, context);
|
|
1315
1330
|
}
|
|
1331
|
+
clone() {
|
|
1332
|
+
return new WritePropExpr(this.receiver.clone(), this.name, this.value.clone(), this.type, this.sourceSpan);
|
|
1333
|
+
}
|
|
1316
1334
|
}
|
|
1317
1335
|
class InvokeFunctionExpr extends Expression {
|
|
1318
1336
|
constructor(fn, args, type, sourceSpan, pure = false) {
|
|
@@ -1331,6 +1349,9 @@ class InvokeFunctionExpr extends Expression {
|
|
|
1331
1349
|
visitExpression(visitor, context) {
|
|
1332
1350
|
return visitor.visitInvokeFunctionExpr(this, context);
|
|
1333
1351
|
}
|
|
1352
|
+
clone() {
|
|
1353
|
+
return new InvokeFunctionExpr(this.fn.clone(), this.args.map(arg => arg.clone()), this.type, this.sourceSpan, this.pure);
|
|
1354
|
+
}
|
|
1334
1355
|
}
|
|
1335
1356
|
class TaggedTemplateExpr extends Expression {
|
|
1336
1357
|
constructor(tag, template, type, sourceSpan) {
|
|
@@ -1349,6 +1370,9 @@ class TaggedTemplateExpr extends Expression {
|
|
|
1349
1370
|
visitExpression(visitor, context) {
|
|
1350
1371
|
return visitor.visitTaggedTemplateExpr(this, context);
|
|
1351
1372
|
}
|
|
1373
|
+
clone() {
|
|
1374
|
+
return new TaggedTemplateExpr(this.tag.clone(), this.template.clone(), this.type, this.sourceSpan);
|
|
1375
|
+
}
|
|
1352
1376
|
}
|
|
1353
1377
|
class InstantiateExpr extends Expression {
|
|
1354
1378
|
constructor(classExpr, args, type, sourceSpan) {
|
|
@@ -1366,6 +1390,9 @@ class InstantiateExpr extends Expression {
|
|
|
1366
1390
|
visitExpression(visitor, context) {
|
|
1367
1391
|
return visitor.visitInstantiateExpr(this, context);
|
|
1368
1392
|
}
|
|
1393
|
+
clone() {
|
|
1394
|
+
return new InstantiateExpr(this.classExpr.clone(), this.args.map(arg => arg.clone()), this.type, this.sourceSpan);
|
|
1395
|
+
}
|
|
1369
1396
|
}
|
|
1370
1397
|
class LiteralExpr extends Expression {
|
|
1371
1398
|
constructor(value, type, sourceSpan) {
|
|
@@ -1381,12 +1408,18 @@ class LiteralExpr extends Expression {
|
|
|
1381
1408
|
visitExpression(visitor, context) {
|
|
1382
1409
|
return visitor.visitLiteralExpr(this, context);
|
|
1383
1410
|
}
|
|
1411
|
+
clone() {
|
|
1412
|
+
return new LiteralExpr(this.value, this.type, this.sourceSpan);
|
|
1413
|
+
}
|
|
1384
1414
|
}
|
|
1385
1415
|
class TemplateLiteral {
|
|
1386
1416
|
constructor(elements, expressions) {
|
|
1387
1417
|
this.elements = elements;
|
|
1388
1418
|
this.expressions = expressions;
|
|
1389
1419
|
}
|
|
1420
|
+
clone() {
|
|
1421
|
+
return new TemplateLiteral(this.elements.map(el => el.clone()), this.expressions.map(expr => expr.clone()));
|
|
1422
|
+
}
|
|
1390
1423
|
}
|
|
1391
1424
|
class TemplateLiteralElement {
|
|
1392
1425
|
constructor(text, sourceSpan, rawText) {
|
|
@@ -1401,6 +1434,9 @@ class TemplateLiteralElement {
|
|
|
1401
1434
|
this.rawText =
|
|
1402
1435
|
rawText ?? sourceSpan?.toString() ?? escapeForTemplateLiteral(escapeSlashes(text));
|
|
1403
1436
|
}
|
|
1437
|
+
clone() {
|
|
1438
|
+
return new TemplateLiteralElement(this.text, this.sourceSpan, this.rawText);
|
|
1439
|
+
}
|
|
1404
1440
|
}
|
|
1405
1441
|
class LiteralPiece {
|
|
1406
1442
|
constructor(text, sourceSpan) {
|
|
@@ -1445,6 +1481,9 @@ class LocalizedString extends Expression {
|
|
|
1445
1481
|
visitExpression(visitor, context) {
|
|
1446
1482
|
return visitor.visitLocalizedString(this, context);
|
|
1447
1483
|
}
|
|
1484
|
+
clone() {
|
|
1485
|
+
return new LocalizedString(this.metaBlock, this.messageParts, this.placeHolderNames, this.expressions.map(expr => expr.clone()), this.sourceSpan);
|
|
1486
|
+
}
|
|
1448
1487
|
/**
|
|
1449
1488
|
* Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used
|
|
1450
1489
|
* in a `$localize` tagged string. The format of the metadata is the same as that parsed by
|
|
@@ -1546,6 +1585,9 @@ class ExternalExpr extends Expression {
|
|
|
1546
1585
|
visitExpression(visitor, context) {
|
|
1547
1586
|
return visitor.visitExternalExpr(this, context);
|
|
1548
1587
|
}
|
|
1588
|
+
clone() {
|
|
1589
|
+
return new ExternalExpr(this.value, this.type, this.typeParams, this.sourceSpan);
|
|
1590
|
+
}
|
|
1549
1591
|
}
|
|
1550
1592
|
class ExternalReference {
|
|
1551
1593
|
constructor(moduleName, name, runtime) {
|
|
@@ -1571,6 +1613,9 @@ class ConditionalExpr extends Expression {
|
|
|
1571
1613
|
visitExpression(visitor, context) {
|
|
1572
1614
|
return visitor.visitConditionalExpr(this, context);
|
|
1573
1615
|
}
|
|
1616
|
+
clone() {
|
|
1617
|
+
return new ConditionalExpr(this.condition.clone(), this.trueCase.clone(), this.falseCase?.clone(), this.type, this.sourceSpan);
|
|
1618
|
+
}
|
|
1574
1619
|
}
|
|
1575
1620
|
class NotExpr extends Expression {
|
|
1576
1621
|
constructor(condition, sourceSpan) {
|
|
@@ -1586,6 +1631,9 @@ class NotExpr extends Expression {
|
|
|
1586
1631
|
visitExpression(visitor, context) {
|
|
1587
1632
|
return visitor.visitNotExpr(this, context);
|
|
1588
1633
|
}
|
|
1634
|
+
clone() {
|
|
1635
|
+
return new NotExpr(this.condition.clone(), this.sourceSpan);
|
|
1636
|
+
}
|
|
1589
1637
|
}
|
|
1590
1638
|
class FnParam {
|
|
1591
1639
|
constructor(name, type = null) {
|
|
@@ -1595,6 +1643,9 @@ class FnParam {
|
|
|
1595
1643
|
isEquivalent(param) {
|
|
1596
1644
|
return this.name === param.name;
|
|
1597
1645
|
}
|
|
1646
|
+
clone() {
|
|
1647
|
+
return new FnParam(this.name, this.type);
|
|
1648
|
+
}
|
|
1598
1649
|
}
|
|
1599
1650
|
class FunctionExpr extends Expression {
|
|
1600
1651
|
constructor(params, statements, type, sourceSpan, name) {
|
|
@@ -1616,6 +1667,10 @@ class FunctionExpr extends Expression {
|
|
|
1616
1667
|
toDeclStmt(name, modifiers) {
|
|
1617
1668
|
return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
|
|
1618
1669
|
}
|
|
1670
|
+
clone() {
|
|
1671
|
+
// TODO: Should we deep clone statements?
|
|
1672
|
+
return new FunctionExpr(this.params.map(p => p.clone()), this.statements, this.type, this.sourceSpan, this.name);
|
|
1673
|
+
}
|
|
1619
1674
|
}
|
|
1620
1675
|
class UnaryOperatorExpr extends Expression {
|
|
1621
1676
|
constructor(operator, expr, type, sourceSpan, parens = true) {
|
|
@@ -1634,6 +1689,9 @@ class UnaryOperatorExpr extends Expression {
|
|
|
1634
1689
|
visitExpression(visitor, context) {
|
|
1635
1690
|
return visitor.visitUnaryOperatorExpr(this, context);
|
|
1636
1691
|
}
|
|
1692
|
+
clone() {
|
|
1693
|
+
return new UnaryOperatorExpr(this.operator, this.expr.clone(), this.type, this.sourceSpan, this.parens);
|
|
1694
|
+
}
|
|
1637
1695
|
}
|
|
1638
1696
|
class BinaryOperatorExpr extends Expression {
|
|
1639
1697
|
constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
|
|
@@ -1653,6 +1711,9 @@ class BinaryOperatorExpr extends Expression {
|
|
|
1653
1711
|
visitExpression(visitor, context) {
|
|
1654
1712
|
return visitor.visitBinaryOperatorExpr(this, context);
|
|
1655
1713
|
}
|
|
1714
|
+
clone() {
|
|
1715
|
+
return new BinaryOperatorExpr(this.operator, this.lhs.clone(), this.rhs.clone(), this.type, this.sourceSpan, this.parens);
|
|
1716
|
+
}
|
|
1656
1717
|
}
|
|
1657
1718
|
class ReadPropExpr extends Expression {
|
|
1658
1719
|
constructor(receiver, name, type, sourceSpan) {
|
|
@@ -1673,6 +1734,9 @@ class ReadPropExpr extends Expression {
|
|
|
1673
1734
|
set(value) {
|
|
1674
1735
|
return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
|
|
1675
1736
|
}
|
|
1737
|
+
clone() {
|
|
1738
|
+
return new ReadPropExpr(this.receiver.clone(), this.name, this.type, this.sourceSpan);
|
|
1739
|
+
}
|
|
1676
1740
|
}
|
|
1677
1741
|
class ReadKeyExpr extends Expression {
|
|
1678
1742
|
constructor(receiver, index, type, sourceSpan) {
|
|
@@ -1693,6 +1757,9 @@ class ReadKeyExpr extends Expression {
|
|
|
1693
1757
|
set(value) {
|
|
1694
1758
|
return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
|
|
1695
1759
|
}
|
|
1760
|
+
clone() {
|
|
1761
|
+
return new ReadKeyExpr(this.receiver, this.index.clone(), this.type, this.sourceSpan);
|
|
1762
|
+
}
|
|
1696
1763
|
}
|
|
1697
1764
|
class LiteralArrayExpr extends Expression {
|
|
1698
1765
|
constructor(entries, type, sourceSpan) {
|
|
@@ -1708,6 +1775,9 @@ class LiteralArrayExpr extends Expression {
|
|
|
1708
1775
|
visitExpression(visitor, context) {
|
|
1709
1776
|
return visitor.visitLiteralArrayExpr(this, context);
|
|
1710
1777
|
}
|
|
1778
|
+
clone() {
|
|
1779
|
+
return new LiteralArrayExpr(this.entries.map(e => e.clone()), this.type, this.sourceSpan);
|
|
1780
|
+
}
|
|
1711
1781
|
}
|
|
1712
1782
|
class LiteralMapEntry {
|
|
1713
1783
|
constructor(key, value, quoted) {
|
|
@@ -1718,6 +1788,9 @@ class LiteralMapEntry {
|
|
|
1718
1788
|
isEquivalent(e) {
|
|
1719
1789
|
return this.key === e.key && this.value.isEquivalent(e.value);
|
|
1720
1790
|
}
|
|
1791
|
+
clone() {
|
|
1792
|
+
return new LiteralMapEntry(this.key, this.value.clone(), this.quoted);
|
|
1793
|
+
}
|
|
1721
1794
|
}
|
|
1722
1795
|
class LiteralMapExpr extends Expression {
|
|
1723
1796
|
constructor(entries, type, sourceSpan) {
|
|
@@ -1737,6 +1810,10 @@ class LiteralMapExpr extends Expression {
|
|
|
1737
1810
|
visitExpression(visitor, context) {
|
|
1738
1811
|
return visitor.visitLiteralMapExpr(this, context);
|
|
1739
1812
|
}
|
|
1813
|
+
clone() {
|
|
1814
|
+
const entriesClone = this.entries.map(entry => entry.clone());
|
|
1815
|
+
return new LiteralMapExpr(entriesClone, this.type, this.sourceSpan);
|
|
1816
|
+
}
|
|
1740
1817
|
}
|
|
1741
1818
|
class CommaExpr extends Expression {
|
|
1742
1819
|
constructor(parts, sourceSpan) {
|
|
@@ -1752,6 +1829,9 @@ class CommaExpr extends Expression {
|
|
|
1752
1829
|
visitExpression(visitor, context) {
|
|
1753
1830
|
return visitor.visitCommaExpr(this, context);
|
|
1754
1831
|
}
|
|
1832
|
+
clone() {
|
|
1833
|
+
return new CommaExpr(this.parts.map(p => p.clone()));
|
|
1834
|
+
}
|
|
1755
1835
|
}
|
|
1756
1836
|
const NULL_EXPR = new LiteralExpr(null, null, null);
|
|
1757
1837
|
const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
|
|
@@ -2256,6 +2336,9 @@ class FixupExpression extends Expression {
|
|
|
2256
2336
|
isConstant() {
|
|
2257
2337
|
return true;
|
|
2258
2338
|
}
|
|
2339
|
+
clone() {
|
|
2340
|
+
throw new Error(`Not supported.`);
|
|
2341
|
+
}
|
|
2259
2342
|
fixup(expression) {
|
|
2260
2343
|
this.resolved = expression;
|
|
2261
2344
|
this.shared = true;
|
|
@@ -7247,6 +7330,7 @@ class InterpolationExpression extends Expression {
|
|
|
7247
7330
|
this.isConstant = unsupported;
|
|
7248
7331
|
this.isEquivalent = unsupported;
|
|
7249
7332
|
this.visitExpression = unsupported;
|
|
7333
|
+
this.clone = unsupported;
|
|
7250
7334
|
}
|
|
7251
7335
|
}
|
|
7252
7336
|
class DefaultLocalResolver {
|
|
@@ -8453,7 +8537,10 @@ class ElementAttributes {
|
|
|
8453
8537
|
this.known.add(name);
|
|
8454
8538
|
const array = this.arrayFor(kind);
|
|
8455
8539
|
array.push(...getAttributeNameLiterals$1(name));
|
|
8456
|
-
if (
|
|
8540
|
+
if (kind === ElementAttributeKind.Attribute || kind === ElementAttributeKind.Style) {
|
|
8541
|
+
if (value === null) {
|
|
8542
|
+
throw Error('Attribute & style element attributes must have a value');
|
|
8543
|
+
}
|
|
8457
8544
|
array.push(value);
|
|
8458
8545
|
}
|
|
8459
8546
|
}
|
|
@@ -8556,6 +8643,10 @@ var OpKind;
|
|
|
8556
8643
|
* An operation to instantiate a pipe.
|
|
8557
8644
|
*/
|
|
8558
8645
|
OpKind[OpKind["Pipe"] = 16] = "Pipe";
|
|
8646
|
+
/**
|
|
8647
|
+
* An operation to associate an attribute with an element.
|
|
8648
|
+
*/
|
|
8649
|
+
OpKind[OpKind["Attribute"] = 17] = "Attribute";
|
|
8559
8650
|
})(OpKind || (OpKind = {}));
|
|
8560
8651
|
/**
|
|
8561
8652
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -8610,6 +8701,22 @@ var ExpressionKind;
|
|
|
8610
8701
|
* Binding to a pipe transformation with a variable number of arguments.
|
|
8611
8702
|
*/
|
|
8612
8703
|
ExpressionKind[ExpressionKind["PipeBindingVariadic"] = 11] = "PipeBindingVariadic";
|
|
8704
|
+
/*
|
|
8705
|
+
* A safe property read requiring expansion into a null check.
|
|
8706
|
+
*/
|
|
8707
|
+
ExpressionKind[ExpressionKind["SafePropertyRead"] = 12] = "SafePropertyRead";
|
|
8708
|
+
/**
|
|
8709
|
+
* A safe keyed read requiring expansion into a null check.
|
|
8710
|
+
*/
|
|
8711
|
+
ExpressionKind[ExpressionKind["SafeKeyedRead"] = 13] = "SafeKeyedRead";
|
|
8712
|
+
/**
|
|
8713
|
+
* A safe function call requiring expansion into a null check.
|
|
8714
|
+
*/
|
|
8715
|
+
ExpressionKind[ExpressionKind["SafeInvokeFunction"] = 14] = "SafeInvokeFunction";
|
|
8716
|
+
/**
|
|
8717
|
+
* An intermediate expression that will be expanded from a safe read into an explicit ternary.
|
|
8718
|
+
*/
|
|
8719
|
+
ExpressionKind[ExpressionKind["SafeTernaryExpr"] = 15] = "SafeTernaryExpr";
|
|
8613
8720
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
8614
8721
|
/**
|
|
8615
8722
|
* Distinguishes between different kinds of `SemanticVariable`s.
|
|
@@ -8746,6 +8853,9 @@ class LexicalReadExpr extends ExpressionBase {
|
|
|
8746
8853
|
return false;
|
|
8747
8854
|
}
|
|
8748
8855
|
transformInternalExpressions() { }
|
|
8856
|
+
clone() {
|
|
8857
|
+
return new LexicalReadExpr(this.name);
|
|
8858
|
+
}
|
|
8749
8859
|
}
|
|
8750
8860
|
/**
|
|
8751
8861
|
* Runtime operation to retrieve the value of a local reference.
|
|
@@ -8768,6 +8878,11 @@ class ReferenceExpr extends ExpressionBase {
|
|
|
8768
8878
|
return false;
|
|
8769
8879
|
}
|
|
8770
8880
|
transformInternalExpressions() { }
|
|
8881
|
+
clone() {
|
|
8882
|
+
const expr = new ReferenceExpr(this.target, this.offset);
|
|
8883
|
+
expr.slot = this.slot;
|
|
8884
|
+
return expr;
|
|
8885
|
+
}
|
|
8771
8886
|
}
|
|
8772
8887
|
/**
|
|
8773
8888
|
* A reference to the current view context (usually the `ctx` variable in a template function).
|
|
@@ -8786,6 +8901,9 @@ class ContextExpr extends ExpressionBase {
|
|
|
8786
8901
|
return false;
|
|
8787
8902
|
}
|
|
8788
8903
|
transformInternalExpressions() { }
|
|
8904
|
+
clone() {
|
|
8905
|
+
return new ContextExpr(this.view);
|
|
8906
|
+
}
|
|
8789
8907
|
}
|
|
8790
8908
|
/**
|
|
8791
8909
|
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
@@ -8804,6 +8922,11 @@ class NextContextExpr extends ExpressionBase {
|
|
|
8804
8922
|
return false;
|
|
8805
8923
|
}
|
|
8806
8924
|
transformInternalExpressions() { }
|
|
8925
|
+
clone() {
|
|
8926
|
+
const expr = new NextContextExpr();
|
|
8927
|
+
expr.steps = this.steps;
|
|
8928
|
+
return expr;
|
|
8929
|
+
}
|
|
8807
8930
|
}
|
|
8808
8931
|
/**
|
|
8809
8932
|
* Runtime operation to snapshot the current view context.
|
|
@@ -8824,6 +8947,9 @@ class GetCurrentViewExpr extends ExpressionBase {
|
|
|
8824
8947
|
return false;
|
|
8825
8948
|
}
|
|
8826
8949
|
transformInternalExpressions() { }
|
|
8950
|
+
clone() {
|
|
8951
|
+
return new GetCurrentViewExpr();
|
|
8952
|
+
}
|
|
8827
8953
|
}
|
|
8828
8954
|
/**
|
|
8829
8955
|
* Runtime operation to restore a snapshotted view.
|
|
@@ -8858,6 +8984,9 @@ class RestoreViewExpr extends ExpressionBase {
|
|
|
8858
8984
|
this.view = transformExpressionsInExpression(this.view, transform, flags);
|
|
8859
8985
|
}
|
|
8860
8986
|
}
|
|
8987
|
+
clone() {
|
|
8988
|
+
return new RestoreViewExpr(this.view instanceof Expression ? this.view.clone() : this.view);
|
|
8989
|
+
}
|
|
8861
8990
|
}
|
|
8862
8991
|
/**
|
|
8863
8992
|
* Runtime operation to reset the current view context after `RestoreView`.
|
|
@@ -8880,6 +9009,9 @@ class ResetViewExpr extends ExpressionBase {
|
|
|
8880
9009
|
transformInternalExpressions(transform, flags) {
|
|
8881
9010
|
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
8882
9011
|
}
|
|
9012
|
+
clone() {
|
|
9013
|
+
return new ResetViewExpr(this.expr.clone());
|
|
9014
|
+
}
|
|
8883
9015
|
}
|
|
8884
9016
|
/**
|
|
8885
9017
|
* Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
|
|
@@ -8899,6 +9031,11 @@ class ReadVariableExpr extends ExpressionBase {
|
|
|
8899
9031
|
return false;
|
|
8900
9032
|
}
|
|
8901
9033
|
transformInternalExpressions() { }
|
|
9034
|
+
clone() {
|
|
9035
|
+
const expr = new ReadVariableExpr(this.xref);
|
|
9036
|
+
expr.name = this.name;
|
|
9037
|
+
return expr;
|
|
9038
|
+
}
|
|
8902
9039
|
}
|
|
8903
9040
|
class PureFunctionExpr extends ExpressionBase {
|
|
8904
9041
|
static { _b = ConsumesVarsTrait, _c = UsesVarOffset; }
|
|
@@ -8944,6 +9081,12 @@ class PureFunctionExpr extends ExpressionBase {
|
|
|
8944
9081
|
this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags);
|
|
8945
9082
|
}
|
|
8946
9083
|
}
|
|
9084
|
+
clone() {
|
|
9085
|
+
const expr = new PureFunctionExpr(this.body?.clone() ?? null, this.args.map(arg => arg.clone()));
|
|
9086
|
+
expr.fn = this.fn?.clone() ?? null;
|
|
9087
|
+
expr.varOffset = this.varOffset;
|
|
9088
|
+
return expr;
|
|
9089
|
+
}
|
|
8947
9090
|
}
|
|
8948
9091
|
class PureFunctionParameterExpr extends ExpressionBase {
|
|
8949
9092
|
constructor(index) {
|
|
@@ -8959,6 +9102,9 @@ class PureFunctionParameterExpr extends ExpressionBase {
|
|
|
8959
9102
|
return true;
|
|
8960
9103
|
}
|
|
8961
9104
|
transformInternalExpressions() { }
|
|
9105
|
+
clone() {
|
|
9106
|
+
return new PureFunctionParameterExpr(this.index);
|
|
9107
|
+
}
|
|
8962
9108
|
}
|
|
8963
9109
|
class PipeBindingExpr extends ExpressionBase {
|
|
8964
9110
|
static { _d = UsesSlotIndex, _e = ConsumesVarsTrait, _f = UsesVarOffset; }
|
|
@@ -8990,6 +9136,12 @@ class PipeBindingExpr extends ExpressionBase {
|
|
|
8990
9136
|
this.args[idx] = transformExpressionsInExpression(this.args[idx], transform, flags);
|
|
8991
9137
|
}
|
|
8992
9138
|
}
|
|
9139
|
+
clone() {
|
|
9140
|
+
const r = new PipeBindingExpr(this.target, this.name, this.args.map(a => a.clone()));
|
|
9141
|
+
r.slot = this.slot;
|
|
9142
|
+
r.varOffset = this.varOffset;
|
|
9143
|
+
return r;
|
|
9144
|
+
}
|
|
8993
9145
|
}
|
|
8994
9146
|
class PipeBindingVariadicExpr extends ExpressionBase {
|
|
8995
9147
|
static { _g = UsesSlotIndex, _h = ConsumesVarsTrait, _j = UsesVarOffset; }
|
|
@@ -9018,6 +9170,101 @@ class PipeBindingVariadicExpr extends ExpressionBase {
|
|
|
9018
9170
|
transformInternalExpressions(transform, flags) {
|
|
9019
9171
|
this.args = transformExpressionsInExpression(this.args, transform, flags);
|
|
9020
9172
|
}
|
|
9173
|
+
clone() {
|
|
9174
|
+
const r = new PipeBindingVariadicExpr(this.target, this.name, this.args.clone(), this.numArgs);
|
|
9175
|
+
r.slot = this.slot;
|
|
9176
|
+
r.varOffset = this.varOffset;
|
|
9177
|
+
return r;
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
class SafePropertyReadExpr extends ExpressionBase {
|
|
9181
|
+
constructor(receiver, name) {
|
|
9182
|
+
super();
|
|
9183
|
+
this.receiver = receiver;
|
|
9184
|
+
this.name = name;
|
|
9185
|
+
this.kind = ExpressionKind.SafePropertyRead;
|
|
9186
|
+
}
|
|
9187
|
+
visitExpression(visitor, context) { }
|
|
9188
|
+
isEquivalent() {
|
|
9189
|
+
return false;
|
|
9190
|
+
}
|
|
9191
|
+
isConstant() {
|
|
9192
|
+
return false;
|
|
9193
|
+
}
|
|
9194
|
+
transformInternalExpressions(transform, flags) {
|
|
9195
|
+
this.receiver = transformExpressionsInExpression(this.receiver, transform, flags);
|
|
9196
|
+
}
|
|
9197
|
+
clone() {
|
|
9198
|
+
return new SafePropertyReadExpr(this.receiver.clone(), this.name);
|
|
9199
|
+
}
|
|
9200
|
+
}
|
|
9201
|
+
class SafeKeyedReadExpr extends ExpressionBase {
|
|
9202
|
+
constructor(receiver, index) {
|
|
9203
|
+
super();
|
|
9204
|
+
this.receiver = receiver;
|
|
9205
|
+
this.index = index;
|
|
9206
|
+
this.kind = ExpressionKind.SafeKeyedRead;
|
|
9207
|
+
}
|
|
9208
|
+
visitExpression(visitor, context) { }
|
|
9209
|
+
isEquivalent() {
|
|
9210
|
+
return false;
|
|
9211
|
+
}
|
|
9212
|
+
isConstant() {
|
|
9213
|
+
return false;
|
|
9214
|
+
}
|
|
9215
|
+
transformInternalExpressions(transform, flags) {
|
|
9216
|
+
this.receiver = transformExpressionsInExpression(this.receiver, transform, flags);
|
|
9217
|
+
this.index = transformExpressionsInExpression(this.index, transform, flags);
|
|
9218
|
+
}
|
|
9219
|
+
clone() {
|
|
9220
|
+
return new SafeKeyedReadExpr(this.receiver.clone(), this.index.clone());
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
class SafeInvokeFunctionExpr extends ExpressionBase {
|
|
9224
|
+
constructor(receiver, args) {
|
|
9225
|
+
super();
|
|
9226
|
+
this.receiver = receiver;
|
|
9227
|
+
this.args = args;
|
|
9228
|
+
this.kind = ExpressionKind.SafeInvokeFunction;
|
|
9229
|
+
}
|
|
9230
|
+
visitExpression(visitor, context) { }
|
|
9231
|
+
isEquivalent() {
|
|
9232
|
+
return false;
|
|
9233
|
+
}
|
|
9234
|
+
isConstant() {
|
|
9235
|
+
return false;
|
|
9236
|
+
}
|
|
9237
|
+
transformInternalExpressions(transform, flags) {
|
|
9238
|
+
this.receiver = transformExpressionsInExpression(this.receiver, transform, flags);
|
|
9239
|
+
for (let i = 0; i < this.args.length; i++) {
|
|
9240
|
+
this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags);
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
clone() {
|
|
9244
|
+
return new SafeInvokeFunctionExpr(this.receiver.clone(), this.args.map(a => a.clone()));
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
9247
|
+
class SafeTernaryExpr extends ExpressionBase {
|
|
9248
|
+
constructor(guard, expr) {
|
|
9249
|
+
super();
|
|
9250
|
+
this.guard = guard;
|
|
9251
|
+
this.expr = expr;
|
|
9252
|
+
this.kind = ExpressionKind.SafeTernaryExpr;
|
|
9253
|
+
}
|
|
9254
|
+
visitExpression(visitor, context) { }
|
|
9255
|
+
isEquivalent() {
|
|
9256
|
+
return false;
|
|
9257
|
+
}
|
|
9258
|
+
isConstant() {
|
|
9259
|
+
return false;
|
|
9260
|
+
}
|
|
9261
|
+
transformInternalExpressions(transform, flags) {
|
|
9262
|
+
this.guard = transformExpressionsInExpression(this.guard, transform, flags);
|
|
9263
|
+
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
9264
|
+
}
|
|
9265
|
+
clone() {
|
|
9266
|
+
return new SafeTernaryExpr(this.guard.clone(), this.expr.clone());
|
|
9267
|
+
}
|
|
9021
9268
|
}
|
|
9022
9269
|
/**
|
|
9023
9270
|
* Visits all `Expression`s in the AST of `op` with the `visitor` function.
|
|
@@ -9052,6 +9299,11 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
9052
9299
|
case OpKind.Statement:
|
|
9053
9300
|
transformExpressionsInStatement(op.statement, transform, flags);
|
|
9054
9301
|
break;
|
|
9302
|
+
case OpKind.Attribute:
|
|
9303
|
+
if (op.value) {
|
|
9304
|
+
transformExpressionsInExpression(op.value, transform, flags);
|
|
9305
|
+
}
|
|
9306
|
+
break;
|
|
9055
9307
|
case OpKind.Variable:
|
|
9056
9308
|
op.initializer = transformExpressionsInExpression(op.initializer, transform, flags);
|
|
9057
9309
|
break;
|
|
@@ -9421,6 +9673,18 @@ const NEW_OP = {
|
|
|
9421
9673
|
next: null,
|
|
9422
9674
|
};
|
|
9423
9675
|
|
|
9676
|
+
/**
|
|
9677
|
+
* The set of OpKinds that represent the creation of an element or container
|
|
9678
|
+
*/
|
|
9679
|
+
const elementContainerOpKinds = new Set([
|
|
9680
|
+
OpKind.Element, OpKind.ElementStart, OpKind.Container, OpKind.ContainerStart, OpKind.Template
|
|
9681
|
+
]);
|
|
9682
|
+
/**
|
|
9683
|
+
* Checks whether the given operation represents the creation of an element or container.
|
|
9684
|
+
*/
|
|
9685
|
+
function isElementOrContainerOp(op) {
|
|
9686
|
+
return elementContainerOpKinds.has(op.kind);
|
|
9687
|
+
}
|
|
9424
9688
|
/**
|
|
9425
9689
|
* Create an `ElementStartOp`.
|
|
9426
9690
|
*/
|
|
@@ -9515,10 +9779,11 @@ function createInterpolateTextOp(xref, strings, expressions) {
|
|
|
9515
9779
|
/**
|
|
9516
9780
|
* Create a `PropertyOp`.
|
|
9517
9781
|
*/
|
|
9518
|
-
function createPropertyOp(xref, name, expression) {
|
|
9782
|
+
function createPropertyOp(xref, bindingKind, name, expression) {
|
|
9519
9783
|
return {
|
|
9520
9784
|
kind: OpKind.Property,
|
|
9521
9785
|
target: xref,
|
|
9786
|
+
bindingKind,
|
|
9522
9787
|
name,
|
|
9523
9788
|
expression,
|
|
9524
9789
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
@@ -9526,13 +9791,27 @@ function createPropertyOp(xref, name, expression) {
|
|
|
9526
9791
|
...NEW_OP,
|
|
9527
9792
|
};
|
|
9528
9793
|
}
|
|
9794
|
+
/**
|
|
9795
|
+
* Create an `AttributeOp`.
|
|
9796
|
+
*/
|
|
9797
|
+
function createAttributeOp(target, attributeKind, name, value) {
|
|
9798
|
+
return {
|
|
9799
|
+
kind: OpKind.Attribute,
|
|
9800
|
+
target,
|
|
9801
|
+
attributeKind,
|
|
9802
|
+
name,
|
|
9803
|
+
value,
|
|
9804
|
+
...NEW_OP,
|
|
9805
|
+
};
|
|
9806
|
+
}
|
|
9529
9807
|
/**
|
|
9530
9808
|
* Create a `InterpolateProperty`.
|
|
9531
9809
|
*/
|
|
9532
|
-
function createInterpolatePropertyOp(xref, name, strings, expressions) {
|
|
9810
|
+
function createInterpolatePropertyOp(xref, bindingKind, name, strings, expressions) {
|
|
9533
9811
|
return {
|
|
9534
9812
|
kind: OpKind.InterpolateProperty,
|
|
9535
9813
|
target: xref,
|
|
9814
|
+
bindingKind,
|
|
9536
9815
|
name,
|
|
9537
9816
|
strings,
|
|
9538
9817
|
expressions,
|
|
@@ -9553,62 +9832,302 @@ function createAdvanceOp(delta) {
|
|
|
9553
9832
|
}
|
|
9554
9833
|
|
|
9555
9834
|
/**
|
|
9556
|
-
*
|
|
9557
|
-
*
|
|
9835
|
+
* Counts the number of variable slots used within each view, and stores that on the view itself, as
|
|
9836
|
+
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
9558
9837
|
*/
|
|
9559
|
-
function
|
|
9838
|
+
function phaseVarCounting(cpl) {
|
|
9839
|
+
// First, count the vars used in each view, and update the view-level counter.
|
|
9560
9840
|
for (const [_, view] of cpl.views) {
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9841
|
+
let varCount = 0;
|
|
9842
|
+
for (const op of view.ops()) {
|
|
9843
|
+
if (hasConsumesVarsTrait(op)) {
|
|
9844
|
+
varCount += varsUsedByOp(op);
|
|
9565
9845
|
}
|
|
9566
|
-
|
|
9846
|
+
visitExpressionsInOp(op, expr => {
|
|
9847
|
+
if (!isIrExpression(expr)) {
|
|
9848
|
+
return;
|
|
9849
|
+
}
|
|
9850
|
+
// Some expressions require knowledge of the number of variable slots consumed.
|
|
9851
|
+
if (hasUsesVarOffsetTrait(expr)) {
|
|
9852
|
+
expr.varOffset = varCount;
|
|
9853
|
+
}
|
|
9854
|
+
if (hasConsumesVarsTrait(expr)) {
|
|
9855
|
+
varCount += varsUsedByIrExpression(expr);
|
|
9856
|
+
}
|
|
9857
|
+
});
|
|
9858
|
+
}
|
|
9859
|
+
view.vars = varCount;
|
|
9860
|
+
}
|
|
9861
|
+
// Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is an
|
|
9862
|
+
// embedded view).
|
|
9863
|
+
for (const [_, view] of cpl.views) {
|
|
9864
|
+
for (const op of view.create) {
|
|
9865
|
+
if (op.kind !== OpKind.Template) {
|
|
9567
9866
|
continue;
|
|
9568
9867
|
}
|
|
9569
|
-
const
|
|
9570
|
-
|
|
9571
|
-
op.attributes = cpl.addConst(attrArray);
|
|
9572
|
-
}
|
|
9573
|
-
else {
|
|
9574
|
-
op.attributes = null;
|
|
9575
|
-
}
|
|
9868
|
+
const childView = cpl.views.get(op.xref);
|
|
9869
|
+
op.vars = childView.vars;
|
|
9576
9870
|
}
|
|
9577
9871
|
}
|
|
9578
9872
|
}
|
|
9579
|
-
function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, styles, template }) {
|
|
9580
|
-
const attrArray = [...attributes];
|
|
9581
|
-
if (projectAs !== null) {
|
|
9582
|
-
attrArray.push(literal(5 /* core.AttributeMarker.ProjectAs */), literal(projectAs));
|
|
9583
|
-
}
|
|
9584
|
-
if (classes.length > 0) {
|
|
9585
|
-
attrArray.push(literal(1 /* core.AttributeMarker.Classes */), ...classes);
|
|
9586
|
-
}
|
|
9587
|
-
if (styles.length > 0) {
|
|
9588
|
-
attrArray.push(literal(2 /* core.AttributeMarker.Styles */), ...styles);
|
|
9589
|
-
}
|
|
9590
|
-
if (bindings.length > 0) {
|
|
9591
|
-
attrArray.push(literal(3 /* core.AttributeMarker.Bindings */), ...bindings);
|
|
9592
|
-
}
|
|
9593
|
-
if (template.length > 0) {
|
|
9594
|
-
attrArray.push(literal(4 /* core.AttributeMarker.Template */), ...template);
|
|
9595
|
-
}
|
|
9596
|
-
if (i18n.length > 0) {
|
|
9597
|
-
attrArray.push(literal(6 /* core.AttributeMarker.I18n */), ...i18n);
|
|
9598
|
-
}
|
|
9599
|
-
return literalArr(attrArray);
|
|
9600
|
-
}
|
|
9601
|
-
|
|
9602
|
-
const REPLACEMENTS = new Map([
|
|
9603
|
-
[OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]],
|
|
9604
|
-
[OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]],
|
|
9605
|
-
]);
|
|
9606
9873
|
/**
|
|
9607
|
-
*
|
|
9608
|
-
*
|
|
9874
|
+
* Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so
|
|
9875
|
+
* count the variables used by any particular `op`.
|
|
9609
9876
|
*/
|
|
9610
|
-
function
|
|
9611
|
-
|
|
9877
|
+
function varsUsedByOp(op) {
|
|
9878
|
+
switch (op.kind) {
|
|
9879
|
+
case OpKind.Property:
|
|
9880
|
+
// Property bindings use 1 variable slot.
|
|
9881
|
+
return 1;
|
|
9882
|
+
case OpKind.InterpolateText:
|
|
9883
|
+
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
9884
|
+
return op.expressions.length;
|
|
9885
|
+
case OpKind.InterpolateProperty:
|
|
9886
|
+
// `ir.InterpolatePropertyOp`s use a variable slot for each dynamic expression, plus one for
|
|
9887
|
+
// the result.
|
|
9888
|
+
return 1 + op.expressions.length;
|
|
9889
|
+
default:
|
|
9890
|
+
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
9891
|
+
}
|
|
9892
|
+
}
|
|
9893
|
+
function varsUsedByIrExpression(expr) {
|
|
9894
|
+
switch (expr.kind) {
|
|
9895
|
+
case ExpressionKind.PureFunctionExpr:
|
|
9896
|
+
return 1 + expr.args.length;
|
|
9897
|
+
case ExpressionKind.PipeBinding:
|
|
9898
|
+
return 1 + expr.args.length;
|
|
9899
|
+
case ExpressionKind.PipeBindingVariadic:
|
|
9900
|
+
return 1 + expr.numArgs;
|
|
9901
|
+
default:
|
|
9902
|
+
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
|
|
9906
|
+
function phaseAlignPipeVariadicVarOffset(cpl) {
|
|
9907
|
+
for (const view of cpl.views.values()) {
|
|
9908
|
+
for (const op of view.update) {
|
|
9909
|
+
visitExpressionsInOp(op, expr => {
|
|
9910
|
+
if (!(expr instanceof PipeBindingVariadicExpr)) {
|
|
9911
|
+
return expr;
|
|
9912
|
+
}
|
|
9913
|
+
if (!(expr.args instanceof PureFunctionExpr)) {
|
|
9914
|
+
return expr;
|
|
9915
|
+
}
|
|
9916
|
+
if (expr.varOffset === null || expr.args.varOffset === null) {
|
|
9917
|
+
throw new Error(`Must run after variable counting`);
|
|
9918
|
+
}
|
|
9919
|
+
// The structure of this variadic pipe expression is:
|
|
9920
|
+
// PipeBindingVariadic(#, Y, PureFunction(X, ...ARGS))
|
|
9921
|
+
// Where X and Y are the slot offsets for the variables used by these operations, and Y > X.
|
|
9922
|
+
// In `TemplateDefinitionBuilder` the PipeBindingVariadic variable slots are allocated
|
|
9923
|
+
// before the PureFunction slots, which is unusually out-of-order.
|
|
9924
|
+
//
|
|
9925
|
+
// To maintain identical output for the tests in question, we adjust the variable offsets of
|
|
9926
|
+
// these two calls to emulate TDB's behavior. This is not perfect, because the ARGS of the
|
|
9927
|
+
// PureFunction call may also allocate slots which by TDB's ordering would come after X, and
|
|
9928
|
+
// we don't account for that. Still, this should be enough to pass the existing pipe tests.
|
|
9929
|
+
// Put the PipeBindingVariadic vars where the PureFunction vars were previously allocated.
|
|
9930
|
+
expr.varOffset = expr.args.varOffset;
|
|
9931
|
+
// Put the PureFunction vars following the PipeBindingVariadic vars.
|
|
9932
|
+
expr.args.varOffset = expr.varOffset + varsUsedByIrExpression(expr);
|
|
9933
|
+
});
|
|
9934
|
+
}
|
|
9935
|
+
}
|
|
9936
|
+
}
|
|
9937
|
+
|
|
9938
|
+
/**
|
|
9939
|
+
* Looks up an element in the given map by xref ID.
|
|
9940
|
+
*/
|
|
9941
|
+
function lookupElement(elements, xref) {
|
|
9942
|
+
const el = elements.get(xref);
|
|
9943
|
+
if (el === undefined) {
|
|
9944
|
+
throw new Error('All attributes should have an element-like target.');
|
|
9945
|
+
}
|
|
9946
|
+
return el;
|
|
9947
|
+
}
|
|
9948
|
+
/**
|
|
9949
|
+
* Find all attribute and binding ops, and collect them into the ElementAttribute structures.
|
|
9950
|
+
* In cases where no instruction needs to be generated for the attribute or binding, it is removed.
|
|
9951
|
+
*/
|
|
9952
|
+
function phaseAttributeExtraction(cpl, compatibility) {
|
|
9953
|
+
for (const [_, view] of cpl.views) {
|
|
9954
|
+
populateElementAttributes(view, compatibility);
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
/**
|
|
9958
|
+
* Populates the ElementAttributes map for the given view, and removes ops for any bindings that do
|
|
9959
|
+
* not need further processing.
|
|
9960
|
+
*/
|
|
9961
|
+
function populateElementAttributes(view, compatibility) {
|
|
9962
|
+
const elements = new Map();
|
|
9963
|
+
for (const op of view.create) {
|
|
9964
|
+
if (!isElementOrContainerOp(op)) {
|
|
9965
|
+
continue;
|
|
9966
|
+
}
|
|
9967
|
+
elements.set(op.xref, op);
|
|
9968
|
+
}
|
|
9969
|
+
for (const op of view.ops()) {
|
|
9970
|
+
let ownerOp;
|
|
9971
|
+
switch (op.kind) {
|
|
9972
|
+
case OpKind.Attribute:
|
|
9973
|
+
ownerOp = lookupElement(elements, op.target);
|
|
9974
|
+
assertIsElementAttributes(ownerOp.attributes);
|
|
9975
|
+
let extractable = compatibility ?
|
|
9976
|
+
(op.value instanceof LiteralExpr && typeof op.value.value === 'string') :
|
|
9977
|
+
(op.value.isConstant());
|
|
9978
|
+
// We don't need to generate instructions for attributes that can be extracted as consts.
|
|
9979
|
+
if (extractable) {
|
|
9980
|
+
ownerOp.attributes.add(op.attributeKind, op.name, op.value);
|
|
9981
|
+
OpList.remove(op);
|
|
9982
|
+
}
|
|
9983
|
+
break;
|
|
9984
|
+
case OpKind.Property:
|
|
9985
|
+
case OpKind.InterpolateProperty:
|
|
9986
|
+
ownerOp = lookupElement(elements, op.target);
|
|
9987
|
+
assertIsElementAttributes(ownerOp.attributes);
|
|
9988
|
+
ownerOp.attributes.add(op.bindingKind, op.name, null);
|
|
9989
|
+
break;
|
|
9990
|
+
case OpKind.Listener:
|
|
9991
|
+
ownerOp = lookupElement(elements, op.target);
|
|
9992
|
+
assertIsElementAttributes(ownerOp.attributes);
|
|
9993
|
+
ownerOp.attributes.add(ElementAttributeKind.Binding, op.name, null);
|
|
9994
|
+
// We don't need to generate instructions for listeners on templates.
|
|
9995
|
+
if (ownerOp.kind === OpKind.Template) {
|
|
9996
|
+
OpList.remove(op);
|
|
9997
|
+
}
|
|
9998
|
+
break;
|
|
9999
|
+
}
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
10002
|
+
|
|
10003
|
+
const CHAINABLE = new Set([
|
|
10004
|
+
Identifiers.elementStart,
|
|
10005
|
+
Identifiers.elementEnd,
|
|
10006
|
+
Identifiers.property,
|
|
10007
|
+
Identifiers.elementContainerStart,
|
|
10008
|
+
Identifiers.elementContainerEnd,
|
|
10009
|
+
Identifiers.elementContainer,
|
|
10010
|
+
]);
|
|
10011
|
+
/**
|
|
10012
|
+
* Post-process a reified view compilation and convert sequential calls to chainable instructions
|
|
10013
|
+
* into chain calls.
|
|
10014
|
+
*
|
|
10015
|
+
* For example, two `elementStart` operations in sequence:
|
|
10016
|
+
*
|
|
10017
|
+
* ```typescript
|
|
10018
|
+
* elementStart(0, 'div');
|
|
10019
|
+
* elementStart(1, 'span');
|
|
10020
|
+
* ```
|
|
10021
|
+
*
|
|
10022
|
+
* Can be called as a chain instead:
|
|
10023
|
+
*
|
|
10024
|
+
* ```typescript
|
|
10025
|
+
* elementStart(0, 'div')(1, 'span');
|
|
10026
|
+
* ```
|
|
10027
|
+
*/
|
|
10028
|
+
function phaseChaining(cpl) {
|
|
10029
|
+
for (const [_, view] of cpl.views) {
|
|
10030
|
+
chainOperationsInList(view.create);
|
|
10031
|
+
chainOperationsInList(view.update);
|
|
10032
|
+
}
|
|
10033
|
+
}
|
|
10034
|
+
function chainOperationsInList(opList) {
|
|
10035
|
+
let chain = null;
|
|
10036
|
+
for (const op of opList) {
|
|
10037
|
+
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement)) {
|
|
10038
|
+
// This type of statement isn't chainable.
|
|
10039
|
+
chain = null;
|
|
10040
|
+
continue;
|
|
10041
|
+
}
|
|
10042
|
+
if (!(op.statement.expr instanceof InvokeFunctionExpr) ||
|
|
10043
|
+
!(op.statement.expr.fn instanceof ExternalExpr)) {
|
|
10044
|
+
// This is a statement, but not an instruction-type call, so not chainable.
|
|
10045
|
+
chain = null;
|
|
10046
|
+
continue;
|
|
10047
|
+
}
|
|
10048
|
+
const instruction = op.statement.expr.fn.value;
|
|
10049
|
+
if (!CHAINABLE.has(instruction)) {
|
|
10050
|
+
// This instruction isn't chainable.
|
|
10051
|
+
chain = null;
|
|
10052
|
+
continue;
|
|
10053
|
+
}
|
|
10054
|
+
// This instruction can be chained. It can either be added on to the previous chain (if
|
|
10055
|
+
// compatible) or it can be the start of a new chain.
|
|
10056
|
+
if (chain !== null && chain.instruction === instruction) {
|
|
10057
|
+
// This instruction can be added onto the previous chain.
|
|
10058
|
+
const expression = chain.expression.callFn(op.statement.expr.args, op.statement.expr.sourceSpan, op.statement.expr.pure);
|
|
10059
|
+
chain.expression = expression;
|
|
10060
|
+
chain.op.statement = expression.toStmt();
|
|
10061
|
+
OpList.remove(op);
|
|
10062
|
+
}
|
|
10063
|
+
else {
|
|
10064
|
+
// Leave this instruction alone for now, but consider it the start of a new chain.
|
|
10065
|
+
chain = {
|
|
10066
|
+
op,
|
|
10067
|
+
instruction,
|
|
10068
|
+
expression: op.statement.expr,
|
|
10069
|
+
};
|
|
10070
|
+
}
|
|
10071
|
+
}
|
|
10072
|
+
}
|
|
10073
|
+
|
|
10074
|
+
/**
|
|
10075
|
+
* Converts the semantic attributes of element-like operations (elements, templates) into constant
|
|
10076
|
+
* array expressions, and lifts them into the overall component `consts`.
|
|
10077
|
+
*/
|
|
10078
|
+
function phaseConstCollection(cpl) {
|
|
10079
|
+
for (const [_, view] of cpl.views) {
|
|
10080
|
+
for (const op of view.create) {
|
|
10081
|
+
if (op.kind !== OpKind.ElementStart && op.kind !== OpKind.Element &&
|
|
10082
|
+
op.kind !== OpKind.Template) {
|
|
10083
|
+
continue;
|
|
10084
|
+
}
|
|
10085
|
+
else if (!(op.attributes instanceof ElementAttributes)) {
|
|
10086
|
+
continue;
|
|
10087
|
+
}
|
|
10088
|
+
const attrArray = serializeAttributes(op.attributes);
|
|
10089
|
+
if (attrArray.entries.length > 0) {
|
|
10090
|
+
op.attributes = cpl.addConst(attrArray);
|
|
10091
|
+
}
|
|
10092
|
+
else {
|
|
10093
|
+
op.attributes = null;
|
|
10094
|
+
}
|
|
10095
|
+
}
|
|
10096
|
+
}
|
|
10097
|
+
}
|
|
10098
|
+
function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, styles, template }) {
|
|
10099
|
+
const attrArray = [...attributes];
|
|
10100
|
+
if (projectAs !== null) {
|
|
10101
|
+
attrArray.push(literal(5 /* core.AttributeMarker.ProjectAs */), literal(projectAs));
|
|
10102
|
+
}
|
|
10103
|
+
if (classes.length > 0) {
|
|
10104
|
+
attrArray.push(literal(1 /* core.AttributeMarker.Classes */), ...classes);
|
|
10105
|
+
}
|
|
10106
|
+
if (styles.length > 0) {
|
|
10107
|
+
attrArray.push(literal(2 /* core.AttributeMarker.Styles */), ...styles);
|
|
10108
|
+
}
|
|
10109
|
+
if (bindings.length > 0) {
|
|
10110
|
+
attrArray.push(literal(3 /* core.AttributeMarker.Bindings */), ...bindings);
|
|
10111
|
+
}
|
|
10112
|
+
if (template.length > 0) {
|
|
10113
|
+
attrArray.push(literal(4 /* core.AttributeMarker.Template */), ...template);
|
|
10114
|
+
}
|
|
10115
|
+
if (i18n.length > 0) {
|
|
10116
|
+
attrArray.push(literal(6 /* core.AttributeMarker.I18n */), ...i18n);
|
|
10117
|
+
}
|
|
10118
|
+
return literalArr(attrArray);
|
|
10119
|
+
}
|
|
10120
|
+
|
|
10121
|
+
const REPLACEMENTS = new Map([
|
|
10122
|
+
[OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]],
|
|
10123
|
+
[OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]],
|
|
10124
|
+
]);
|
|
10125
|
+
/**
|
|
10126
|
+
* Replace sequences of mergable elements (e.g. `ElementStart` and `ElementEnd`) with a consolidated
|
|
10127
|
+
* element (e.g. `Element`).
|
|
10128
|
+
*/
|
|
10129
|
+
function phaseEmptyElements(cpl) {
|
|
10130
|
+
for (const [_, view] of cpl.views) {
|
|
9612
10131
|
for (const op of view.create) {
|
|
9613
10132
|
const opReplacements = REPLACEMENTS.get(op.kind);
|
|
9614
10133
|
if (opReplacements === undefined) {
|
|
@@ -9674,804 +10193,961 @@ function phaseGenerateAdvance(cpl) {
|
|
|
9674
10193
|
}
|
|
9675
10194
|
}
|
|
9676
10195
|
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
if (localRefIndex !== null) {
|
|
9692
|
-
args.push(literal(constIndex), // might be null, but that's okay.
|
|
9693
|
-
literal(localRefIndex));
|
|
9694
|
-
}
|
|
9695
|
-
else if (constIndex !== null) {
|
|
9696
|
-
args.push(literal(constIndex));
|
|
10196
|
+
function phaseNullishCoalescing(cpl) {
|
|
10197
|
+
for (const view of cpl.views.values()) {
|
|
10198
|
+
for (const op of view.ops()) {
|
|
10199
|
+
transformExpressionsInOp(op, expr => {
|
|
10200
|
+
if (!(expr instanceof BinaryOperatorExpr) ||
|
|
10201
|
+
expr.operator !== BinaryOperator.NullishCoalesce) {
|
|
10202
|
+
return expr;
|
|
10203
|
+
}
|
|
10204
|
+
// TODO: We need to unconditionally emit a temporary variable to match
|
|
10205
|
+
// TemplateDefinitionBuilder. (We could also emit one conditionally when not in
|
|
10206
|
+
// compatibility mode.)
|
|
10207
|
+
return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.And, new BinaryOperatorExpr(BinaryOperator.NotIdentical, expr.lhs, NULL_EXPR), new BinaryOperatorExpr(BinaryOperator.NotIdentical, expr.lhs, new LiteralExpr(undefined))), expr.lhs, expr.rhs);
|
|
10208
|
+
}, VisitorContextFlag.None);
|
|
10209
|
+
}
|
|
9697
10210
|
}
|
|
9698
|
-
return call(instruction, args);
|
|
9699
|
-
}
|
|
9700
|
-
function elementEnd() {
|
|
9701
|
-
return call(Identifiers.elementEnd, []);
|
|
9702
|
-
}
|
|
9703
|
-
function elementContainerStart(slot, constIndex, localRefIndex) {
|
|
9704
|
-
return elementOrContainerBase(Identifiers.elementContainerStart, slot, /* tag */ null, constIndex, localRefIndex);
|
|
9705
10211
|
}
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
return call(Identifiers.listener, [
|
|
9724
|
-
literal(name),
|
|
9725
|
-
handlerFn,
|
|
9726
|
-
]);
|
|
9727
|
-
}
|
|
9728
|
-
function pipe(slot, name) {
|
|
9729
|
-
return call(Identifiers.pipe, [
|
|
9730
|
-
literal(slot),
|
|
9731
|
-
literal(name),
|
|
9732
|
-
]);
|
|
9733
|
-
}
|
|
9734
|
-
function advance(delta) {
|
|
9735
|
-
return call(Identifiers.advance, [
|
|
9736
|
-
literal(delta),
|
|
9737
|
-
]);
|
|
9738
|
-
}
|
|
9739
|
-
function reference(slot) {
|
|
9740
|
-
return importExpr(Identifiers.reference).callFn([
|
|
9741
|
-
literal(slot),
|
|
9742
|
-
]);
|
|
9743
|
-
}
|
|
9744
|
-
function nextContext(steps) {
|
|
9745
|
-
return importExpr(Identifiers.nextContext).callFn(steps === 1 ? [] : [literal(steps)]);
|
|
9746
|
-
}
|
|
9747
|
-
function getCurrentView() {
|
|
9748
|
-
return importExpr(Identifiers.getCurrentView).callFn([]);
|
|
9749
|
-
}
|
|
9750
|
-
function restoreView(savedView) {
|
|
9751
|
-
return importExpr(Identifiers.restoreView).callFn([
|
|
9752
|
-
savedView,
|
|
9753
|
-
]);
|
|
9754
|
-
}
|
|
9755
|
-
function resetView(returnValue) {
|
|
9756
|
-
return importExpr(Identifiers.resetView).callFn([
|
|
9757
|
-
returnValue,
|
|
9758
|
-
]);
|
|
10212
|
+
|
|
10213
|
+
/**
|
|
10214
|
+
* Generate a preamble sequence for each view creation block and listener function which declares
|
|
10215
|
+
* any variables that be referenced in other operations in the block.
|
|
10216
|
+
*
|
|
10217
|
+
* Variables generated include:
|
|
10218
|
+
* * a saved view context to be used to restore the current view in event listeners.
|
|
10219
|
+
* * the context of the restored view within event listener handlers.
|
|
10220
|
+
* * context variables from the current view as well as all parent views (including the root
|
|
10221
|
+
* context if needed).
|
|
10222
|
+
* * local references from elements within the current view and any lexical parents.
|
|
10223
|
+
*
|
|
10224
|
+
* Variables are generated here unconditionally, and may optimized away in future operations if it
|
|
10225
|
+
* turns out their values (and any side effects) are unused.
|
|
10226
|
+
*/
|
|
10227
|
+
function phaseGenerateVariables(cpl) {
|
|
10228
|
+
recursivelyProcessView(cpl.root, /* there is no parent scope for the root view */ null);
|
|
9759
10229
|
}
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
10230
|
+
/**
|
|
10231
|
+
* Process the given `ViewCompilation` and generate preambles for it and any listeners that it
|
|
10232
|
+
* declares.
|
|
10233
|
+
*
|
|
10234
|
+
* @param `parentScope` a scope extracted from the parent view which captures any variables which
|
|
10235
|
+
* should be inherited by this view. `null` if the current view is the root view.
|
|
10236
|
+
*/
|
|
10237
|
+
function recursivelyProcessView(view, parentScope) {
|
|
10238
|
+
// Extract a `Scope` from this view.
|
|
10239
|
+
const scope = getScopeForView(view, parentScope);
|
|
10240
|
+
// Embedded views require an operation to save/restore the view context.
|
|
10241
|
+
if (view.parent !== null) {
|
|
10242
|
+
// Start the view creation block with an operation to save the current view context. This may be
|
|
10243
|
+
// used to restore the view context in any listeners that may be present.
|
|
9764
10244
|
}
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
Identifiers.pipeBind3,
|
|
9777
|
-
Identifiers.pipeBind4,
|
|
9778
|
-
];
|
|
9779
|
-
function pipeBind(slot, varOffset, args) {
|
|
9780
|
-
if (args.length < 1 || args.length > PIPE_BINDINGS.length) {
|
|
9781
|
-
throw new Error(`pipeBind() argument count out of bounds`);
|
|
10245
|
+
for (const op of view.create) {
|
|
10246
|
+
switch (op.kind) {
|
|
10247
|
+
case OpKind.Template:
|
|
10248
|
+
// Descend into child embedded views.
|
|
10249
|
+
recursivelyProcessView(view.tpl.views.get(op.xref), scope);
|
|
10250
|
+
break;
|
|
10251
|
+
case OpKind.Listener:
|
|
10252
|
+
// Prepend variables to listener handler functions.
|
|
10253
|
+
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
|
|
10254
|
+
break;
|
|
10255
|
+
}
|
|
9782
10256
|
}
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
literal(varOffset),
|
|
9787
|
-
...args,
|
|
9788
|
-
]);
|
|
9789
|
-
}
|
|
9790
|
-
function pipeBindV(slot, varOffset, args) {
|
|
9791
|
-
return importExpr(Identifiers.pipeBindV).callFn([
|
|
9792
|
-
literal(slot),
|
|
9793
|
-
literal(varOffset),
|
|
9794
|
-
args,
|
|
9795
|
-
]);
|
|
10257
|
+
// Prepend the declarations for all available variables in scope to the `update` block.
|
|
10258
|
+
const preambleOps = generateVariablesInScopeForView(view, scope);
|
|
10259
|
+
view.update.prepend(preambleOps);
|
|
9796
10260
|
}
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
10261
|
+
/**
|
|
10262
|
+
* Process a view and generate a `Scope` representing the variables available for reference within
|
|
10263
|
+
* that view.
|
|
10264
|
+
*/
|
|
10265
|
+
function getScopeForView(view, parent) {
|
|
10266
|
+
const scope = {
|
|
10267
|
+
view: view.xref,
|
|
10268
|
+
viewContextVariable: {
|
|
10269
|
+
kind: SemanticVariableKind.Context,
|
|
10270
|
+
name: null,
|
|
10271
|
+
view: view.xref,
|
|
10272
|
+
},
|
|
10273
|
+
contextVariables: new Map(),
|
|
10274
|
+
references: [],
|
|
10275
|
+
parent,
|
|
10276
|
+
};
|
|
10277
|
+
for (const identifier of view.contextVariables.keys()) {
|
|
10278
|
+
scope.contextVariables.set(identifier, {
|
|
10279
|
+
kind: SemanticVariableKind.Identifier,
|
|
10280
|
+
name: null,
|
|
10281
|
+
identifier,
|
|
10282
|
+
});
|
|
9804
10283
|
}
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
10284
|
+
for (const op of view.create) {
|
|
10285
|
+
switch (op.kind) {
|
|
10286
|
+
case OpKind.Element:
|
|
10287
|
+
case OpKind.ElementStart:
|
|
10288
|
+
case OpKind.Template:
|
|
10289
|
+
if (!Array.isArray(op.localRefs)) {
|
|
10290
|
+
throw new Error(`AssertionError: expected localRefs to be an array`);
|
|
10291
|
+
}
|
|
10292
|
+
// Record available local references from this element.
|
|
10293
|
+
for (let offset = 0; offset < op.localRefs.length; offset++) {
|
|
10294
|
+
scope.references.push({
|
|
10295
|
+
name: op.localRefs[offset].name,
|
|
10296
|
+
targetId: op.xref,
|
|
10297
|
+
offset,
|
|
10298
|
+
variable: {
|
|
10299
|
+
kind: SemanticVariableKind.Identifier,
|
|
10300
|
+
name: null,
|
|
10301
|
+
identifier: op.localRefs[offset].name,
|
|
10302
|
+
},
|
|
10303
|
+
});
|
|
10304
|
+
}
|
|
10305
|
+
break;
|
|
9809
10306
|
}
|
|
9810
|
-
// idx points at the last string.
|
|
9811
|
-
interpolationArgs.push(literal(strings[idx]));
|
|
9812
10307
|
}
|
|
9813
|
-
return
|
|
10308
|
+
return scope;
|
|
9814
10309
|
}
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
10310
|
+
/**
|
|
10311
|
+
* Generate declarations for all variables that are in scope for a given view.
|
|
10312
|
+
*
|
|
10313
|
+
* This is a recursive process, as views inherit variables available from their parent view, which
|
|
10314
|
+
* itself may have inherited variables, etc.
|
|
10315
|
+
*/
|
|
10316
|
+
function generateVariablesInScopeForView(view, scope) {
|
|
10317
|
+
const newOps = [];
|
|
10318
|
+
if (scope.view !== view.xref) {
|
|
10319
|
+
// Before generating variables for a parent view, we need to switch to the context of the parent
|
|
10320
|
+
// view with a `nextContext` expression. This context switching operation itself declares a
|
|
10321
|
+
// variable, because the context of the view may be referenced directly.
|
|
10322
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), scope.viewContextVariable, new NextContextExpr()));
|
|
9818
10323
|
}
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
10324
|
+
// Add variables for all context variables available in this scope's view.
|
|
10325
|
+
for (const [name, value] of view.tpl.views.get(scope.view).contextVariables) {
|
|
10326
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), scope.contextVariables.get(name), new ReadPropExpr(new ContextExpr(scope.view), value)));
|
|
9822
10327
|
}
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
interpolationArgs.push(literal(strings[idx]), expressions[idx]);
|
|
9827
|
-
}
|
|
9828
|
-
// idx points at the last string.
|
|
9829
|
-
interpolationArgs.push(literal(strings[idx]));
|
|
10328
|
+
// Add variables for all local references declared for elements in this scope.
|
|
10329
|
+
for (const ref of scope.references) {
|
|
10330
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
9830
10331
|
}
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
fn,
|
|
9837
|
-
], args);
|
|
9838
|
-
}
|
|
9839
|
-
function call(instruction, args) {
|
|
9840
|
-
return createStatementOp(importExpr(instruction).callFn(args).toStmt());
|
|
10332
|
+
if (scope.parent !== null) {
|
|
10333
|
+
// Recursively add variables from the parent scope.
|
|
10334
|
+
newOps.push(...generateVariablesInScopeForView(view, scope.parent));
|
|
10335
|
+
}
|
|
10336
|
+
return newOps;
|
|
9841
10337
|
}
|
|
10338
|
+
|
|
9842
10339
|
/**
|
|
9843
|
-
*
|
|
10340
|
+
* Lifts local reference declarations on element-like structures within each view into an entry in
|
|
10341
|
+
* the `consts` array for the whole component.
|
|
9844
10342
|
*/
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
}
|
|
9865
|
-
/**
|
|
9866
|
-
* `InterpolationConfig` for the `propertyInterpolate` instruction.
|
|
9867
|
-
*/
|
|
9868
|
-
const PROPERTY_INTERPOLATE_CONFIG = {
|
|
9869
|
-
constant: [
|
|
9870
|
-
Identifiers.propertyInterpolate,
|
|
9871
|
-
Identifiers.propertyInterpolate1,
|
|
9872
|
-
Identifiers.propertyInterpolate2,
|
|
9873
|
-
Identifiers.propertyInterpolate3,
|
|
9874
|
-
Identifiers.propertyInterpolate4,
|
|
9875
|
-
Identifiers.propertyInterpolate5,
|
|
9876
|
-
Identifiers.propertyInterpolate6,
|
|
9877
|
-
Identifiers.propertyInterpolate7,
|
|
9878
|
-
Identifiers.propertyInterpolate8,
|
|
9879
|
-
],
|
|
9880
|
-
variable: Identifiers.propertyInterpolateV,
|
|
9881
|
-
mapping: n => {
|
|
9882
|
-
if (n % 2 === 0) {
|
|
9883
|
-
throw new Error(`Expected odd number of arguments`);
|
|
10343
|
+
function phaseLocalRefs(cpl) {
|
|
10344
|
+
for (const view of cpl.views.values()) {
|
|
10345
|
+
for (const op of view.create) {
|
|
10346
|
+
switch (op.kind) {
|
|
10347
|
+
case OpKind.ElementStart:
|
|
10348
|
+
case OpKind.Element:
|
|
10349
|
+
case OpKind.Template:
|
|
10350
|
+
if (!Array.isArray(op.localRefs)) {
|
|
10351
|
+
throw new Error(`AssertionError: expected localRefs to be an array still`);
|
|
10352
|
+
}
|
|
10353
|
+
op.numSlotsUsed += op.localRefs.length;
|
|
10354
|
+
if (op.localRefs.length > 0) {
|
|
10355
|
+
const localRefs = serializeLocalRefs(op.localRefs);
|
|
10356
|
+
op.localRefs = cpl.addConst(localRefs);
|
|
10357
|
+
}
|
|
10358
|
+
else {
|
|
10359
|
+
op.localRefs = null;
|
|
10360
|
+
}
|
|
10361
|
+
break;
|
|
10362
|
+
}
|
|
9884
10363
|
}
|
|
9885
|
-
return (n - 1) / 2;
|
|
9886
|
-
},
|
|
9887
|
-
};
|
|
9888
|
-
const PURE_FUNCTION_CONFIG = {
|
|
9889
|
-
constant: [
|
|
9890
|
-
Identifiers.pureFunction0,
|
|
9891
|
-
Identifiers.pureFunction1,
|
|
9892
|
-
Identifiers.pureFunction2,
|
|
9893
|
-
Identifiers.pureFunction3,
|
|
9894
|
-
Identifiers.pureFunction4,
|
|
9895
|
-
Identifiers.pureFunction5,
|
|
9896
|
-
Identifiers.pureFunction6,
|
|
9897
|
-
Identifiers.pureFunction7,
|
|
9898
|
-
Identifiers.pureFunction8,
|
|
9899
|
-
],
|
|
9900
|
-
variable: Identifiers.pureFunctionV,
|
|
9901
|
-
mapping: n => n,
|
|
9902
|
-
};
|
|
9903
|
-
function callVariadicInstructionExpr(config, baseArgs, interpolationArgs) {
|
|
9904
|
-
const n = config.mapping(interpolationArgs.length);
|
|
9905
|
-
if (n < config.constant.length) {
|
|
9906
|
-
// Constant calling pattern.
|
|
9907
|
-
return importExpr(config.constant[n]).callFn([...baseArgs, ...interpolationArgs]);
|
|
9908
|
-
}
|
|
9909
|
-
else if (config.variable !== null) {
|
|
9910
|
-
// Variable calling pattern.
|
|
9911
|
-
return importExpr(config.variable).callFn([...baseArgs, literalArr(interpolationArgs)]);
|
|
9912
|
-
}
|
|
9913
|
-
else {
|
|
9914
|
-
throw new Error(`AssertionError: unable to call variadic function`);
|
|
9915
10364
|
}
|
|
9916
10365
|
}
|
|
9917
|
-
function
|
|
9918
|
-
|
|
10366
|
+
function serializeLocalRefs(refs) {
|
|
10367
|
+
const constRefs = [];
|
|
10368
|
+
for (const ref of refs) {
|
|
10369
|
+
constRefs.push(literal(ref.name), literal(ref.target));
|
|
10370
|
+
}
|
|
10371
|
+
return literalArr(constRefs);
|
|
9919
10372
|
}
|
|
9920
10373
|
|
|
9921
10374
|
/**
|
|
9922
|
-
*
|
|
9923
|
-
* runtime calls in their place.
|
|
10375
|
+
* Generate names for functions and variables across all views.
|
|
9924
10376
|
*
|
|
9925
|
-
*
|
|
9926
|
-
*
|
|
9927
|
-
* `ir.StatementOp`s (which wrap generated `o.Statement`s).
|
|
10377
|
+
* This includes propagating those names into any `ir.ReadVariableExpr`s of those variables, so that
|
|
10378
|
+
* the reads can be emitted correctly.
|
|
9928
10379
|
*/
|
|
9929
|
-
function
|
|
9930
|
-
|
|
9931
|
-
reifyCreateOperations(view, view.create);
|
|
9932
|
-
reifyUpdateOperations(view, view.update);
|
|
9933
|
-
}
|
|
10380
|
+
function phaseNaming(cpl) {
|
|
10381
|
+
addNamesToView(cpl.root, cpl.componentName, { index: 0 });
|
|
9934
10382
|
}
|
|
9935
|
-
function
|
|
9936
|
-
|
|
9937
|
-
|
|
10383
|
+
function addNamesToView(view, baseName, state) {
|
|
10384
|
+
if (view.fnName === null) {
|
|
10385
|
+
view.fnName = `${baseName}_Template`;
|
|
10386
|
+
}
|
|
10387
|
+
// Keep track of the names we assign to variables in the view. We'll need to propagate these
|
|
10388
|
+
// into reads of those variables afterwards.
|
|
10389
|
+
const varNames = new Map();
|
|
10390
|
+
for (const op of view.ops()) {
|
|
9938
10391
|
switch (op.kind) {
|
|
9939
|
-
case OpKind.
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
case OpKind.ElementEnd:
|
|
9949
|
-
OpList.replace(op, elementEnd());
|
|
9950
|
-
break;
|
|
9951
|
-
case OpKind.ContainerStart:
|
|
9952
|
-
OpList.replace(op, elementContainerStart(op.slot, op.attributes, op.localRefs));
|
|
9953
|
-
break;
|
|
9954
|
-
case OpKind.Container:
|
|
9955
|
-
OpList.replace(op, elementContainer(op.slot, op.attributes, op.localRefs));
|
|
10392
|
+
case OpKind.Listener:
|
|
10393
|
+
if (op.handlerFnName === null) {
|
|
10394
|
+
// TODO(alxhub): convert this temporary name to match how the
|
|
10395
|
+
// `TemplateDefinitionBuilder` names listener functions.
|
|
10396
|
+
if (op.slot === null) {
|
|
10397
|
+
throw new Error(`Expected a slot to be assigned`);
|
|
10398
|
+
}
|
|
10399
|
+
op.handlerFnName = `${view.fnName}_${op.tag}_${op.name}_${op.slot}_listener`;
|
|
10400
|
+
}
|
|
9956
10401
|
break;
|
|
9957
|
-
case OpKind.
|
|
9958
|
-
|
|
10402
|
+
case OpKind.Variable:
|
|
10403
|
+
varNames.set(op.xref, getVariableName(op.variable, state));
|
|
9959
10404
|
break;
|
|
9960
10405
|
case OpKind.Template:
|
|
9961
10406
|
const childView = view.tpl.views.get(op.xref);
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
case OpKind.Pipe:
|
|
9965
|
-
OpList.replace(op, pipe(op.slot, op.name));
|
|
9966
|
-
break;
|
|
9967
|
-
case OpKind.Listener:
|
|
9968
|
-
const listenerFn = reifyListenerHandler(view, op.handlerFnName, op.handlerOps);
|
|
9969
|
-
OpList.replace(op, listener(op.name, listenerFn));
|
|
9970
|
-
break;
|
|
9971
|
-
case OpKind.Variable:
|
|
9972
|
-
if (op.variable.name === null) {
|
|
9973
|
-
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
10407
|
+
if (op.slot === null) {
|
|
10408
|
+
throw new Error(`Expected slot to be assigned`);
|
|
9974
10409
|
}
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
// Pass statement operations directly through.
|
|
10410
|
+
// TODO: properly escape the tag name.
|
|
10411
|
+
const safeTagName = op.tag.replace('-', '_');
|
|
10412
|
+
addNamesToView(childView, `${baseName}_${safeTagName}_${op.slot}`, state);
|
|
9979
10413
|
break;
|
|
9980
|
-
default:
|
|
9981
|
-
throw new Error(`AssertionError: Unsupported reification of create op ${OpKind[op.kind]}`);
|
|
9982
10414
|
}
|
|
9983
10415
|
}
|
|
10416
|
+
// Having named all variables declared in the view, now we can push those names into the
|
|
10417
|
+
// `ir.ReadVariableExpr` expressions which represent reads of those variables.
|
|
10418
|
+
for (const op of view.ops()) {
|
|
10419
|
+
visitExpressionsInOp(op, expr => {
|
|
10420
|
+
if (!(expr instanceof ReadVariableExpr) || expr.name !== null) {
|
|
10421
|
+
return;
|
|
10422
|
+
}
|
|
10423
|
+
if (!varNames.has(expr.xref)) {
|
|
10424
|
+
throw new Error(`Variable ${expr.xref} not yet named`);
|
|
10425
|
+
}
|
|
10426
|
+
expr.name = varNames.get(expr.xref);
|
|
10427
|
+
});
|
|
10428
|
+
}
|
|
9984
10429
|
}
|
|
9985
|
-
function
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
OpList.replace(op, advance(op.delta));
|
|
9991
|
-
break;
|
|
9992
|
-
case OpKind.Property:
|
|
9993
|
-
OpList.replace(op, property(op.name, op.expression));
|
|
9994
|
-
break;
|
|
9995
|
-
case OpKind.InterpolateProperty:
|
|
9996
|
-
OpList.replace(op, propertyInterpolate(op.name, op.strings, op.expressions));
|
|
9997
|
-
break;
|
|
9998
|
-
case OpKind.InterpolateText:
|
|
9999
|
-
OpList.replace(op, textInterpolate(op.strings, op.expressions));
|
|
10000
|
-
break;
|
|
10001
|
-
case OpKind.Variable:
|
|
10002
|
-
if (op.variable.name === null) {
|
|
10003
|
-
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
10004
|
-
}
|
|
10005
|
-
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
10006
|
-
break;
|
|
10007
|
-
case OpKind.Statement:
|
|
10008
|
-
// Pass statement operations directly through.
|
|
10430
|
+
function getVariableName(variable, state) {
|
|
10431
|
+
if (variable.name === null) {
|
|
10432
|
+
switch (variable.kind) {
|
|
10433
|
+
case SemanticVariableKind.Identifier:
|
|
10434
|
+
variable.name = `${variable.identifier}_${state.index++}`;
|
|
10009
10435
|
break;
|
|
10010
10436
|
default:
|
|
10011
|
-
|
|
10437
|
+
variable.name = `_r${state.index++}`;
|
|
10438
|
+
break;
|
|
10012
10439
|
}
|
|
10013
10440
|
}
|
|
10441
|
+
return variable.name;
|
|
10014
10442
|
}
|
|
10015
|
-
|
|
10016
|
-
if (!isIrExpression(expr)) {
|
|
10017
|
-
return expr;
|
|
10018
|
-
}
|
|
10019
|
-
switch (expr.kind) {
|
|
10020
|
-
case ExpressionKind.NextContext:
|
|
10021
|
-
return nextContext(expr.steps);
|
|
10022
|
-
case ExpressionKind.Reference:
|
|
10023
|
-
return reference(expr.slot + 1 + expr.offset);
|
|
10024
|
-
case ExpressionKind.LexicalRead:
|
|
10025
|
-
throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
|
|
10026
|
-
case ExpressionKind.RestoreView:
|
|
10027
|
-
if (typeof expr.view === 'number') {
|
|
10028
|
-
throw new Error(`AssertionError: unresolved RestoreView`);
|
|
10029
|
-
}
|
|
10030
|
-
return restoreView(expr.view);
|
|
10031
|
-
case ExpressionKind.ResetView:
|
|
10032
|
-
return resetView(expr.expr);
|
|
10033
|
-
case ExpressionKind.GetCurrentView:
|
|
10034
|
-
return getCurrentView();
|
|
10035
|
-
case ExpressionKind.ReadVariable:
|
|
10036
|
-
if (expr.name === null) {
|
|
10037
|
-
throw new Error(`Read of unnamed variable ${expr.xref}`);
|
|
10038
|
-
}
|
|
10039
|
-
return variable(expr.name);
|
|
10040
|
-
case ExpressionKind.PureFunctionExpr:
|
|
10041
|
-
if (expr.fn === null) {
|
|
10042
|
-
throw new Error(`AssertionError: expected PureFunctions to have been extracted`);
|
|
10043
|
-
}
|
|
10044
|
-
return pureFunction(expr.varOffset, expr.fn, expr.args);
|
|
10045
|
-
case ExpressionKind.PureFunctionParameterExpr:
|
|
10046
|
-
throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
|
|
10047
|
-
case ExpressionKind.PipeBinding:
|
|
10048
|
-
return pipeBind(expr.slot, expr.varOffset, expr.args);
|
|
10049
|
-
case ExpressionKind.PipeBindingVariadic:
|
|
10050
|
-
return pipeBindV(expr.slot, expr.varOffset, expr.args);
|
|
10051
|
-
default:
|
|
10052
|
-
throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
|
|
10053
|
-
}
|
|
10054
|
-
}
|
|
10443
|
+
|
|
10055
10444
|
/**
|
|
10056
|
-
*
|
|
10057
|
-
*
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10445
|
+
* Merges logically sequential `NextContextExpr` operations.
|
|
10446
|
+
*
|
|
10447
|
+
* `NextContextExpr` can be referenced repeatedly, "popping" the runtime's context stack each time.
|
|
10448
|
+
* When two such expressions appear back-to-back, it's possible to merge them together into a single
|
|
10449
|
+
* `NextContextExpr` that steps multiple contexts. This merging is possible if all conditions are
|
|
10450
|
+
* met:
|
|
10451
|
+
*
|
|
10452
|
+
* * The result of the `NextContextExpr` that's folded into the subsequent one is not stored (that
|
|
10453
|
+
* is, the call is purely side-effectful).
|
|
10454
|
+
* * No operations in between them uses the implicit context.
|
|
10455
|
+
*/
|
|
10456
|
+
function phaseMergeNextContext(cpl) {
|
|
10457
|
+
for (const view of cpl.views.values()) {
|
|
10458
|
+
for (const op of view.create) {
|
|
10459
|
+
if (op.kind === OpKind.Listener) {
|
|
10460
|
+
mergeNextContextsInOps(op.handlerOps);
|
|
10461
|
+
}
|
|
10069
10462
|
}
|
|
10070
|
-
|
|
10463
|
+
mergeNextContextsInOps(view.update);
|
|
10464
|
+
}
|
|
10465
|
+
}
|
|
10466
|
+
function mergeNextContextsInOps(ops) {
|
|
10467
|
+
for (const op of ops) {
|
|
10468
|
+
// Look for a candidate operation to maybe merge.
|
|
10469
|
+
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement) ||
|
|
10470
|
+
!(op.statement.expr instanceof NextContextExpr)) {
|
|
10471
|
+
continue;
|
|
10472
|
+
}
|
|
10473
|
+
const mergeSteps = op.statement.expr.steps;
|
|
10474
|
+
// Try to merge this `ir.NextContextExpr`.
|
|
10475
|
+
let tryToMerge = true;
|
|
10476
|
+
for (let candidate = op.next; candidate.kind !== OpKind.ListEnd && tryToMerge; candidate = candidate.next) {
|
|
10477
|
+
visitExpressionsInOp(candidate, (expr, flags) => {
|
|
10478
|
+
if (!isIrExpression(expr)) {
|
|
10479
|
+
return expr;
|
|
10480
|
+
}
|
|
10481
|
+
if (!tryToMerge) {
|
|
10482
|
+
// Either we've already merged, or failed to merge.
|
|
10483
|
+
return;
|
|
10484
|
+
}
|
|
10485
|
+
if (flags & VisitorContextFlag.InChildOperation) {
|
|
10486
|
+
// We cannot merge into child operations.
|
|
10487
|
+
return;
|
|
10488
|
+
}
|
|
10489
|
+
switch (expr.kind) {
|
|
10490
|
+
case ExpressionKind.NextContext:
|
|
10491
|
+
// Merge the previous `ir.NextContextExpr` into this one.
|
|
10492
|
+
expr.steps += mergeSteps;
|
|
10493
|
+
OpList.remove(op);
|
|
10494
|
+
tryToMerge = false;
|
|
10495
|
+
break;
|
|
10496
|
+
case ExpressionKind.GetCurrentView:
|
|
10497
|
+
case ExpressionKind.Reference:
|
|
10498
|
+
// Can't merge past a dependency on the context.
|
|
10499
|
+
tryToMerge = false;
|
|
10500
|
+
break;
|
|
10501
|
+
}
|
|
10502
|
+
});
|
|
10503
|
+
}
|
|
10504
|
+
}
|
|
10505
|
+
}
|
|
10506
|
+
|
|
10507
|
+
const CONTAINER_TAG = 'ng-container';
|
|
10508
|
+
/**
|
|
10509
|
+
* Replace an `Element` or `ElementStart` whose tag is `ng-container` with a specific op.
|
|
10510
|
+
*/
|
|
10511
|
+
function phaseNgContainer(cpl) {
|
|
10512
|
+
for (const [_, view] of cpl.views) {
|
|
10513
|
+
const updatedElementXrefs = new Set();
|
|
10514
|
+
for (const op of view.create) {
|
|
10515
|
+
if (op.kind === OpKind.ElementStart && op.tag === CONTAINER_TAG) {
|
|
10516
|
+
// Transmute the `ElementStart` instruction to `ContainerStart`.
|
|
10517
|
+
op.kind = OpKind.ContainerStart;
|
|
10518
|
+
updatedElementXrefs.add(op.xref);
|
|
10519
|
+
}
|
|
10520
|
+
if (op.kind === OpKind.ElementEnd && updatedElementXrefs.has(op.xref)) {
|
|
10521
|
+
// This `ElementEnd` is associated with an `ElementStart` we already transmuted.
|
|
10522
|
+
op.kind = OpKind.ContainerEnd;
|
|
10523
|
+
}
|
|
10524
|
+
}
|
|
10525
|
+
}
|
|
10526
|
+
}
|
|
10527
|
+
|
|
10528
|
+
function phasePipeCreation(cpl) {
|
|
10529
|
+
for (const view of cpl.views.values()) {
|
|
10530
|
+
processPipeBindingsInView(view);
|
|
10531
|
+
}
|
|
10532
|
+
}
|
|
10533
|
+
function processPipeBindingsInView(view) {
|
|
10534
|
+
for (const updateOp of view.update) {
|
|
10535
|
+
visitExpressionsInOp(updateOp, (expr, flags) => {
|
|
10536
|
+
if (!isIrExpression(expr)) {
|
|
10537
|
+
return;
|
|
10538
|
+
}
|
|
10539
|
+
if (expr.kind !== ExpressionKind.PipeBinding) {
|
|
10540
|
+
return;
|
|
10541
|
+
}
|
|
10542
|
+
if (flags & VisitorContextFlag.InChildOperation) {
|
|
10543
|
+
throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
|
|
10544
|
+
}
|
|
10545
|
+
if (!hasDependsOnSlotContextTrait(updateOp)) {
|
|
10546
|
+
throw new Error(`AssertionError: pipe binding associated with non-slot operation ${OpKind[updateOp.kind]}`);
|
|
10547
|
+
}
|
|
10548
|
+
addPipeToCreationBlock(view, updateOp.target, expr);
|
|
10549
|
+
});
|
|
10550
|
+
}
|
|
10551
|
+
}
|
|
10552
|
+
function addPipeToCreationBlock(view, afterTargetXref, binding) {
|
|
10553
|
+
// Find the appropriate point to insert the Pipe creation operation.
|
|
10554
|
+
// We're looking for `afterTargetXref` (and also want to insert after any other pipe operations
|
|
10555
|
+
// which might be beyond it).
|
|
10556
|
+
for (let op = view.create.head.next; op.kind !== OpKind.ListEnd; op = op.next) {
|
|
10557
|
+
if (!hasConsumesSlotTrait(op)) {
|
|
10558
|
+
continue;
|
|
10559
|
+
}
|
|
10560
|
+
if (op.xref !== afterTargetXref) {
|
|
10561
|
+
continue;
|
|
10562
|
+
}
|
|
10563
|
+
// We've found a tentative insertion point; however, we also want to skip past any _other_ pipe
|
|
10564
|
+
// operations present.
|
|
10565
|
+
while (op.next.kind === OpKind.Pipe) {
|
|
10566
|
+
op = op.next;
|
|
10567
|
+
}
|
|
10568
|
+
const pipe = createPipeOp(binding.target, binding.name);
|
|
10569
|
+
OpList.insertBefore(pipe, op.next);
|
|
10570
|
+
// This completes adding the pipe to the creation block.
|
|
10571
|
+
return;
|
|
10572
|
+
}
|
|
10573
|
+
// At this point, we've failed to add the pipe to the creation block.
|
|
10574
|
+
throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`);
|
|
10575
|
+
}
|
|
10576
|
+
|
|
10577
|
+
function phasePipeVariadic(cpl) {
|
|
10578
|
+
for (const view of cpl.views.values()) {
|
|
10579
|
+
for (const op of view.update) {
|
|
10580
|
+
transformExpressionsInOp(op, expr => {
|
|
10581
|
+
if (!(expr instanceof PipeBindingExpr)) {
|
|
10582
|
+
return expr;
|
|
10583
|
+
}
|
|
10584
|
+
// Pipes are variadic if they have more than 4 arguments.
|
|
10585
|
+
if (expr.args.length <= 4) {
|
|
10586
|
+
return expr;
|
|
10587
|
+
}
|
|
10588
|
+
return new PipeBindingVariadicExpr(expr.target, expr.name, literalArr(expr.args), expr.args.length);
|
|
10589
|
+
}, VisitorContextFlag.None);
|
|
10590
|
+
}
|
|
10591
|
+
}
|
|
10592
|
+
}
|
|
10593
|
+
|
|
10594
|
+
function phasePureFunctionExtraction(cpl) {
|
|
10595
|
+
for (const view of cpl.views.values()) {
|
|
10596
|
+
for (const op of view.ops()) {
|
|
10597
|
+
visitExpressionsInOp(op, expr => {
|
|
10598
|
+
if (!(expr instanceof PureFunctionExpr) || expr.body === null) {
|
|
10599
|
+
return;
|
|
10600
|
+
}
|
|
10601
|
+
const constantDef = new PureFunctionConstant(expr.args.length);
|
|
10602
|
+
expr.fn = cpl.pool.getSharedConstant(constantDef, expr.body);
|
|
10603
|
+
expr.body = null;
|
|
10604
|
+
});
|
|
10605
|
+
}
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
class PureFunctionConstant extends GenericKeyFn {
|
|
10609
|
+
constructor(numArgs) {
|
|
10610
|
+
super();
|
|
10611
|
+
this.numArgs = numArgs;
|
|
10612
|
+
}
|
|
10613
|
+
keyOf(expr) {
|
|
10614
|
+
if (expr instanceof PureFunctionParameterExpr) {
|
|
10615
|
+
return `param(${expr.index})`;
|
|
10616
|
+
}
|
|
10617
|
+
else {
|
|
10618
|
+
return super.keyOf(expr);
|
|
10619
|
+
}
|
|
10620
|
+
}
|
|
10621
|
+
toSharedConstantDeclaration(declName, keyExpr) {
|
|
10622
|
+
const fnParams = [];
|
|
10623
|
+
for (let idx = 0; idx < this.numArgs; idx++) {
|
|
10624
|
+
fnParams.push(new FnParam('_p' + idx));
|
|
10625
|
+
}
|
|
10626
|
+
// We will never visit `ir.PureFunctionParameterExpr`s that don't belong to us, because this
|
|
10627
|
+
// transform runs inside another visitor which will visit nested pure functions before this one.
|
|
10628
|
+
const returnExpr = transformExpressionsInExpression(keyExpr, expr => {
|
|
10629
|
+
if (!(expr instanceof PureFunctionParameterExpr)) {
|
|
10630
|
+
return expr;
|
|
10631
|
+
}
|
|
10632
|
+
return variable('_p' + expr.index);
|
|
10633
|
+
}, VisitorContextFlag.None);
|
|
10634
|
+
return new DeclareFunctionStmt(declName, fnParams, [new ReturnStatement(returnExpr)]);
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
|
|
10638
|
+
function phasePureLiteralStructures(cpl) {
|
|
10639
|
+
for (const view of cpl.views.values()) {
|
|
10640
|
+
for (const op of view.update) {
|
|
10641
|
+
transformExpressionsInOp(op, (expr, flags) => {
|
|
10642
|
+
if (flags & VisitorContextFlag.InChildOperation) {
|
|
10643
|
+
return expr;
|
|
10644
|
+
}
|
|
10645
|
+
if (expr instanceof LiteralArrayExpr) {
|
|
10646
|
+
return transformLiteralArray(expr);
|
|
10647
|
+
}
|
|
10648
|
+
else if (expr instanceof LiteralMapExpr) {
|
|
10649
|
+
return transformLiteralMap(expr);
|
|
10650
|
+
}
|
|
10651
|
+
return expr;
|
|
10652
|
+
}, VisitorContextFlag.None);
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
}
|
|
10656
|
+
function transformLiteralArray(expr) {
|
|
10657
|
+
const derivedEntries = [];
|
|
10658
|
+
const nonConstantArgs = [];
|
|
10659
|
+
for (const entry of expr.entries) {
|
|
10660
|
+
if (entry.isConstant()) {
|
|
10661
|
+
derivedEntries.push(entry);
|
|
10662
|
+
}
|
|
10663
|
+
else {
|
|
10664
|
+
const idx = nonConstantArgs.length;
|
|
10665
|
+
nonConstantArgs.push(entry);
|
|
10666
|
+
derivedEntries.push(new PureFunctionParameterExpr(idx));
|
|
10667
|
+
}
|
|
10668
|
+
}
|
|
10669
|
+
return new PureFunctionExpr(literalArr(derivedEntries), nonConstantArgs);
|
|
10670
|
+
}
|
|
10671
|
+
function transformLiteralMap(expr) {
|
|
10672
|
+
let derivedEntries = [];
|
|
10673
|
+
const nonConstantArgs = [];
|
|
10674
|
+
for (const entry of expr.entries) {
|
|
10675
|
+
if (entry.value.isConstant()) {
|
|
10676
|
+
derivedEntries.push(entry);
|
|
10677
|
+
}
|
|
10678
|
+
else {
|
|
10679
|
+
const idx = nonConstantArgs.length;
|
|
10680
|
+
nonConstantArgs.push(entry.value);
|
|
10681
|
+
derivedEntries.push(new LiteralMapEntry(entry.key, new PureFunctionParameterExpr(idx), entry.quoted));
|
|
10682
|
+
}
|
|
10683
|
+
}
|
|
10684
|
+
return new PureFunctionExpr(literalMap(derivedEntries), nonConstantArgs);
|
|
10685
|
+
}
|
|
10686
|
+
|
|
10687
|
+
// This file contains helpers for generating calls to Ivy instructions. In particular, each
|
|
10688
|
+
// instruction type is represented as a function, which may select a specific instruction variant
|
|
10689
|
+
// depending on the exact arguments.
|
|
10690
|
+
function element(slot, tag, constIndex, localRefIndex) {
|
|
10691
|
+
return elementOrContainerBase(Identifiers.element, slot, tag, constIndex, localRefIndex);
|
|
10692
|
+
}
|
|
10693
|
+
function elementStart(slot, tag, constIndex, localRefIndex) {
|
|
10694
|
+
return elementOrContainerBase(Identifiers.elementStart, slot, tag, constIndex, localRefIndex);
|
|
10695
|
+
}
|
|
10696
|
+
function elementOrContainerBase(instruction, slot, tag, constIndex, localRefIndex) {
|
|
10697
|
+
const args = [literal(slot)];
|
|
10698
|
+
if (tag !== null) {
|
|
10699
|
+
args.push(literal(tag));
|
|
10700
|
+
}
|
|
10701
|
+
if (localRefIndex !== null) {
|
|
10702
|
+
args.push(literal(constIndex), // might be null, but that's okay.
|
|
10703
|
+
literal(localRefIndex));
|
|
10704
|
+
}
|
|
10705
|
+
else if (constIndex !== null) {
|
|
10706
|
+
args.push(literal(constIndex));
|
|
10707
|
+
}
|
|
10708
|
+
return call(instruction, args);
|
|
10709
|
+
}
|
|
10710
|
+
function elementEnd() {
|
|
10711
|
+
return call(Identifiers.elementEnd, []);
|
|
10712
|
+
}
|
|
10713
|
+
function elementContainerStart(slot, constIndex, localRefIndex) {
|
|
10714
|
+
return elementOrContainerBase(Identifiers.elementContainerStart, slot, /* tag */ null, constIndex, localRefIndex);
|
|
10715
|
+
}
|
|
10716
|
+
function elementContainer(slot, constIndex, localRefIndex) {
|
|
10717
|
+
return elementOrContainerBase(Identifiers.elementContainer, slot, /* tag */ null, constIndex, localRefIndex);
|
|
10718
|
+
}
|
|
10719
|
+
function elementContainerEnd() {
|
|
10720
|
+
return call(Identifiers.elementContainerEnd, []);
|
|
10721
|
+
}
|
|
10722
|
+
function template(slot, templateFnRef, decls, vars, tag, constIndex) {
|
|
10723
|
+
return call(Identifiers.templateCreate, [
|
|
10724
|
+
literal(slot),
|
|
10725
|
+
templateFnRef,
|
|
10726
|
+
literal(decls),
|
|
10727
|
+
literal(vars),
|
|
10728
|
+
literal(tag),
|
|
10729
|
+
literal(constIndex),
|
|
10730
|
+
]);
|
|
10731
|
+
}
|
|
10732
|
+
function listener(name, handlerFn) {
|
|
10733
|
+
return call(Identifiers.listener, [
|
|
10734
|
+
literal(name),
|
|
10735
|
+
handlerFn,
|
|
10736
|
+
]);
|
|
10737
|
+
}
|
|
10738
|
+
function pipe(slot, name) {
|
|
10739
|
+
return call(Identifiers.pipe, [
|
|
10740
|
+
literal(slot),
|
|
10741
|
+
literal(name),
|
|
10742
|
+
]);
|
|
10743
|
+
}
|
|
10744
|
+
function advance(delta) {
|
|
10745
|
+
return call(Identifiers.advance, [
|
|
10746
|
+
literal(delta),
|
|
10747
|
+
]);
|
|
10748
|
+
}
|
|
10749
|
+
function reference(slot) {
|
|
10750
|
+
return importExpr(Identifiers.reference).callFn([
|
|
10751
|
+
literal(slot),
|
|
10752
|
+
]);
|
|
10753
|
+
}
|
|
10754
|
+
function nextContext(steps) {
|
|
10755
|
+
return importExpr(Identifiers.nextContext).callFn(steps === 1 ? [] : [literal(steps)]);
|
|
10756
|
+
}
|
|
10757
|
+
function getCurrentView() {
|
|
10758
|
+
return importExpr(Identifiers.getCurrentView).callFn([]);
|
|
10759
|
+
}
|
|
10760
|
+
function restoreView(savedView) {
|
|
10761
|
+
return importExpr(Identifiers.restoreView).callFn([
|
|
10762
|
+
savedView,
|
|
10763
|
+
]);
|
|
10764
|
+
}
|
|
10765
|
+
function resetView(returnValue) {
|
|
10766
|
+
return importExpr(Identifiers.resetView).callFn([
|
|
10767
|
+
returnValue,
|
|
10768
|
+
]);
|
|
10769
|
+
}
|
|
10770
|
+
function text(slot, initialValue) {
|
|
10771
|
+
const args = [literal(slot)];
|
|
10772
|
+
if (initialValue !== '') {
|
|
10773
|
+
args.push(literal(initialValue));
|
|
10071
10774
|
}
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10775
|
+
return call(Identifiers.text, args);
|
|
10776
|
+
}
|
|
10777
|
+
function property(name, expression) {
|
|
10778
|
+
return call(Identifiers.property, [
|
|
10779
|
+
literal(name),
|
|
10780
|
+
expression,
|
|
10781
|
+
]);
|
|
10782
|
+
}
|
|
10783
|
+
const PIPE_BINDINGS = [
|
|
10784
|
+
Identifiers.pipeBind1,
|
|
10785
|
+
Identifiers.pipeBind2,
|
|
10786
|
+
Identifiers.pipeBind3,
|
|
10787
|
+
Identifiers.pipeBind4,
|
|
10788
|
+
];
|
|
10789
|
+
function pipeBind(slot, varOffset, args) {
|
|
10790
|
+
if (args.length < 1 || args.length > PIPE_BINDINGS.length) {
|
|
10791
|
+
throw new Error(`pipeBind() argument count out of bounds`);
|
|
10079
10792
|
}
|
|
10080
|
-
|
|
10793
|
+
const instruction = PIPE_BINDINGS[args.length - 1];
|
|
10794
|
+
return importExpr(instruction).callFn([
|
|
10795
|
+
literal(slot),
|
|
10796
|
+
literal(varOffset),
|
|
10797
|
+
...args,
|
|
10798
|
+
]);
|
|
10081
10799
|
}
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10800
|
+
function pipeBindV(slot, varOffset, args) {
|
|
10801
|
+
return importExpr(Identifiers.pipeBindV).callFn([
|
|
10802
|
+
literal(slot),
|
|
10803
|
+
literal(varOffset),
|
|
10804
|
+
args,
|
|
10805
|
+
]);
|
|
10806
|
+
}
|
|
10807
|
+
function textInterpolate(strings, expressions) {
|
|
10808
|
+
if (strings.length < 1 || expressions.length !== strings.length - 1) {
|
|
10809
|
+
throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
|
|
10089
10810
|
}
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10811
|
+
const interpolationArgs = [];
|
|
10812
|
+
if (expressions.length === 1 && strings[0] === '' && strings[1] === '') {
|
|
10813
|
+
interpolationArgs.push(expressions[0]);
|
|
10814
|
+
}
|
|
10815
|
+
else {
|
|
10816
|
+
let idx;
|
|
10817
|
+
for (idx = 0; idx < expressions.length; idx++) {
|
|
10818
|
+
interpolationArgs.push(literal(strings[idx]), expressions[idx]);
|
|
10093
10819
|
}
|
|
10820
|
+
// idx points at the last string.
|
|
10821
|
+
interpolationArgs.push(literal(strings[idx]));
|
|
10094
10822
|
}
|
|
10823
|
+
return callVariadicInstruction(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs);
|
|
10095
10824
|
}
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
* assigned data slots of those operations to any expressions which reference them via
|
|
10100
|
-
* `UsesSlotIndexTrait`.
|
|
10101
|
-
*
|
|
10102
|
-
* This phase is also responsible for counting the number of slots used for each view (its `decls`)
|
|
10103
|
-
* and propagating that number into the `Template` operations which declare embedded views.
|
|
10104
|
-
*/
|
|
10105
|
-
function phaseSlotAllocation(cpl) {
|
|
10106
|
-
// Map of all declarations in all views within the component which require an assigned slot index.
|
|
10107
|
-
// This map needs to be global (across all views within the component) since it's possible to
|
|
10108
|
-
// reference a slot from one view from an expression within another (e.g. local references work
|
|
10109
|
-
// this way).
|
|
10110
|
-
const slotMap = new Map();
|
|
10111
|
-
// Process all views in the component and assign slot indexes.
|
|
10112
|
-
for (const [_, view] of cpl.views) {
|
|
10113
|
-
// Slot indices start at 0 for each view (and are not unique between views).
|
|
10114
|
-
let slotCount = 0;
|
|
10115
|
-
for (const op of view.create) {
|
|
10116
|
-
// Only consider declarations which consume data slots.
|
|
10117
|
-
if (!hasConsumesSlotTrait(op)) {
|
|
10118
|
-
continue;
|
|
10119
|
-
}
|
|
10120
|
-
// Assign slots to this declaration starting at the current `slotCount`.
|
|
10121
|
-
op.slot = slotCount;
|
|
10122
|
-
// And track its assigned slot in the `slotMap`.
|
|
10123
|
-
slotMap.set(op.xref, op.slot);
|
|
10124
|
-
// Each declaration may use more than 1 slot, so increment `slotCount` to reserve the number
|
|
10125
|
-
// of slots required.
|
|
10126
|
-
slotCount += op.numSlotsUsed;
|
|
10127
|
-
}
|
|
10128
|
-
// Record the total number of slots used on the view itself. This will later be propagated into
|
|
10129
|
-
// `ir.TemplateOp`s which declare those views (except for the root view).
|
|
10130
|
-
view.decls = slotCount;
|
|
10825
|
+
function propertyInterpolate(name, strings, expressions) {
|
|
10826
|
+
if (strings.length < 1 || expressions.length !== strings.length - 1) {
|
|
10827
|
+
throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
|
|
10131
10828
|
}
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
for (
|
|
10139
|
-
|
|
10140
|
-
// Record the number of slots used by the view this `ir.TemplateOp` declares in the
|
|
10141
|
-
// operation itself, so it can be emitted later.
|
|
10142
|
-
const childView = cpl.views.get(op.xref);
|
|
10143
|
-
op.decls = childView.decls;
|
|
10144
|
-
}
|
|
10145
|
-
if (hasUsesSlotIndexTrait(op) && op.slot === null) {
|
|
10146
|
-
if (!slotMap.has(op.target)) {
|
|
10147
|
-
// We do expect to find a slot allocated for everything which might be referenced.
|
|
10148
|
-
throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
|
|
10149
|
-
}
|
|
10150
|
-
op.slot = slotMap.get(op.target);
|
|
10151
|
-
}
|
|
10152
|
-
// Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
|
|
10153
|
-
visitExpressionsInOp(op, expr => {
|
|
10154
|
-
if (!isIrExpression(expr)) {
|
|
10155
|
-
return;
|
|
10156
|
-
}
|
|
10157
|
-
if (!hasUsesSlotIndexTrait(expr) || expr.slot !== null) {
|
|
10158
|
-
return;
|
|
10159
|
-
}
|
|
10160
|
-
// The `UsesSlotIndexExprTrait` indicates that this expression references something declared
|
|
10161
|
-
// in this component template by its slot index. Use the `target` `ir.XrefId` to find the
|
|
10162
|
-
// allocated slot for that declaration in `slotMap`.
|
|
10163
|
-
if (!slotMap.has(expr.target)) {
|
|
10164
|
-
// We do expect to find a slot allocated for everything which might be referenced.
|
|
10165
|
-
throw new Error(`AssertionError: no slot allocated for ${expr.constructor.name} target ${expr.target}`);
|
|
10166
|
-
}
|
|
10167
|
-
// Record the allocated slot on the expression.
|
|
10168
|
-
expr.slot = slotMap.get(expr.target);
|
|
10169
|
-
});
|
|
10829
|
+
const interpolationArgs = [];
|
|
10830
|
+
if (expressions.length === 1 && strings[0] === '' && strings[1] === '') {
|
|
10831
|
+
interpolationArgs.push(expressions[0]);
|
|
10832
|
+
}
|
|
10833
|
+
else {
|
|
10834
|
+
let idx;
|
|
10835
|
+
for (idx = 0; idx < expressions.length; idx++) {
|
|
10836
|
+
interpolationArgs.push(literal(strings[idx]), expressions[idx]);
|
|
10170
10837
|
}
|
|
10838
|
+
// idx points at the last string.
|
|
10839
|
+
interpolationArgs.push(literal(strings[idx]));
|
|
10171
10840
|
}
|
|
10841
|
+
return callVariadicInstruction(PROPERTY_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs);
|
|
10842
|
+
}
|
|
10843
|
+
function pureFunction(varOffset, fn, args) {
|
|
10844
|
+
return callVariadicInstructionExpr(PURE_FUNCTION_CONFIG, [
|
|
10845
|
+
literal(varOffset),
|
|
10846
|
+
fn,
|
|
10847
|
+
], args);
|
|
10848
|
+
}
|
|
10849
|
+
function call(instruction, args) {
|
|
10850
|
+
return createStatementOp(importExpr(instruction).callFn(args).toStmt());
|
|
10172
10851
|
}
|
|
10173
|
-
|
|
10174
10852
|
/**
|
|
10175
|
-
*
|
|
10176
|
-
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
10853
|
+
* `InterpolationConfig` for the `textInterpolate` instruction.
|
|
10177
10854
|
*/
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
if (hasConsumesVarsTrait(expr)) {
|
|
10195
|
-
varCount += varsUsedByIrExpression(expr);
|
|
10196
|
-
}
|
|
10197
|
-
});
|
|
10855
|
+
const TEXT_INTERPOLATE_CONFIG = {
|
|
10856
|
+
constant: [
|
|
10857
|
+
Identifiers.textInterpolate,
|
|
10858
|
+
Identifiers.textInterpolate1,
|
|
10859
|
+
Identifiers.textInterpolate2,
|
|
10860
|
+
Identifiers.textInterpolate3,
|
|
10861
|
+
Identifiers.textInterpolate4,
|
|
10862
|
+
Identifiers.textInterpolate5,
|
|
10863
|
+
Identifiers.textInterpolate6,
|
|
10864
|
+
Identifiers.textInterpolate7,
|
|
10865
|
+
Identifiers.textInterpolate8,
|
|
10866
|
+
],
|
|
10867
|
+
variable: Identifiers.textInterpolateV,
|
|
10868
|
+
mapping: n => {
|
|
10869
|
+
if (n % 2 === 0) {
|
|
10870
|
+
throw new Error(`Expected odd number of arguments`);
|
|
10198
10871
|
}
|
|
10199
|
-
|
|
10200
|
-
}
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10872
|
+
return (n - 1) / 2;
|
|
10873
|
+
},
|
|
10874
|
+
};
|
|
10875
|
+
/**
|
|
10876
|
+
* `InterpolationConfig` for the `propertyInterpolate` instruction.
|
|
10877
|
+
*/
|
|
10878
|
+
const PROPERTY_INTERPOLATE_CONFIG = {
|
|
10879
|
+
constant: [
|
|
10880
|
+
Identifiers.propertyInterpolate,
|
|
10881
|
+
Identifiers.propertyInterpolate1,
|
|
10882
|
+
Identifiers.propertyInterpolate2,
|
|
10883
|
+
Identifiers.propertyInterpolate3,
|
|
10884
|
+
Identifiers.propertyInterpolate4,
|
|
10885
|
+
Identifiers.propertyInterpolate5,
|
|
10886
|
+
Identifiers.propertyInterpolate6,
|
|
10887
|
+
Identifiers.propertyInterpolate7,
|
|
10888
|
+
Identifiers.propertyInterpolate8,
|
|
10889
|
+
],
|
|
10890
|
+
variable: Identifiers.propertyInterpolateV,
|
|
10891
|
+
mapping: n => {
|
|
10892
|
+
if (n % 2 === 0) {
|
|
10893
|
+
throw new Error(`Expected odd number of arguments`);
|
|
10210
10894
|
}
|
|
10895
|
+
return (n - 1) / 2;
|
|
10896
|
+
},
|
|
10897
|
+
};
|
|
10898
|
+
const PURE_FUNCTION_CONFIG = {
|
|
10899
|
+
constant: [
|
|
10900
|
+
Identifiers.pureFunction0,
|
|
10901
|
+
Identifiers.pureFunction1,
|
|
10902
|
+
Identifiers.pureFunction2,
|
|
10903
|
+
Identifiers.pureFunction3,
|
|
10904
|
+
Identifiers.pureFunction4,
|
|
10905
|
+
Identifiers.pureFunction5,
|
|
10906
|
+
Identifiers.pureFunction6,
|
|
10907
|
+
Identifiers.pureFunction7,
|
|
10908
|
+
Identifiers.pureFunction8,
|
|
10909
|
+
],
|
|
10910
|
+
variable: Identifiers.pureFunctionV,
|
|
10911
|
+
mapping: n => n,
|
|
10912
|
+
};
|
|
10913
|
+
function callVariadicInstructionExpr(config, baseArgs, interpolationArgs) {
|
|
10914
|
+
const n = config.mapping(interpolationArgs.length);
|
|
10915
|
+
if (n < config.constant.length) {
|
|
10916
|
+
// Constant calling pattern.
|
|
10917
|
+
return importExpr(config.constant[n]).callFn([...baseArgs, ...interpolationArgs]);
|
|
10211
10918
|
}
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
* count the variables used by any particular `op`.
|
|
10216
|
-
*/
|
|
10217
|
-
function varsUsedByOp(op) {
|
|
10218
|
-
switch (op.kind) {
|
|
10219
|
-
case OpKind.Property:
|
|
10220
|
-
// Property bindings use 1 variable slot.
|
|
10221
|
-
return 1;
|
|
10222
|
-
case OpKind.InterpolateText:
|
|
10223
|
-
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
10224
|
-
return op.expressions.length;
|
|
10225
|
-
case OpKind.InterpolateProperty:
|
|
10226
|
-
// `ir.InterpolatePropertyOp`s use a variable slot for each dynamic expression, plus one for
|
|
10227
|
-
// the result.
|
|
10228
|
-
return 1 + op.expressions.length;
|
|
10229
|
-
default:
|
|
10230
|
-
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
10919
|
+
else if (config.variable !== null) {
|
|
10920
|
+
// Variable calling pattern.
|
|
10921
|
+
return importExpr(config.variable).callFn([...baseArgs, literalArr(interpolationArgs)]);
|
|
10231
10922
|
}
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
switch (expr.kind) {
|
|
10235
|
-
case ExpressionKind.PureFunctionExpr:
|
|
10236
|
-
return 1 + expr.args.length;
|
|
10237
|
-
case ExpressionKind.PipeBinding:
|
|
10238
|
-
return 1 + expr.args.length;
|
|
10239
|
-
case ExpressionKind.PipeBindingVariadic:
|
|
10240
|
-
return 1 + expr.numArgs;
|
|
10241
|
-
default:
|
|
10242
|
-
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
10923
|
+
else {
|
|
10924
|
+
throw new Error(`AssertionError: unable to call variadic function`);
|
|
10243
10925
|
}
|
|
10244
10926
|
}
|
|
10927
|
+
function callVariadicInstruction(config, baseArgs, interpolationArgs) {
|
|
10928
|
+
return createStatementOp(callVariadicInstructionExpr(config, baseArgs, interpolationArgs).toStmt());
|
|
10929
|
+
}
|
|
10245
10930
|
|
|
10246
10931
|
/**
|
|
10247
|
-
*
|
|
10932
|
+
* Compiles semantic operations across all views and generates output `o.Statement`s with actual
|
|
10933
|
+
* runtime calls in their place.
|
|
10248
10934
|
*
|
|
10249
|
-
*
|
|
10250
|
-
* the
|
|
10935
|
+
* Reification replaces semantic operations with selected Ivy instructions and other generated code
|
|
10936
|
+
* structures. After reification, the create/update operation lists of all views should only contain
|
|
10937
|
+
* `ir.StatementOp`s (which wrap generated `o.Statement`s).
|
|
10251
10938
|
*/
|
|
10252
|
-
function
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
if (view.fnName === null) {
|
|
10257
|
-
view.fnName = `${baseName}_Template`;
|
|
10939
|
+
function phaseReify(cpl) {
|
|
10940
|
+
for (const [_, view] of cpl.views) {
|
|
10941
|
+
reifyCreateOperations(view, view.create);
|
|
10942
|
+
reifyUpdateOperations(view, view.update);
|
|
10258
10943
|
}
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
const
|
|
10262
|
-
|
|
10944
|
+
}
|
|
10945
|
+
function reifyCreateOperations(view, ops) {
|
|
10946
|
+
for (const op of ops) {
|
|
10947
|
+
transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
|
|
10263
10948
|
switch (op.kind) {
|
|
10264
|
-
case OpKind.
|
|
10265
|
-
|
|
10266
|
-
// TODO(alxhub): convert this temporary name to match how the
|
|
10267
|
-
// `TemplateDefinitionBuilder` names listener functions.
|
|
10268
|
-
if (op.slot === null) {
|
|
10269
|
-
throw new Error(`Expected a slot to be assigned`);
|
|
10270
|
-
}
|
|
10271
|
-
op.handlerFnName = `${view.fnName}_${op.tag}_${op.name}_${op.slot}_listener`;
|
|
10272
|
-
}
|
|
10949
|
+
case OpKind.Text:
|
|
10950
|
+
OpList.replace(op, text(op.slot, op.initialValue));
|
|
10273
10951
|
break;
|
|
10274
|
-
case OpKind.
|
|
10275
|
-
|
|
10952
|
+
case OpKind.ElementStart:
|
|
10953
|
+
OpList.replace(op, elementStart(op.slot, op.tag, op.attributes, op.localRefs));
|
|
10954
|
+
break;
|
|
10955
|
+
case OpKind.Element:
|
|
10956
|
+
OpList.replace(op, element(op.slot, op.tag, op.attributes, op.localRefs));
|
|
10957
|
+
break;
|
|
10958
|
+
case OpKind.ElementEnd:
|
|
10959
|
+
OpList.replace(op, elementEnd());
|
|
10960
|
+
break;
|
|
10961
|
+
case OpKind.ContainerStart:
|
|
10962
|
+
OpList.replace(op, elementContainerStart(op.slot, op.attributes, op.localRefs));
|
|
10963
|
+
break;
|
|
10964
|
+
case OpKind.Container:
|
|
10965
|
+
OpList.replace(op, elementContainer(op.slot, op.attributes, op.localRefs));
|
|
10966
|
+
break;
|
|
10967
|
+
case OpKind.ContainerEnd:
|
|
10968
|
+
OpList.replace(op, elementContainerEnd());
|
|
10276
10969
|
break;
|
|
10277
10970
|
case OpKind.Template:
|
|
10278
10971
|
const childView = view.tpl.views.get(op.xref);
|
|
10279
|
-
|
|
10280
|
-
|
|
10972
|
+
OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes));
|
|
10973
|
+
break;
|
|
10974
|
+
case OpKind.Pipe:
|
|
10975
|
+
OpList.replace(op, pipe(op.slot, op.name));
|
|
10976
|
+
break;
|
|
10977
|
+
case OpKind.Listener:
|
|
10978
|
+
const listenerFn = reifyListenerHandler(view, op.handlerFnName, op.handlerOps);
|
|
10979
|
+
OpList.replace(op, listener(op.name, listenerFn));
|
|
10980
|
+
break;
|
|
10981
|
+
case OpKind.Variable:
|
|
10982
|
+
if (op.variable.name === null) {
|
|
10983
|
+
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
10281
10984
|
}
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10985
|
+
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
10986
|
+
break;
|
|
10987
|
+
case OpKind.Statement:
|
|
10988
|
+
// Pass statement operations directly through.
|
|
10285
10989
|
break;
|
|
10990
|
+
default:
|
|
10991
|
+
throw new Error(`AssertionError: Unsupported reification of create op ${OpKind[op.kind]}`);
|
|
10286
10992
|
}
|
|
10287
10993
|
}
|
|
10288
|
-
// Having named all variables declared in the view, now we can push those names into the
|
|
10289
|
-
// `ir.ReadVariableExpr` expressions which represent reads of those variables.
|
|
10290
|
-
for (const op of view.ops()) {
|
|
10291
|
-
visitExpressionsInOp(op, expr => {
|
|
10292
|
-
if (!(expr instanceof ReadVariableExpr) || expr.name !== null) {
|
|
10293
|
-
return;
|
|
10294
|
-
}
|
|
10295
|
-
if (!varNames.has(expr.xref)) {
|
|
10296
|
-
throw new Error(`Variable ${expr.xref} not yet named`);
|
|
10297
|
-
}
|
|
10298
|
-
expr.name = varNames.get(expr.xref);
|
|
10299
|
-
});
|
|
10300
|
-
}
|
|
10301
10994
|
}
|
|
10302
|
-
function
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10995
|
+
function reifyUpdateOperations(_view, ops) {
|
|
10996
|
+
for (const op of ops) {
|
|
10997
|
+
transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
|
|
10998
|
+
switch (op.kind) {
|
|
10999
|
+
case OpKind.Advance:
|
|
11000
|
+
OpList.replace(op, advance(op.delta));
|
|
10307
11001
|
break;
|
|
10308
|
-
|
|
10309
|
-
|
|
11002
|
+
case OpKind.Property:
|
|
11003
|
+
OpList.replace(op, property(op.name, op.expression));
|
|
11004
|
+
break;
|
|
11005
|
+
case OpKind.InterpolateProperty:
|
|
11006
|
+
OpList.replace(op, propertyInterpolate(op.name, op.strings, op.expressions));
|
|
11007
|
+
break;
|
|
11008
|
+
case OpKind.InterpolateText:
|
|
11009
|
+
OpList.replace(op, textInterpolate(op.strings, op.expressions));
|
|
11010
|
+
break;
|
|
11011
|
+
case OpKind.Variable:
|
|
11012
|
+
if (op.variable.name === null) {
|
|
11013
|
+
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
11014
|
+
}
|
|
11015
|
+
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
11016
|
+
break;
|
|
11017
|
+
case OpKind.Statement:
|
|
11018
|
+
// Pass statement operations directly through.
|
|
10310
11019
|
break;
|
|
11020
|
+
default:
|
|
11021
|
+
throw new Error(`AssertionError: Unsupported reification of update op ${OpKind[op.kind]}`);
|
|
10311
11022
|
}
|
|
10312
11023
|
}
|
|
10313
|
-
return variable.name;
|
|
10314
11024
|
}
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
* the `consts` array for the whole component.
|
|
10319
|
-
*/
|
|
10320
|
-
function phaseLocalRefs(cpl) {
|
|
10321
|
-
for (const view of cpl.views.values()) {
|
|
10322
|
-
for (const op of view.create) {
|
|
10323
|
-
switch (op.kind) {
|
|
10324
|
-
case OpKind.ElementStart:
|
|
10325
|
-
case OpKind.Element:
|
|
10326
|
-
case OpKind.Template:
|
|
10327
|
-
if (!Array.isArray(op.localRefs)) {
|
|
10328
|
-
throw new Error(`AssertionError: expected localRefs to be an array still`);
|
|
10329
|
-
}
|
|
10330
|
-
op.numSlotsUsed += op.localRefs.length;
|
|
10331
|
-
if (op.localRefs.length > 0) {
|
|
10332
|
-
const localRefs = serializeLocalRefs(op.localRefs);
|
|
10333
|
-
op.localRefs = cpl.addConst(localRefs);
|
|
10334
|
-
}
|
|
10335
|
-
else {
|
|
10336
|
-
op.localRefs = null;
|
|
10337
|
-
}
|
|
10338
|
-
break;
|
|
10339
|
-
}
|
|
10340
|
-
}
|
|
11025
|
+
function reifyIrExpression(expr) {
|
|
11026
|
+
if (!isIrExpression(expr)) {
|
|
11027
|
+
return expr;
|
|
10341
11028
|
}
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
11029
|
+
switch (expr.kind) {
|
|
11030
|
+
case ExpressionKind.NextContext:
|
|
11031
|
+
return nextContext(expr.steps);
|
|
11032
|
+
case ExpressionKind.Reference:
|
|
11033
|
+
return reference(expr.slot + 1 + expr.offset);
|
|
11034
|
+
case ExpressionKind.LexicalRead:
|
|
11035
|
+
throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
|
|
11036
|
+
case ExpressionKind.RestoreView:
|
|
11037
|
+
if (typeof expr.view === 'number') {
|
|
11038
|
+
throw new Error(`AssertionError: unresolved RestoreView`);
|
|
11039
|
+
}
|
|
11040
|
+
return restoreView(expr.view);
|
|
11041
|
+
case ExpressionKind.ResetView:
|
|
11042
|
+
return resetView(expr.expr);
|
|
11043
|
+
case ExpressionKind.GetCurrentView:
|
|
11044
|
+
return getCurrentView();
|
|
11045
|
+
case ExpressionKind.ReadVariable:
|
|
11046
|
+
if (expr.name === null) {
|
|
11047
|
+
throw new Error(`Read of unnamed variable ${expr.xref}`);
|
|
11048
|
+
}
|
|
11049
|
+
return variable(expr.name);
|
|
11050
|
+
case ExpressionKind.PureFunctionExpr:
|
|
11051
|
+
if (expr.fn === null) {
|
|
11052
|
+
throw new Error(`AssertionError: expected PureFunctions to have been extracted`);
|
|
11053
|
+
}
|
|
11054
|
+
return pureFunction(expr.varOffset, expr.fn, expr.args);
|
|
11055
|
+
case ExpressionKind.PureFunctionParameterExpr:
|
|
11056
|
+
throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
|
|
11057
|
+
case ExpressionKind.PipeBinding:
|
|
11058
|
+
return pipeBind(expr.slot, expr.varOffset, expr.args);
|
|
11059
|
+
case ExpressionKind.PipeBindingVariadic:
|
|
11060
|
+
return pipeBindV(expr.slot, expr.varOffset, expr.args);
|
|
11061
|
+
default:
|
|
11062
|
+
throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
|
|
10347
11063
|
}
|
|
10348
|
-
return literalArr(constRefs);
|
|
10349
11064
|
}
|
|
10350
|
-
|
|
10351
11065
|
/**
|
|
10352
|
-
*
|
|
10353
|
-
*
|
|
10354
|
-
*
|
|
10355
|
-
* Variables generated include:
|
|
10356
|
-
* * a saved view context to be used to restore the current view in event listeners.
|
|
10357
|
-
* * the context of the restored view within event listener handlers.
|
|
10358
|
-
* * context variables from the current view as well as all parent views (including the root
|
|
10359
|
-
* context if needed).
|
|
10360
|
-
* * local references from elements within the current view and any lexical parents.
|
|
10361
|
-
*
|
|
10362
|
-
* Variables are generated here unconditionally, and may optimized away in future operations if it
|
|
10363
|
-
* turns out their values (and any side effects) are unused.
|
|
11066
|
+
* Listeners get turned into a function expression, which may or may not have the `$event`
|
|
11067
|
+
* parameter defined.
|
|
10364
11068
|
*/
|
|
10365
|
-
function
|
|
10366
|
-
|
|
11069
|
+
function reifyListenerHandler(view, name, handlerOps) {
|
|
11070
|
+
const lookForEvent = new LookForEventVisitor();
|
|
11071
|
+
// First, reify all instruction calls within `handlerOps`.
|
|
11072
|
+
reifyUpdateOperations(view, handlerOps);
|
|
11073
|
+
// Next, extract all the `o.Statement`s from the reified operations. We can expect that at this
|
|
11074
|
+
// point, all operations have been converted to statements.
|
|
11075
|
+
const handlerStmts = [];
|
|
11076
|
+
for (const op of handlerOps) {
|
|
11077
|
+
if (op.kind !== OpKind.Statement) {
|
|
11078
|
+
throw new Error(`AssertionError: expected reified statements, but found op ${OpKind[op.kind]}`);
|
|
11079
|
+
}
|
|
11080
|
+
handlerStmts.push(op.statement);
|
|
11081
|
+
}
|
|
11082
|
+
// Scan the statement list for usages of `$event`. If referenced, we need to generate it as a
|
|
11083
|
+
// parameter.
|
|
11084
|
+
lookForEvent.visitAllStatements(handlerStmts, null);
|
|
11085
|
+
const params = [];
|
|
11086
|
+
if (lookForEvent.seenEventRead) {
|
|
11087
|
+
// We need the `$event` parameter.
|
|
11088
|
+
params.push(new FnParam('$event'));
|
|
11089
|
+
}
|
|
11090
|
+
return fn(params, handlerStmts, undefined, undefined, name);
|
|
10367
11091
|
}
|
|
10368
11092
|
/**
|
|
10369
|
-
*
|
|
10370
|
-
* declares.
|
|
10371
|
-
*
|
|
10372
|
-
* @param `parentScope` a scope extracted from the parent view which captures any variables which
|
|
10373
|
-
* should be inherited by this view. `null` if the current view is the root view.
|
|
11093
|
+
* Visitor which scans for reads of the `$event` special variable.
|
|
10374
11094
|
*/
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
if (view.parent !== null) {
|
|
10380
|
-
// Start the view creation block with an operation to save the current view context. This may be
|
|
10381
|
-
// used to restore the view context in any listeners that may be present.
|
|
11095
|
+
class LookForEventVisitor extends RecursiveAstVisitor$1 {
|
|
11096
|
+
constructor() {
|
|
11097
|
+
super(...arguments);
|
|
11098
|
+
this.seenEventRead = false;
|
|
10382
11099
|
}
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
// Descend into child embedded views.
|
|
10387
|
-
recursivelyProcessView(view.tpl.views.get(op.xref), scope);
|
|
10388
|
-
break;
|
|
10389
|
-
case OpKind.Listener:
|
|
10390
|
-
// Prepend variables to listener handler functions.
|
|
10391
|
-
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
|
|
10392
|
-
break;
|
|
11100
|
+
visitReadVarExpr(ast, context) {
|
|
11101
|
+
if (ast.name === '$event') {
|
|
11102
|
+
this.seenEventRead = true;
|
|
10393
11103
|
}
|
|
10394
11104
|
}
|
|
10395
|
-
// Prepend the declarations for all available variables in scope to the `update` block.
|
|
10396
|
-
const preambleOps = generateVariablesInScopeForView(view, scope);
|
|
10397
|
-
view.update.prepend(preambleOps);
|
|
10398
11105
|
}
|
|
11106
|
+
|
|
10399
11107
|
/**
|
|
10400
|
-
*
|
|
10401
|
-
*
|
|
11108
|
+
* Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
|
|
11109
|
+
* either the `ctx` parameter to component functions (for the current view context) or to variables
|
|
11110
|
+
* that store those contexts (for contexts accessed via the `nextContext()` instruction).
|
|
10402
11111
|
*/
|
|
10403
|
-
function
|
|
10404
|
-
const
|
|
10405
|
-
view
|
|
10406
|
-
|
|
10407
|
-
kind: SemanticVariableKind.Context,
|
|
10408
|
-
name: null,
|
|
10409
|
-
view: view.xref,
|
|
10410
|
-
},
|
|
10411
|
-
contextVariables: new Map(),
|
|
10412
|
-
references: [],
|
|
10413
|
-
parent,
|
|
10414
|
-
};
|
|
10415
|
-
for (const identifier of view.contextVariables.keys()) {
|
|
10416
|
-
scope.contextVariables.set(identifier, {
|
|
10417
|
-
kind: SemanticVariableKind.Identifier,
|
|
10418
|
-
name: null,
|
|
10419
|
-
identifier,
|
|
10420
|
-
});
|
|
11112
|
+
function phaseResolveContexts(cpl) {
|
|
11113
|
+
for (const view of cpl.views.values()) {
|
|
11114
|
+
processLexicalScope$1(view, view.create);
|
|
11115
|
+
processLexicalScope$1(view, view.update);
|
|
10421
11116
|
}
|
|
10422
|
-
|
|
11117
|
+
}
|
|
11118
|
+
function processLexicalScope$1(view, ops) {
|
|
11119
|
+
// Track the expressions used to access all available contexts within the current view, by the
|
|
11120
|
+
// view `ir.XrefId`.
|
|
11121
|
+
const scope = new Map();
|
|
11122
|
+
// The current view's context is accessible via the `ctx` parameter.
|
|
11123
|
+
scope.set(view.xref, variable('ctx'));
|
|
11124
|
+
for (const op of ops) {
|
|
10423
11125
|
switch (op.kind) {
|
|
10424
|
-
case OpKind.
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
}
|
|
10430
|
-
// Record available local references from this element.
|
|
10431
|
-
for (let offset = 0; offset < op.localRefs.length; offset++) {
|
|
10432
|
-
scope.references.push({
|
|
10433
|
-
name: op.localRefs[offset].name,
|
|
10434
|
-
targetId: op.xref,
|
|
10435
|
-
offset,
|
|
10436
|
-
variable: {
|
|
10437
|
-
kind: SemanticVariableKind.Identifier,
|
|
10438
|
-
name: null,
|
|
10439
|
-
identifier: op.localRefs[offset].name,
|
|
10440
|
-
},
|
|
10441
|
-
});
|
|
11126
|
+
case OpKind.Variable:
|
|
11127
|
+
switch (op.variable.kind) {
|
|
11128
|
+
case SemanticVariableKind.Context:
|
|
11129
|
+
scope.set(op.variable.view, new ReadVariableExpr(op.xref));
|
|
11130
|
+
break;
|
|
10442
11131
|
}
|
|
10443
11132
|
break;
|
|
11133
|
+
case OpKind.Listener:
|
|
11134
|
+
processLexicalScope$1(view, op.handlerOps);
|
|
11135
|
+
break;
|
|
10444
11136
|
}
|
|
10445
11137
|
}
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
// view with a `nextContext` expression. This context switching operation itself declares a
|
|
10459
|
-
// variable, because the context of the view may be referenced directly.
|
|
10460
|
-
newOps.push(createVariableOp(view.tpl.allocateXrefId(), scope.viewContextVariable, new NextContextExpr()));
|
|
10461
|
-
}
|
|
10462
|
-
// Add variables for all context variables available in this scope's view.
|
|
10463
|
-
for (const [name, value] of view.tpl.views.get(scope.view).contextVariables) {
|
|
10464
|
-
newOps.push(createVariableOp(view.tpl.allocateXrefId(), scope.contextVariables.get(name), new ReadPropExpr(new ContextExpr(scope.view), value)));
|
|
10465
|
-
}
|
|
10466
|
-
// Add variables for all local references declared for elements in this scope.
|
|
10467
|
-
for (const ref of scope.references) {
|
|
10468
|
-
newOps.push(createVariableOp(view.tpl.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
10469
|
-
}
|
|
10470
|
-
if (scope.parent !== null) {
|
|
10471
|
-
// Recursively add variables from the parent scope.
|
|
10472
|
-
newOps.push(...generateVariablesInScopeForView(view, scope.parent));
|
|
11138
|
+
for (const op of ops) {
|
|
11139
|
+
transformExpressionsInOp(op, expr => {
|
|
11140
|
+
if (expr instanceof ContextExpr) {
|
|
11141
|
+
if (!scope.has(expr.view)) {
|
|
11142
|
+
throw new Error(`No context found for reference to view ${expr.view} from view ${view.xref}`);
|
|
11143
|
+
}
|
|
11144
|
+
return scope.get(expr.view);
|
|
11145
|
+
}
|
|
11146
|
+
else {
|
|
11147
|
+
return expr;
|
|
11148
|
+
}
|
|
11149
|
+
}, VisitorContextFlag.None);
|
|
10473
11150
|
}
|
|
10474
|
-
return newOps;
|
|
10475
11151
|
}
|
|
10476
11152
|
|
|
10477
11153
|
/**
|
|
@@ -10483,11 +11159,11 @@ function generateVariablesInScopeForView(view, scope) {
|
|
|
10483
11159
|
*/
|
|
10484
11160
|
function phaseResolveNames(cpl) {
|
|
10485
11161
|
for (const [_, view] of cpl.views) {
|
|
10486
|
-
processLexicalScope
|
|
10487
|
-
processLexicalScope
|
|
11162
|
+
processLexicalScope(view, view.create, null);
|
|
11163
|
+
processLexicalScope(view, view.update, null);
|
|
10488
11164
|
}
|
|
10489
11165
|
}
|
|
10490
|
-
function processLexicalScope
|
|
11166
|
+
function processLexicalScope(view, ops, savedView) {
|
|
10491
11167
|
// Maps names defined in the lexical scope of this template to the `ir.XrefId`s of the variable
|
|
10492
11168
|
// declarations which represent those values.
|
|
10493
11169
|
//
|
|
@@ -10521,7 +11197,7 @@ function processLexicalScope$1(view, ops, savedView) {
|
|
|
10521
11197
|
case OpKind.Listener:
|
|
10522
11198
|
// Listener functions have separate variable declarations, so process them as a separate
|
|
10523
11199
|
// lexical scope.
|
|
10524
|
-
processLexicalScope
|
|
11200
|
+
processLexicalScope(view, op.handlerOps, savedView);
|
|
10525
11201
|
break;
|
|
10526
11202
|
}
|
|
10527
11203
|
}
|
|
@@ -10560,49 +11236,117 @@ function processLexicalScope$1(view, ops, savedView) {
|
|
|
10560
11236
|
}
|
|
10561
11237
|
}
|
|
10562
11238
|
|
|
10563
|
-
|
|
10564
|
-
* Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
|
|
10565
|
-
* either the `ctx` parameter to component functions (for the current view context) or to variables
|
|
10566
|
-
* that store those contexts (for contexts accessed via the `nextContext()` instruction).
|
|
10567
|
-
*/
|
|
10568
|
-
function phaseResolveContexts(cpl) {
|
|
11239
|
+
function phaseSaveRestoreView(cpl) {
|
|
10569
11240
|
for (const view of cpl.views.values()) {
|
|
10570
|
-
|
|
10571
|
-
|
|
11241
|
+
if (view === cpl.root) {
|
|
11242
|
+
// Save/restore operations are not necessary for the root view.
|
|
11243
|
+
continue;
|
|
11244
|
+
}
|
|
11245
|
+
view.create.prepend([
|
|
11246
|
+
createVariableOp(view.tpl.allocateXrefId(), {
|
|
11247
|
+
kind: SemanticVariableKind.SavedView,
|
|
11248
|
+
name: null,
|
|
11249
|
+
view: view.xref,
|
|
11250
|
+
}, new GetCurrentViewExpr()),
|
|
11251
|
+
]);
|
|
11252
|
+
for (const op of view.create) {
|
|
11253
|
+
if (op.kind !== OpKind.Listener) {
|
|
11254
|
+
continue;
|
|
11255
|
+
}
|
|
11256
|
+
op.handlerOps.prepend([
|
|
11257
|
+
createVariableOp(view.tpl.allocateXrefId(), {
|
|
11258
|
+
kind: SemanticVariableKind.Context,
|
|
11259
|
+
name: null,
|
|
11260
|
+
view: view.xref,
|
|
11261
|
+
}, new RestoreViewExpr(view.xref)),
|
|
11262
|
+
]);
|
|
11263
|
+
// The "restore view" operation in listeners requires a call to `resetView` to reset the
|
|
11264
|
+
// context prior to returning from the listener operation. Find any `return` statements in
|
|
11265
|
+
// the listener body and wrap them in a call to reset the view.
|
|
11266
|
+
for (const handlerOp of op.handlerOps) {
|
|
11267
|
+
if (handlerOp.kind === OpKind.Statement &&
|
|
11268
|
+
handlerOp.statement instanceof ReturnStatement) {
|
|
11269
|
+
handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value);
|
|
11270
|
+
}
|
|
11271
|
+
}
|
|
11272
|
+
}
|
|
10572
11273
|
}
|
|
10573
11274
|
}
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
11275
|
+
|
|
11276
|
+
/**
|
|
11277
|
+
* Assign data slots for all operations which implement `ConsumesSlotOpTrait`, and propagate the
|
|
11278
|
+
* assigned data slots of those operations to any expressions which reference them via
|
|
11279
|
+
* `UsesSlotIndexTrait`.
|
|
11280
|
+
*
|
|
11281
|
+
* This phase is also responsible for counting the number of slots used for each view (its `decls`)
|
|
11282
|
+
* and propagating that number into the `Template` operations which declare embedded views.
|
|
11283
|
+
*/
|
|
11284
|
+
function phaseSlotAllocation(cpl) {
|
|
11285
|
+
// Map of all declarations in all views within the component which require an assigned slot index.
|
|
11286
|
+
// This map needs to be global (across all views within the component) since it's possible to
|
|
11287
|
+
// reference a slot from one view from an expression within another (e.g. local references work
|
|
11288
|
+
// this way).
|
|
11289
|
+
const slotMap = new Map();
|
|
11290
|
+
// Process all views in the component and assign slot indexes.
|
|
11291
|
+
for (const [_, view] of cpl.views) {
|
|
11292
|
+
// Slot indices start at 0 for each view (and are not unique between views).
|
|
11293
|
+
let slotCount = 0;
|
|
11294
|
+
for (const op of view.create) {
|
|
11295
|
+
// Only consider declarations which consume data slots.
|
|
11296
|
+
if (!hasConsumesSlotTrait(op)) {
|
|
11297
|
+
continue;
|
|
11298
|
+
}
|
|
11299
|
+
// Assign slots to this declaration starting at the current `slotCount`.
|
|
11300
|
+
op.slot = slotCount;
|
|
11301
|
+
// And track its assigned slot in the `slotMap`.
|
|
11302
|
+
slotMap.set(op.xref, op.slot);
|
|
11303
|
+
// Each declaration may use more than 1 slot, so increment `slotCount` to reserve the number
|
|
11304
|
+
// of slots required.
|
|
11305
|
+
slotCount += op.numSlotsUsed;
|
|
10592
11306
|
}
|
|
11307
|
+
// Record the total number of slots used on the view itself. This will later be propagated into
|
|
11308
|
+
// `ir.TemplateOp`s which declare those views (except for the root view).
|
|
11309
|
+
view.decls = slotCount;
|
|
10593
11310
|
}
|
|
10594
|
-
for
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
11311
|
+
// After slot assignment, `slotMap` now contains slot assignments for every declaration in the
|
|
11312
|
+
// whole template, across all views. Next, look for expressions which implement
|
|
11313
|
+
// `UsesSlotIndexExprTrait` and propagate the assigned slot indexes into them.
|
|
11314
|
+
// Additionally, this second scan allows us to find `ir.TemplateOp`s which declare views and
|
|
11315
|
+
// propagate the number of slots used for each view into the operation which declares it.
|
|
11316
|
+
for (const [_, view] of cpl.views) {
|
|
11317
|
+
for (const op of view.ops()) {
|
|
11318
|
+
if (op.kind === OpKind.Template) {
|
|
11319
|
+
// Record the number of slots used by the view this `ir.TemplateOp` declares in the
|
|
11320
|
+
// operation itself, so it can be emitted later.
|
|
11321
|
+
const childView = cpl.views.get(op.xref);
|
|
11322
|
+
op.decls = childView.decls;
|
|
10601
11323
|
}
|
|
10602
|
-
|
|
10603
|
-
|
|
11324
|
+
if (hasUsesSlotIndexTrait(op) && op.slot === null) {
|
|
11325
|
+
if (!slotMap.has(op.target)) {
|
|
11326
|
+
// We do expect to find a slot allocated for everything which might be referenced.
|
|
11327
|
+
throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
|
|
11328
|
+
}
|
|
11329
|
+
op.slot = slotMap.get(op.target);
|
|
10604
11330
|
}
|
|
10605
|
-
|
|
11331
|
+
// Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
|
|
11332
|
+
visitExpressionsInOp(op, expr => {
|
|
11333
|
+
if (!isIrExpression(expr)) {
|
|
11334
|
+
return;
|
|
11335
|
+
}
|
|
11336
|
+
if (!hasUsesSlotIndexTrait(expr) || expr.slot !== null) {
|
|
11337
|
+
return;
|
|
11338
|
+
}
|
|
11339
|
+
// The `UsesSlotIndexExprTrait` indicates that this expression references something declared
|
|
11340
|
+
// in this component template by its slot index. Use the `target` `ir.XrefId` to find the
|
|
11341
|
+
// allocated slot for that declaration in `slotMap`.
|
|
11342
|
+
if (!slotMap.has(expr.target)) {
|
|
11343
|
+
// We do expect to find a slot allocated for everything which might be referenced.
|
|
11344
|
+
throw new Error(`AssertionError: no slot allocated for ${expr.constructor.name} target ${expr.target}`);
|
|
11345
|
+
}
|
|
11346
|
+
// Record the allocated slot on the expression.
|
|
11347
|
+
expr.slot = slotMap.get(expr.target);
|
|
11348
|
+
});
|
|
11349
|
+
}
|
|
10606
11350
|
}
|
|
10607
11351
|
}
|
|
10608
11352
|
|
|
@@ -10927,429 +11671,123 @@ function tryInlineVariableInitializer(id, initializer, target, declFences) {
|
|
|
10927
11671
|
}
|
|
10928
11672
|
else if ((flags & VisitorContextFlag.InChildOperation) && (declFences & Fence.ViewContextRead)) {
|
|
10929
11673
|
// We cannot inline variables that are sensitive to the current context across operation
|
|
10930
|
-
// boundaries.
|
|
10931
|
-
return expr;
|
|
10932
|
-
}
|
|
10933
|
-
switch (expr.kind) {
|
|
10934
|
-
case ExpressionKind.ReadVariable:
|
|
10935
|
-
if (expr.xref === id) {
|
|
10936
|
-
// This is the usage site of the variable. Since nothing has disallowed inlining, it's
|
|
10937
|
-
// safe to inline the initializer here.
|
|
10938
|
-
inlined = true;
|
|
10939
|
-
return initializer;
|
|
10940
|
-
}
|
|
10941
|
-
break;
|
|
10942
|
-
default:
|
|
10943
|
-
// For other types of `ir.Expression`s, whether inlining is allowed depends on their fences.
|
|
10944
|
-
const exprFences = fencesForIrExpression(expr);
|
|
10945
|
-
inliningAllowed = inliningAllowed && safeToInlinePastFences(exprFences, declFences);
|
|
10946
|
-
break;
|
|
10947
|
-
}
|
|
10948
|
-
return expr;
|
|
10949
|
-
}, VisitorContextFlag.None);
|
|
10950
|
-
return inlined;
|
|
10951
|
-
}
|
|
10952
|
-
/**
|
|
10953
|
-
* Determines whether inlining of `decl` should be allowed in "conservative" mode.
|
|
10954
|
-
*
|
|
10955
|
-
* In conservative mode, inlining behavior is limited to those operations which the
|
|
10956
|
-
* `TemplateDefinitionBuilder` supported, with the goal of producing equivalent output.
|
|
10957
|
-
*/
|
|
10958
|
-
function allowConservativeInlining(decl, target) {
|
|
10959
|
-
// TODO(alxhub): understand exactly how TemplateDefinitionBuilder approaches inlining, and record
|
|
10960
|
-
// that behavior here.
|
|
10961
|
-
switch (decl.variable.kind) {
|
|
10962
|
-
case SemanticVariableKind.Identifier:
|
|
10963
|
-
return false;
|
|
10964
|
-
case SemanticVariableKind.Context:
|
|
10965
|
-
// Context can only be inlined into other variables.
|
|
10966
|
-
return target.kind === OpKind.Variable;
|
|
10967
|
-
default:
|
|
10968
|
-
return true;
|
|
10969
|
-
}
|
|
10970
|
-
}
|
|
10971
|
-
|
|
10972
|
-
const CHAINABLE = new Set([
|
|
10973
|
-
Identifiers.elementStart,
|
|
10974
|
-
Identifiers.elementEnd,
|
|
10975
|
-
Identifiers.property,
|
|
10976
|
-
Identifiers.elementContainerStart,
|
|
10977
|
-
Identifiers.elementContainerEnd,
|
|
10978
|
-
Identifiers.elementContainer,
|
|
10979
|
-
]);
|
|
10980
|
-
/**
|
|
10981
|
-
* Post-process a reified view compilation and convert sequential calls to chainable instructions
|
|
10982
|
-
* into chain calls.
|
|
10983
|
-
*
|
|
10984
|
-
* For example, two `elementStart` operations in sequence:
|
|
10985
|
-
*
|
|
10986
|
-
* ```typescript
|
|
10987
|
-
* elementStart(0, 'div');
|
|
10988
|
-
* elementStart(1, 'span');
|
|
10989
|
-
* ```
|
|
10990
|
-
*
|
|
10991
|
-
* Can be called as a chain instead:
|
|
10992
|
-
*
|
|
10993
|
-
* ```typescript
|
|
10994
|
-
* elementStart(0, 'div')(1, 'span');
|
|
10995
|
-
* ```
|
|
10996
|
-
*/
|
|
10997
|
-
function phaseChaining(cpl) {
|
|
10998
|
-
for (const [_, view] of cpl.views) {
|
|
10999
|
-
chainOperationsInList(view.create);
|
|
11000
|
-
chainOperationsInList(view.update);
|
|
11001
|
-
}
|
|
11002
|
-
}
|
|
11003
|
-
function chainOperationsInList(opList) {
|
|
11004
|
-
let chain = null;
|
|
11005
|
-
for (const op of opList) {
|
|
11006
|
-
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement)) {
|
|
11007
|
-
// This type of statement isn't chainable.
|
|
11008
|
-
chain = null;
|
|
11009
|
-
continue;
|
|
11010
|
-
}
|
|
11011
|
-
if (!(op.statement.expr instanceof InvokeFunctionExpr) ||
|
|
11012
|
-
!(op.statement.expr.fn instanceof ExternalExpr)) {
|
|
11013
|
-
// This is a statement, but not an instruction-type call, so not chainable.
|
|
11014
|
-
chain = null;
|
|
11015
|
-
continue;
|
|
11016
|
-
}
|
|
11017
|
-
const instruction = op.statement.expr.fn.value;
|
|
11018
|
-
if (!CHAINABLE.has(instruction)) {
|
|
11019
|
-
// This instruction isn't chainable.
|
|
11020
|
-
chain = null;
|
|
11021
|
-
continue;
|
|
11022
|
-
}
|
|
11023
|
-
// This instruction can be chained. It can either be added on to the previous chain (if
|
|
11024
|
-
// compatible) or it can be the start of a new chain.
|
|
11025
|
-
if (chain !== null && chain.instruction === instruction) {
|
|
11026
|
-
// This instruction can be added onto the previous chain.
|
|
11027
|
-
const expression = chain.expression.callFn(op.statement.expr.args, op.statement.expr.sourceSpan, op.statement.expr.pure);
|
|
11028
|
-
chain.expression = expression;
|
|
11029
|
-
chain.op.statement = expression.toStmt();
|
|
11030
|
-
OpList.remove(op);
|
|
11031
|
-
}
|
|
11032
|
-
else {
|
|
11033
|
-
// Leave this instruction alone for now, but consider it the start of a new chain.
|
|
11034
|
-
chain = {
|
|
11035
|
-
op,
|
|
11036
|
-
instruction,
|
|
11037
|
-
expression: op.statement.expr,
|
|
11038
|
-
};
|
|
11039
|
-
}
|
|
11040
|
-
}
|
|
11041
|
-
}
|
|
11042
|
-
|
|
11043
|
-
/**
|
|
11044
|
-
* Merges logically sequential `NextContextExpr` operations.
|
|
11045
|
-
*
|
|
11046
|
-
* `NextContextExpr` can be referenced repeatedly, "popping" the runtime's context stack each time.
|
|
11047
|
-
* When two such expressions appear back-to-back, it's possible to merge them together into a single
|
|
11048
|
-
* `NextContextExpr` that steps multiple contexts. This merging is possible if all conditions are
|
|
11049
|
-
* met:
|
|
11050
|
-
*
|
|
11051
|
-
* * The result of the `NextContextExpr` that's folded into the subsequent one is not stored (that
|
|
11052
|
-
* is, the call is purely side-effectful).
|
|
11053
|
-
* * No operations in between them uses the implicit context.
|
|
11054
|
-
*/
|
|
11055
|
-
function phaseMergeNextContext(cpl) {
|
|
11056
|
-
for (const view of cpl.views.values()) {
|
|
11057
|
-
for (const op of view.create) {
|
|
11058
|
-
if (op.kind === OpKind.Listener) {
|
|
11059
|
-
mergeNextContextsInOps(op.handlerOps);
|
|
11060
|
-
}
|
|
11061
|
-
}
|
|
11062
|
-
mergeNextContextsInOps(view.update);
|
|
11063
|
-
}
|
|
11064
|
-
}
|
|
11065
|
-
function mergeNextContextsInOps(ops) {
|
|
11066
|
-
for (const op of ops) {
|
|
11067
|
-
// Look for a candidate operation to maybe merge.
|
|
11068
|
-
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement) ||
|
|
11069
|
-
!(op.statement.expr instanceof NextContextExpr)) {
|
|
11070
|
-
continue;
|
|
11071
|
-
}
|
|
11072
|
-
const mergeSteps = op.statement.expr.steps;
|
|
11073
|
-
// Try to merge this `ir.NextContextExpr`.
|
|
11074
|
-
let tryToMerge = true;
|
|
11075
|
-
for (let candidate = op.next; candidate.kind !== OpKind.ListEnd && tryToMerge; candidate = candidate.next) {
|
|
11076
|
-
visitExpressionsInOp(candidate, (expr, flags) => {
|
|
11077
|
-
if (!isIrExpression(expr)) {
|
|
11078
|
-
return expr;
|
|
11079
|
-
}
|
|
11080
|
-
if (!tryToMerge) {
|
|
11081
|
-
// Either we've already merged, or failed to merge.
|
|
11082
|
-
return;
|
|
11083
|
-
}
|
|
11084
|
-
if (flags & VisitorContextFlag.InChildOperation) {
|
|
11085
|
-
// We cannot merge into child operations.
|
|
11086
|
-
return;
|
|
11087
|
-
}
|
|
11088
|
-
switch (expr.kind) {
|
|
11089
|
-
case ExpressionKind.NextContext:
|
|
11090
|
-
// Merge the previous `ir.NextContextExpr` into this one.
|
|
11091
|
-
expr.steps += mergeSteps;
|
|
11092
|
-
OpList.remove(op);
|
|
11093
|
-
tryToMerge = false;
|
|
11094
|
-
break;
|
|
11095
|
-
case ExpressionKind.GetCurrentView:
|
|
11096
|
-
case ExpressionKind.Reference:
|
|
11097
|
-
// Can't merge past a dependency on the context.
|
|
11098
|
-
tryToMerge = false;
|
|
11099
|
-
break;
|
|
11100
|
-
}
|
|
11101
|
-
});
|
|
11102
|
-
}
|
|
11103
|
-
}
|
|
11104
|
-
}
|
|
11105
|
-
|
|
11106
|
-
const CONTAINER_TAG = 'ng-container';
|
|
11107
|
-
/**
|
|
11108
|
-
* Replace an `Element` or `ElementStart` whose tag is `ng-container` with a specific op.
|
|
11109
|
-
*/
|
|
11110
|
-
function phaseNgContainer(cpl) {
|
|
11111
|
-
for (const [_, view] of cpl.views) {
|
|
11112
|
-
const updatedElementXrefs = new Set();
|
|
11113
|
-
for (const op of view.create) {
|
|
11114
|
-
if (op.kind === OpKind.ElementStart && op.tag === CONTAINER_TAG) {
|
|
11115
|
-
// Transmute the `ElementStart` instruction to `ContainerStart`.
|
|
11116
|
-
op.kind = OpKind.ContainerStart;
|
|
11117
|
-
updatedElementXrefs.add(op.xref);
|
|
11118
|
-
}
|
|
11119
|
-
if (op.kind === OpKind.ElementEnd && updatedElementXrefs.has(op.xref)) {
|
|
11120
|
-
// This `ElementEnd` is associated with an `ElementStart` we already transmuted.
|
|
11121
|
-
op.kind = OpKind.ContainerEnd;
|
|
11122
|
-
}
|
|
11123
|
-
}
|
|
11124
|
-
}
|
|
11125
|
-
}
|
|
11126
|
-
|
|
11127
|
-
function phaseSaveRestoreView(cpl) {
|
|
11128
|
-
for (const view of cpl.views.values()) {
|
|
11129
|
-
if (view === cpl.root) {
|
|
11130
|
-
// Save/restore operations are not necessary for the root view.
|
|
11131
|
-
continue;
|
|
11132
|
-
}
|
|
11133
|
-
view.create.prepend([
|
|
11134
|
-
createVariableOp(view.tpl.allocateXrefId(), {
|
|
11135
|
-
kind: SemanticVariableKind.SavedView,
|
|
11136
|
-
name: null,
|
|
11137
|
-
view: view.xref,
|
|
11138
|
-
}, new GetCurrentViewExpr()),
|
|
11139
|
-
]);
|
|
11140
|
-
for (const op of view.create) {
|
|
11141
|
-
if (op.kind !== OpKind.Listener) {
|
|
11142
|
-
continue;
|
|
11143
|
-
}
|
|
11144
|
-
op.handlerOps.prepend([
|
|
11145
|
-
createVariableOp(view.tpl.allocateXrefId(), {
|
|
11146
|
-
kind: SemanticVariableKind.Context,
|
|
11147
|
-
name: null,
|
|
11148
|
-
view: view.xref,
|
|
11149
|
-
}, new RestoreViewExpr(view.xref)),
|
|
11150
|
-
]);
|
|
11151
|
-
// The "restore view" operation in listeners requires a call to `resetView` to reset the
|
|
11152
|
-
// context prior to returning from the listener operation. Find any `return` statements in
|
|
11153
|
-
// the listener body and wrap them in a call to reset the view.
|
|
11154
|
-
for (const handlerOp of op.handlerOps) {
|
|
11155
|
-
if (handlerOp.kind === OpKind.Statement &&
|
|
11156
|
-
handlerOp.statement instanceof ReturnStatement) {
|
|
11157
|
-
handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value);
|
|
11674
|
+
// boundaries.
|
|
11675
|
+
return expr;
|
|
11676
|
+
}
|
|
11677
|
+
switch (expr.kind) {
|
|
11678
|
+
case ExpressionKind.ReadVariable:
|
|
11679
|
+
if (expr.xref === id) {
|
|
11680
|
+
// This is the usage site of the variable. Since nothing has disallowed inlining, it's
|
|
11681
|
+
// safe to inline the initializer here.
|
|
11682
|
+
inlined = true;
|
|
11683
|
+
return initializer;
|
|
11158
11684
|
}
|
|
11159
|
-
|
|
11685
|
+
break;
|
|
11686
|
+
default:
|
|
11687
|
+
// For other types of `ir.Expression`s, whether inlining is allowed depends on their fences.
|
|
11688
|
+
const exprFences = fencesForIrExpression(expr);
|
|
11689
|
+
inliningAllowed = inliningAllowed && safeToInlinePastFences(exprFences, declFences);
|
|
11690
|
+
break;
|
|
11160
11691
|
}
|
|
11692
|
+
return expr;
|
|
11693
|
+
}, VisitorContextFlag.None);
|
|
11694
|
+
return inlined;
|
|
11695
|
+
}
|
|
11696
|
+
/**
|
|
11697
|
+
* Determines whether inlining of `decl` should be allowed in "conservative" mode.
|
|
11698
|
+
*
|
|
11699
|
+
* In conservative mode, inlining behavior is limited to those operations which the
|
|
11700
|
+
* `TemplateDefinitionBuilder` supported, with the goal of producing equivalent output.
|
|
11701
|
+
*/
|
|
11702
|
+
function allowConservativeInlining(decl, target) {
|
|
11703
|
+
// TODO(alxhub): understand exactly how TemplateDefinitionBuilder approaches inlining, and record
|
|
11704
|
+
// that behavior here.
|
|
11705
|
+
switch (decl.variable.kind) {
|
|
11706
|
+
case SemanticVariableKind.Identifier:
|
|
11707
|
+
return false;
|
|
11708
|
+
case SemanticVariableKind.Context:
|
|
11709
|
+
// Context can only be inlined into other variables.
|
|
11710
|
+
return target.kind === OpKind.Variable;
|
|
11711
|
+
default:
|
|
11712
|
+
return true;
|
|
11161
11713
|
}
|
|
11162
11714
|
}
|
|
11163
11715
|
|
|
11164
|
-
|
|
11165
|
-
|
|
11716
|
+
/**
|
|
11717
|
+
* Finds all unresolved safe read expressions, and converts them into the appropriate output AST
|
|
11718
|
+
* reads, guarded by null checks.
|
|
11719
|
+
*/
|
|
11720
|
+
function phaseExpandSafeReads(cpl) {
|
|
11721
|
+
for (const [_, view] of cpl.views) {
|
|
11166
11722
|
for (const op of view.ops()) {
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
return;
|
|
11170
|
-
}
|
|
11171
|
-
const constantDef = new PureFunctionConstant(expr.args.length);
|
|
11172
|
-
expr.fn = cpl.pool.getSharedConstant(constantDef, expr.body);
|
|
11173
|
-
expr.body = null;
|
|
11174
|
-
});
|
|
11723
|
+
transformExpressionsInOp(op, safeTransform, VisitorContextFlag.None);
|
|
11724
|
+
transformExpressionsInOp(op, ternaryTransform, VisitorContextFlag.None);
|
|
11175
11725
|
}
|
|
11176
11726
|
}
|
|
11177
11727
|
}
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
super();
|
|
11181
|
-
this.numArgs = numArgs;
|
|
11182
|
-
}
|
|
11183
|
-
keyOf(expr) {
|
|
11184
|
-
if (expr instanceof PureFunctionParameterExpr) {
|
|
11185
|
-
return `param(${expr.index})`;
|
|
11186
|
-
}
|
|
11187
|
-
else {
|
|
11188
|
-
return super.keyOf(expr);
|
|
11189
|
-
}
|
|
11190
|
-
}
|
|
11191
|
-
toSharedConstantDeclaration(declName, keyExpr) {
|
|
11192
|
-
const fnParams = [];
|
|
11193
|
-
for (let idx = 0; idx < this.numArgs; idx++) {
|
|
11194
|
-
fnParams.push(new FnParam('_p' + idx));
|
|
11195
|
-
}
|
|
11196
|
-
// We will never visit `ir.PureFunctionParameterExpr`s that don't belong to us, because this
|
|
11197
|
-
// transform runs inside another visitor which will visit nested pure functions before this one.
|
|
11198
|
-
const returnExpr = transformExpressionsInExpression(keyExpr, expr => {
|
|
11199
|
-
if (!(expr instanceof PureFunctionParameterExpr)) {
|
|
11200
|
-
return expr;
|
|
11201
|
-
}
|
|
11202
|
-
return variable('_p' + expr.index);
|
|
11203
|
-
}, VisitorContextFlag.None);
|
|
11204
|
-
return new DeclareFunctionStmt(declName, fnParams, [new ReturnStatement(returnExpr)]);
|
|
11205
|
-
}
|
|
11728
|
+
function isSafeAccessExpression(e) {
|
|
11729
|
+
return e instanceof SafePropertyReadExpr || e instanceof SafeKeyedReadExpr;
|
|
11206
11730
|
}
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
for (const view of cpl.views.values()) {
|
|
11210
|
-
processPipeBindingsInView(view);
|
|
11211
|
-
}
|
|
11731
|
+
function isUnsafeAccessExpression(e) {
|
|
11732
|
+
return e instanceof ReadPropExpr || e instanceof ReadKeyExpr;
|
|
11212
11733
|
}
|
|
11213
|
-
function
|
|
11214
|
-
|
|
11215
|
-
visitExpressionsInOp(updateOp, (expr, flags) => {
|
|
11216
|
-
if (!isIrExpression(expr)) {
|
|
11217
|
-
return;
|
|
11218
|
-
}
|
|
11219
|
-
if (expr.kind !== ExpressionKind.PipeBinding) {
|
|
11220
|
-
return;
|
|
11221
|
-
}
|
|
11222
|
-
if (flags & VisitorContextFlag.InChildOperation) {
|
|
11223
|
-
throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
|
|
11224
|
-
}
|
|
11225
|
-
if (!hasDependsOnSlotContextTrait(updateOp)) {
|
|
11226
|
-
throw new Error(`AssertionError: pipe binding associated with non-slot operation ${OpKind[updateOp.kind]}`);
|
|
11227
|
-
}
|
|
11228
|
-
addPipeToCreationBlock(view, updateOp.target, expr);
|
|
11229
|
-
});
|
|
11230
|
-
}
|
|
11734
|
+
function isAccessExpression(e) {
|
|
11735
|
+
return isSafeAccessExpression(e) || isUnsafeAccessExpression(e);
|
|
11231
11736
|
}
|
|
11232
|
-
function
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
if (!hasConsumesSlotTrait(op)) {
|
|
11238
|
-
continue;
|
|
11239
|
-
}
|
|
11240
|
-
if (op.xref !== afterTargetXref) {
|
|
11241
|
-
continue;
|
|
11737
|
+
function deepestSafeTernary(e) {
|
|
11738
|
+
if (isAccessExpression(e) && e.receiver instanceof SafeTernaryExpr) {
|
|
11739
|
+
let st = e.receiver;
|
|
11740
|
+
while (st.expr instanceof SafeTernaryExpr) {
|
|
11741
|
+
st = st.expr;
|
|
11242
11742
|
}
|
|
11243
|
-
|
|
11244
|
-
// operations present.
|
|
11245
|
-
while (op.next.kind === OpKind.Pipe) {
|
|
11246
|
-
op = op.next;
|
|
11247
|
-
}
|
|
11248
|
-
const pipe = createPipeOp(binding.target, binding.name);
|
|
11249
|
-
OpList.insertBefore(pipe, op.next);
|
|
11250
|
-
// This completes adding the pipe to the creation block.
|
|
11251
|
-
return;
|
|
11743
|
+
return st;
|
|
11252
11744
|
}
|
|
11253
|
-
|
|
11254
|
-
throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`);
|
|
11745
|
+
return null;
|
|
11255
11746
|
}
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
return expr;
|
|
11263
|
-
}
|
|
11264
|
-
// Pipes are variadic if they have more than 4 arguments.
|
|
11265
|
-
if (expr.args.length <= 4) {
|
|
11266
|
-
return expr;
|
|
11267
|
-
}
|
|
11268
|
-
return new PipeBindingVariadicExpr(expr.target, expr.name, literalArr(expr.args), expr.args.length);
|
|
11269
|
-
}, VisitorContextFlag.None);
|
|
11270
|
-
}
|
|
11747
|
+
// TODO: When strict compatibility with TemplateDefinitionBuilder is not required, we can use `&&`
|
|
11748
|
+
// instead.
|
|
11749
|
+
function safeTransform(e) {
|
|
11750
|
+
if (e instanceof SafeInvokeFunctionExpr) {
|
|
11751
|
+
// TODO: Implement safe function calls in a subsequent commit.
|
|
11752
|
+
return new InvokeFunctionExpr(e.receiver, e.args);
|
|
11271
11753
|
}
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
function phasePureLiteralStructures(cpl) {
|
|
11275
|
-
for (const view of cpl.views.values()) {
|
|
11276
|
-
for (const op of view.update) {
|
|
11277
|
-
transformExpressionsInOp(op, (expr, flags) => {
|
|
11278
|
-
if (flags & VisitorContextFlag.InChildOperation) {
|
|
11279
|
-
return expr;
|
|
11280
|
-
}
|
|
11281
|
-
if (expr instanceof LiteralArrayExpr) {
|
|
11282
|
-
return transformLiteralArray(expr);
|
|
11283
|
-
}
|
|
11284
|
-
else if (expr instanceof LiteralMapExpr) {
|
|
11285
|
-
return transformLiteralMap(expr);
|
|
11286
|
-
}
|
|
11287
|
-
return expr;
|
|
11288
|
-
}, VisitorContextFlag.None);
|
|
11289
|
-
}
|
|
11754
|
+
if (!isAccessExpression(e)) {
|
|
11755
|
+
return e;
|
|
11290
11756
|
}
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
if (entry.isConstant()) {
|
|
11297
|
-
derivedEntries.push(entry);
|
|
11757
|
+
const dst = deepestSafeTernary(e);
|
|
11758
|
+
if (dst) {
|
|
11759
|
+
if (e instanceof ReadPropExpr) {
|
|
11760
|
+
dst.expr = dst.expr.prop(e.name);
|
|
11761
|
+
return e.receiver;
|
|
11298
11762
|
}
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11763
|
+
if (e instanceof ReadKeyExpr) {
|
|
11764
|
+
dst.expr = dst.expr.key(e.index);
|
|
11765
|
+
return e.receiver;
|
|
11766
|
+
}
|
|
11767
|
+
if (e instanceof SafePropertyReadExpr) {
|
|
11768
|
+
dst.expr = new SafeTernaryExpr(dst.expr.clone(), dst.expr.prop(e.name));
|
|
11769
|
+
return e.receiver;
|
|
11770
|
+
}
|
|
11771
|
+
if (e instanceof SafeKeyedReadExpr) {
|
|
11772
|
+
dst.expr = new SafeTernaryExpr(dst.expr.clone(), dst.expr.key(e.index));
|
|
11773
|
+
return e.receiver;
|
|
11303
11774
|
}
|
|
11304
11775
|
}
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
let derivedEntries = [];
|
|
11309
|
-
const nonConstantArgs = [];
|
|
11310
|
-
for (const entry of expr.entries) {
|
|
11311
|
-
if (entry.value.isConstant()) {
|
|
11312
|
-
derivedEntries.push(entry);
|
|
11776
|
+
else {
|
|
11777
|
+
if (e instanceof SafePropertyReadExpr) {
|
|
11778
|
+
return new SafeTernaryExpr(e.receiver.clone(), e.receiver.prop(e.name));
|
|
11313
11779
|
}
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
nonConstantArgs.push(entry.value);
|
|
11317
|
-
derivedEntries.push(new LiteralMapEntry(entry.key, new PureFunctionParameterExpr(idx), entry.quoted));
|
|
11780
|
+
if (e instanceof SafeKeyedReadExpr) {
|
|
11781
|
+
return new SafeTernaryExpr(e.receiver.clone(), e.receiver.key(e.index));
|
|
11318
11782
|
}
|
|
11319
11783
|
}
|
|
11320
|
-
return
|
|
11784
|
+
return e;
|
|
11321
11785
|
}
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
for (const op of view.update) {
|
|
11326
|
-
visitExpressionsInOp(op, expr => {
|
|
11327
|
-
if (!(expr instanceof PipeBindingVariadicExpr)) {
|
|
11328
|
-
return expr;
|
|
11329
|
-
}
|
|
11330
|
-
if (!(expr.args instanceof PureFunctionExpr)) {
|
|
11331
|
-
return expr;
|
|
11332
|
-
}
|
|
11333
|
-
if (expr.varOffset === null || expr.args.varOffset === null) {
|
|
11334
|
-
throw new Error(`Must run after variable counting`);
|
|
11335
|
-
}
|
|
11336
|
-
// The structure of this variadic pipe expression is:
|
|
11337
|
-
// PipeBindingVariadic(#, Y, PureFunction(X, ...ARGS))
|
|
11338
|
-
// Where X and Y are the slot offsets for the variables used by these operations, and Y > X.
|
|
11339
|
-
// In `TemplateDefinitionBuilder` the PipeBindingVariadic variable slots are allocated
|
|
11340
|
-
// before the PureFunction slots, which is unusually out-of-order.
|
|
11341
|
-
//
|
|
11342
|
-
// To maintain identical output for the tests in question, we adjust the variable offsets of
|
|
11343
|
-
// these two calls to emulate TDB's behavior. This is not perfect, because the ARGS of the
|
|
11344
|
-
// PureFunction call may also allocate slots which by TDB's ordering would come after X, and
|
|
11345
|
-
// we don't account for that. Still, this should be enough to pass the existing pipe tests.
|
|
11346
|
-
// Put the PipeBindingVariadic vars where the PureFunction vars were previously allocated.
|
|
11347
|
-
expr.varOffset = expr.args.varOffset;
|
|
11348
|
-
// Put the PureFunction vars following the PipeBindingVariadic vars.
|
|
11349
|
-
expr.args.varOffset = expr.varOffset + varsUsedByIrExpression(expr);
|
|
11350
|
-
});
|
|
11351
|
-
}
|
|
11786
|
+
function ternaryTransform(e) {
|
|
11787
|
+
if (!(e instanceof SafeTernaryExpr)) {
|
|
11788
|
+
return e;
|
|
11352
11789
|
}
|
|
11790
|
+
return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr);
|
|
11353
11791
|
}
|
|
11354
11792
|
|
|
11355
11793
|
/**
|
|
@@ -11357,6 +11795,7 @@ function phaseAlignPipeVariadicVarOffset(cpl) {
|
|
|
11357
11795
|
* processing, the compilation should be in a state where it can be emitted via `emitTemplateFn`.s
|
|
11358
11796
|
*/
|
|
11359
11797
|
function transformTemplate(cpl) {
|
|
11798
|
+
phaseAttributeExtraction(cpl, true);
|
|
11360
11799
|
phasePipeCreation(cpl);
|
|
11361
11800
|
phasePipeVariadic(cpl);
|
|
11362
11801
|
phasePureLiteralStructures(cpl);
|
|
@@ -11366,6 +11805,8 @@ function transformTemplate(cpl) {
|
|
|
11366
11805
|
phaseResolveContexts(cpl);
|
|
11367
11806
|
phaseLocalRefs(cpl);
|
|
11368
11807
|
phaseConstCollection(cpl);
|
|
11808
|
+
phaseNullishCoalescing(cpl);
|
|
11809
|
+
phaseExpandSafeReads(cpl);
|
|
11369
11810
|
phaseSlotAllocation(cpl);
|
|
11370
11811
|
phaseVarCounting(cpl);
|
|
11371
11812
|
phaseGenerateAdvance(cpl);
|
|
@@ -11618,7 +12059,6 @@ function ingestElement(view, element) {
|
|
|
11618
12059
|
const id = view.tpl.allocateXrefId();
|
|
11619
12060
|
const startOp = createElementStartOp(element.name, id);
|
|
11620
12061
|
view.create.push(startOp);
|
|
11621
|
-
ingestAttributes(startOp, element);
|
|
11622
12062
|
ingestBindings(view, startOp, element);
|
|
11623
12063
|
ingestReferences(startOp, element);
|
|
11624
12064
|
ingestNodes(view, element.children);
|
|
@@ -11632,7 +12072,6 @@ function ingestTemplate(view, tmpl) {
|
|
|
11632
12072
|
// TODO: validate the fallback tag name here.
|
|
11633
12073
|
const tplOp = createTemplateOp(childView.xref, tmpl.tagName ?? 'ng-template');
|
|
11634
12074
|
view.create.push(tplOp);
|
|
11635
|
-
ingestAttributes(tplOp, tmpl);
|
|
11636
12075
|
ingestBindings(view, tplOp, tmpl);
|
|
11637
12076
|
ingestReferences(tplOp, tmpl);
|
|
11638
12077
|
ingestNodes(childView, tmpl.children);
|
|
@@ -11722,36 +12161,27 @@ function convertAst(ast, cpl) {
|
|
|
11722
12161
|
else if (ast instanceof Conditional) {
|
|
11723
12162
|
return new ConditionalExpr(convertAst(ast.condition, cpl), convertAst(ast.trueExp, cpl), convertAst(ast.falseExp, cpl));
|
|
11724
12163
|
}
|
|
12164
|
+
else if (ast instanceof NonNullAssert) {
|
|
12165
|
+
// A non-null assertion shouldn't impact generated instructions, so we can just drop it.
|
|
12166
|
+
return convertAst(ast.expression, cpl);
|
|
12167
|
+
}
|
|
11725
12168
|
else if (ast instanceof BindingPipe) {
|
|
11726
12169
|
return new PipeBindingExpr(cpl.allocateXrefId(), ast.name, [
|
|
11727
12170
|
convertAst(ast.exp, cpl),
|
|
11728
12171
|
...ast.args.map(arg => convertAst(arg, cpl)),
|
|
11729
12172
|
]);
|
|
11730
12173
|
}
|
|
11731
|
-
else {
|
|
11732
|
-
|
|
11733
|
-
}
|
|
11734
|
-
}
|
|
11735
|
-
/**
|
|
11736
|
-
* Process all of the attributes on an element-like structure in the template AST and convert them
|
|
11737
|
-
* to their IR representation.
|
|
11738
|
-
*/
|
|
11739
|
-
function ingestAttributes(op, element) {
|
|
11740
|
-
assertIsElementAttributes(op.attributes);
|
|
11741
|
-
for (const attr of element.attributes) {
|
|
11742
|
-
op.attributes.add(ElementAttributeKind.Attribute, attr.name, literal(attr.value));
|
|
12174
|
+
else if (ast instanceof SafeKeyedRead) {
|
|
12175
|
+
return new SafeKeyedReadExpr(convertAst(ast.receiver, cpl), convertAst(ast.key, cpl));
|
|
11743
12176
|
}
|
|
11744
|
-
|
|
11745
|
-
|
|
12177
|
+
else if (ast instanceof SafePropertyRead) {
|
|
12178
|
+
return new SafePropertyReadExpr(convertAst(ast.receiver, cpl), ast.name);
|
|
11746
12179
|
}
|
|
11747
|
-
|
|
11748
|
-
|
|
12180
|
+
else if (ast instanceof SafeCall) {
|
|
12181
|
+
return new SafeInvokeFunctionExpr(convertAst(ast.receiver, cpl), ast.args.map(a => convertAst(a, cpl)));
|
|
11749
12182
|
}
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
// TODO: what do we do about the value here?
|
|
11753
|
-
op.attributes.add(ElementAttributeKind.Template, attr.name, null);
|
|
11754
|
-
}
|
|
12183
|
+
else {
|
|
12184
|
+
throw new Error(`Unhandled expression type: ${ast.constructor.name}`);
|
|
11755
12185
|
}
|
|
11756
12186
|
}
|
|
11757
12187
|
/**
|
|
@@ -11760,60 +12190,77 @@ function ingestAttributes(op, element) {
|
|
|
11760
12190
|
*/
|
|
11761
12191
|
function ingestBindings(view, op, element) {
|
|
11762
12192
|
if (element instanceof Template) {
|
|
11763
|
-
for (const attr of
|
|
11764
|
-
if (
|
|
11765
|
-
|
|
12193
|
+
for (const attr of element.templateAttrs) {
|
|
12194
|
+
if (attr instanceof TextAttribute) {
|
|
12195
|
+
view.update.push(createAttributeOp(op.xref, ElementAttributeKind.Template, attr.name, literal(attr.value)));
|
|
12196
|
+
}
|
|
12197
|
+
else {
|
|
12198
|
+
ingestPropertyBinding(view, op.xref, ElementAttributeKind.Template, attr);
|
|
11766
12199
|
}
|
|
11767
|
-
ingestPropertyBinding(view, op.xref, attr.name, attr.value);
|
|
11768
12200
|
}
|
|
11769
12201
|
}
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
12202
|
+
for (const attr of element.attributes) {
|
|
12203
|
+
view.update.push(createAttributeOp(op.xref, ElementAttributeKind.Attribute, attr.name, literal(attr.value)));
|
|
12204
|
+
}
|
|
12205
|
+
for (const input of element.inputs) {
|
|
12206
|
+
ingestPropertyBinding(view, op.xref, ElementAttributeKind.Binding, input);
|
|
12207
|
+
}
|
|
12208
|
+
for (const output of element.outputs) {
|
|
12209
|
+
const listenerOp = createListenerOp(op.xref, output.name, op.tag);
|
|
12210
|
+
// if output.handler is a chain, then push each statement from the chain separately, and
|
|
12211
|
+
// return the last one?
|
|
12212
|
+
let inputExprs;
|
|
12213
|
+
let handler = output.handler;
|
|
12214
|
+
if (handler instanceof ASTWithSource) {
|
|
12215
|
+
handler = handler.ast;
|
|
11773
12216
|
}
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
let handler = output.handler;
|
|
11780
|
-
if (handler instanceof ASTWithSource) {
|
|
11781
|
-
handler = handler.ast;
|
|
11782
|
-
}
|
|
11783
|
-
if (handler instanceof Chain) {
|
|
11784
|
-
inputExprs = handler.expressions;
|
|
11785
|
-
}
|
|
11786
|
-
else {
|
|
11787
|
-
inputExprs = [handler];
|
|
11788
|
-
}
|
|
11789
|
-
if (inputExprs.length === 0) {
|
|
11790
|
-
throw new Error('Expected listener to have non-empty expression list.');
|
|
11791
|
-
}
|
|
11792
|
-
const expressions = inputExprs.map(expr => convertAst(expr, view.tpl));
|
|
11793
|
-
const returnExpr = expressions.pop();
|
|
11794
|
-
for (const expr of expressions) {
|
|
11795
|
-
const stmtOp = createStatementOp(new ExpressionStatement(expr));
|
|
11796
|
-
listenerOp.handlerOps.push(stmtOp);
|
|
11797
|
-
}
|
|
11798
|
-
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr)));
|
|
11799
|
-
view.create.push(listenerOp);
|
|
12217
|
+
if (handler instanceof Chain) {
|
|
12218
|
+
inputExprs = handler.expressions;
|
|
12219
|
+
}
|
|
12220
|
+
else {
|
|
12221
|
+
inputExprs = [handler];
|
|
11800
12222
|
}
|
|
12223
|
+
if (inputExprs.length === 0) {
|
|
12224
|
+
throw new Error('Expected listener to have non-empty expression list.');
|
|
12225
|
+
}
|
|
12226
|
+
const expressions = inputExprs.map(expr => convertAst(expr, view.tpl));
|
|
12227
|
+
const returnExpr = expressions.pop();
|
|
12228
|
+
for (const expr of expressions) {
|
|
12229
|
+
const stmtOp = createStatementOp(new ExpressionStatement(expr));
|
|
12230
|
+
listenerOp.handlerOps.push(stmtOp);
|
|
12231
|
+
}
|
|
12232
|
+
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr)));
|
|
12233
|
+
view.create.push(listenerOp);
|
|
11801
12234
|
}
|
|
11802
12235
|
}
|
|
11803
|
-
function ingestPropertyBinding(view, xref, name, value) {
|
|
12236
|
+
function ingestPropertyBinding(view, xref, bindingKind, { name, value, type }) {
|
|
11804
12237
|
if (value instanceof ASTWithSource) {
|
|
11805
12238
|
value = value.ast;
|
|
11806
12239
|
}
|
|
11807
12240
|
if (value instanceof Interpolation) {
|
|
11808
|
-
|
|
12241
|
+
switch (type) {
|
|
12242
|
+
case 0 /* e.BindingType.Property */:
|
|
12243
|
+
view.update.push(createInterpolatePropertyOp(xref, bindingKind, name, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl))));
|
|
12244
|
+
break;
|
|
12245
|
+
default:
|
|
12246
|
+
// TODO: implement remaining binding types.
|
|
12247
|
+
throw Error(`Interpolated property binding type not handled: ${type}`);
|
|
12248
|
+
}
|
|
11809
12249
|
}
|
|
11810
12250
|
else {
|
|
11811
|
-
|
|
12251
|
+
switch (type) {
|
|
12252
|
+
case 0 /* e.BindingType.Property */:
|
|
12253
|
+
view.update.push(createPropertyOp(xref, bindingKind, name, convertAst(value, view.tpl)));
|
|
12254
|
+
break;
|
|
12255
|
+
default:
|
|
12256
|
+
// TODO: implement remaining binding types.
|
|
12257
|
+
throw Error(`Property binding type not handled: ${type}`);
|
|
12258
|
+
}
|
|
11812
12259
|
}
|
|
11813
12260
|
}
|
|
11814
12261
|
/**
|
|
11815
|
-
* Process all of the local references on an element-like structure in the template AST and
|
|
11816
|
-
* them to their IR representation.
|
|
12262
|
+
* Process all of the local references on an element-like structure in the template AST and
|
|
12263
|
+
* convert them to their IR representation.
|
|
11817
12264
|
*/
|
|
11818
12265
|
function ingestReferences(op, element) {
|
|
11819
12266
|
assertIsArray(op.localRefs);
|
|
@@ -23369,7 +23816,7 @@ function publishFacade(global) {
|
|
|
23369
23816
|
* @description
|
|
23370
23817
|
* Entry point for all public APIs of the compiler package.
|
|
23371
23818
|
*/
|
|
23372
|
-
const VERSION = new Version('16.
|
|
23819
|
+
const VERSION = new Version('16.2.0-next.0');
|
|
23373
23820
|
|
|
23374
23821
|
class CompilerConfig {
|
|
23375
23822
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -25297,7 +25744,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
25297
25744
|
function compileDeclareClassMetadata(metadata) {
|
|
25298
25745
|
const definitionMap = new DefinitionMap();
|
|
25299
25746
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
25300
|
-
definitionMap.set('version', literal('16.
|
|
25747
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25301
25748
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
25302
25749
|
definitionMap.set('type', metadata.type);
|
|
25303
25750
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -25400,7 +25847,7 @@ function compileDeclareDirectiveFromMetadata(meta) {
|
|
|
25400
25847
|
function createDirectiveDefinitionMap(meta) {
|
|
25401
25848
|
const definitionMap = new DefinitionMap();
|
|
25402
25849
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
25403
|
-
definitionMap.set('version', literal('16.
|
|
25850
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25404
25851
|
// e.g. `type: MyDirective`
|
|
25405
25852
|
definitionMap.set('type', meta.type.value);
|
|
25406
25853
|
if (meta.isStandalone) {
|
|
@@ -25628,7 +26075,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
25628
26075
|
function compileDeclareFactoryFunction(meta) {
|
|
25629
26076
|
const definitionMap = new DefinitionMap();
|
|
25630
26077
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
25631
|
-
definitionMap.set('version', literal('16.
|
|
26078
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25632
26079
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
25633
26080
|
definitionMap.set('type', meta.type.value);
|
|
25634
26081
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -25663,7 +26110,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
25663
26110
|
function createInjectableDefinitionMap(meta) {
|
|
25664
26111
|
const definitionMap = new DefinitionMap();
|
|
25665
26112
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
25666
|
-
definitionMap.set('version', literal('16.
|
|
26113
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25667
26114
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
25668
26115
|
definitionMap.set('type', meta.type.value);
|
|
25669
26116
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -25714,7 +26161,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
25714
26161
|
function createInjectorDefinitionMap(meta) {
|
|
25715
26162
|
const definitionMap = new DefinitionMap();
|
|
25716
26163
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
25717
|
-
definitionMap.set('version', literal('16.
|
|
26164
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25718
26165
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
25719
26166
|
definitionMap.set('type', meta.type.value);
|
|
25720
26167
|
definitionMap.set('providers', meta.providers);
|
|
@@ -25744,7 +26191,7 @@ function compileDeclareNgModuleFromMetadata(meta) {
|
|
|
25744
26191
|
function createNgModuleDefinitionMap(meta) {
|
|
25745
26192
|
const definitionMap = new DefinitionMap();
|
|
25746
26193
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
25747
|
-
definitionMap.set('version', literal('16.
|
|
26194
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25748
26195
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
25749
26196
|
definitionMap.set('type', meta.type.value);
|
|
25750
26197
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -25795,7 +26242,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
25795
26242
|
function createPipeDefinitionMap(meta) {
|
|
25796
26243
|
const definitionMap = new DefinitionMap();
|
|
25797
26244
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
25798
|
-
definitionMap.set('version', literal('16.
|
|
26245
|
+
definitionMap.set('version', literal('16.2.0-next.0'));
|
|
25799
26246
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
25800
26247
|
// e.g. `type: MyPipe`
|
|
25801
26248
|
definitionMap.set('type', meta.type.value);
|