@angular/compiler 16.0.0-rc.1 → 16.0.0-rc.3
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/constant_pool.mjs +2 -1
- package/esm2022/src/i18n/extractor_merger.mjs +1 -1
- package/esm2022/src/i18n/i18n_ast.mjs +4 -4
- package/esm2022/src/i18n/serializers/xliff.mjs +1 -1
- package/esm2022/src/i18n/serializers/xliff2.mjs +4 -1
- package/esm2022/src/i18n/serializers/xtb.mjs +1 -1
- package/esm2022/src/i18n/translation_bundle.mjs +2 -2
- package/esm2022/src/jit_compiler_facade.mjs +1 -1
- package/esm2022/src/ml_parser/entities.mjs +4 -3
- package/esm2022/src/ml_parser/html_whitespaces.mjs +4 -5
- package/esm2022/src/ml_parser/xml_tags.mjs +2 -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/render3/view/compiler.mjs +5 -3
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +33 -26
- package/esm2022/src/template/pipeline/ir/src/operations.mjs +12 -3
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +6 -4
- package/esm2022/src/template/pipeline/ir/src/ops/shared.mjs +1 -2
- package/esm2022/src/template/pipeline/ir/src/traits.mjs +11 -6
- package/esm2022/src/template/pipeline/ir/src/variable.mjs +1 -1
- package/esm2022/src/template/pipeline/src/emit.mjs +30 -8
- package/esm2022/src/template/pipeline/src/ingest.mjs +17 -11
- package/esm2022/src/template/pipeline/src/instruction.mjs +14 -9
- package/esm2022/src/template/pipeline/src/phases/chaining.mjs +78 -0
- package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +29 -21
- package/esm2022/src/template/pipeline/src/phases/naming.mjs +57 -40
- package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +69 -0
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +9 -9
- package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +4 -4
- package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +9 -2
- package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +359 -0
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +724 -154
- package/fesm2022/compiler.mjs.map +1 -1
- package/fesm2022/testing.mjs +1 -1
- package/index.d.ts +3 -3
- 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.0.0-rc.
|
|
2
|
+
* @license Angular v16.0.0-rc.3
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -2220,6 +2220,7 @@ class FixupExpression extends Expression {
|
|
|
2220
2220
|
constructor(resolved) {
|
|
2221
2221
|
super(resolved.type);
|
|
2222
2222
|
this.resolved = resolved;
|
|
2223
|
+
this.shared = false;
|
|
2223
2224
|
this.original = resolved;
|
|
2224
2225
|
}
|
|
2225
2226
|
visitExpression(visitor, context) {
|
|
@@ -4035,11 +4036,12 @@ class Container {
|
|
|
4035
4036
|
}
|
|
4036
4037
|
}
|
|
4037
4038
|
class Icu {
|
|
4038
|
-
constructor(expression, type, cases, sourceSpan) {
|
|
4039
|
+
constructor(expression, type, cases, sourceSpan, expressionPlaceholder) {
|
|
4039
4040
|
this.expression = expression;
|
|
4040
4041
|
this.type = type;
|
|
4041
4042
|
this.cases = cases;
|
|
4042
4043
|
this.sourceSpan = sourceSpan;
|
|
4044
|
+
this.expressionPlaceholder = expressionPlaceholder;
|
|
4043
4045
|
}
|
|
4044
4046
|
visit(visitor, context) {
|
|
4045
4047
|
return visitor.visitIcu(this, context);
|
|
@@ -4095,8 +4097,7 @@ class CloneVisitor {
|
|
|
4095
4097
|
visitIcu(icu, context) {
|
|
4096
4098
|
const cases = {};
|
|
4097
4099
|
Object.keys(icu.cases).forEach(key => cases[key] = icu.cases[key].visit(this, context));
|
|
4098
|
-
const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan);
|
|
4099
|
-
msg.expressionPlaceholder = icu.expressionPlaceholder;
|
|
4100
|
+
const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan, icu.expressionPlaceholder);
|
|
4100
4101
|
return msg;
|
|
4101
4102
|
}
|
|
4102
4103
|
visitTagPlaceholder(ph, context) {
|
|
@@ -8670,6 +8671,14 @@ const TRAIT_CONSUMES_SLOT = {
|
|
|
8670
8671
|
slot: null,
|
|
8671
8672
|
numSlotsUsed: 1,
|
|
8672
8673
|
};
|
|
8674
|
+
/**
|
|
8675
|
+
* Default values for most `UsesSlotIndexTrait` fields (used with the spread operator to initialize
|
|
8676
|
+
* implementors of the trait).
|
|
8677
|
+
*/
|
|
8678
|
+
const TRAIT_USES_SLOT_INDEX = {
|
|
8679
|
+
[UsesSlotIndex]: true,
|
|
8680
|
+
slot: null,
|
|
8681
|
+
};
|
|
8673
8682
|
/**
|
|
8674
8683
|
* Default values for most `DependsOnSlotContextOpTrait` fields (used with the spread operator to
|
|
8675
8684
|
* initialize implementors of the trait).
|
|
@@ -8699,11 +8708,8 @@ function hasDependsOnSlotContextTrait(op) {
|
|
|
8699
8708
|
function hasConsumesVarsTrait(value) {
|
|
8700
8709
|
return value[ConsumesVarsTrait] === true;
|
|
8701
8710
|
}
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
*/
|
|
8705
|
-
function hasUsesSlotIndexTrait(expr) {
|
|
8706
|
-
return expr[UsesSlotIndex] === true;
|
|
8711
|
+
function hasUsesSlotIndexTrait(value) {
|
|
8712
|
+
return value[UsesSlotIndex] === true;
|
|
8707
8713
|
}
|
|
8708
8714
|
|
|
8709
8715
|
var _a;
|
|
@@ -8786,10 +8792,11 @@ class NextContextExpr extends ExpressionBase {
|
|
|
8786
8792
|
constructor() {
|
|
8787
8793
|
super();
|
|
8788
8794
|
this.kind = ExpressionKind.NextContext;
|
|
8795
|
+
this.steps = 1;
|
|
8789
8796
|
}
|
|
8790
8797
|
visitExpression() { }
|
|
8791
8798
|
isEquivalent(e) {
|
|
8792
|
-
return e instanceof NextContextExpr;
|
|
8799
|
+
return e instanceof NextContextExpr && e.steps === this.steps;
|
|
8793
8800
|
}
|
|
8794
8801
|
isConstant() {
|
|
8795
8802
|
return false;
|
|
@@ -8844,9 +8851,9 @@ class RestoreViewExpr extends ExpressionBase {
|
|
|
8844
8851
|
isConstant() {
|
|
8845
8852
|
return false;
|
|
8846
8853
|
}
|
|
8847
|
-
transformInternalExpressions(transform) {
|
|
8854
|
+
transformInternalExpressions(transform, flags) {
|
|
8848
8855
|
if (typeof this.view !== 'number') {
|
|
8849
|
-
this.view = transformExpressionsInExpression(this.view, transform);
|
|
8856
|
+
this.view = transformExpressionsInExpression(this.view, transform, flags);
|
|
8850
8857
|
}
|
|
8851
8858
|
}
|
|
8852
8859
|
}
|
|
@@ -8868,8 +8875,8 @@ class ResetViewExpr extends ExpressionBase {
|
|
|
8868
8875
|
isConstant() {
|
|
8869
8876
|
return false;
|
|
8870
8877
|
}
|
|
8871
|
-
transformInternalExpressions(transform) {
|
|
8872
|
-
this.expr = transformExpressionsInExpression(this.expr, transform);
|
|
8878
|
+
transformInternalExpressions(transform, flags) {
|
|
8879
|
+
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
8873
8880
|
}
|
|
8874
8881
|
}
|
|
8875
8882
|
/**
|
|
@@ -8895,36 +8902,41 @@ class ReadVariableExpr extends ExpressionBase {
|
|
|
8895
8902
|
* Visits all `Expression`s in the AST of `op` with the `visitor` function.
|
|
8896
8903
|
*/
|
|
8897
8904
|
function visitExpressionsInOp(op, visitor) {
|
|
8898
|
-
transformExpressionsInOp(op, (expr) => {
|
|
8899
|
-
visitor(expr);
|
|
8905
|
+
transformExpressionsInOp(op, (expr, flags) => {
|
|
8906
|
+
visitor(expr, flags);
|
|
8900
8907
|
return expr;
|
|
8901
|
-
});
|
|
8908
|
+
}, VisitorContextFlag.None);
|
|
8902
8909
|
}
|
|
8910
|
+
var VisitorContextFlag;
|
|
8911
|
+
(function (VisitorContextFlag) {
|
|
8912
|
+
VisitorContextFlag[VisitorContextFlag["None"] = 0] = "None";
|
|
8913
|
+
VisitorContextFlag[VisitorContextFlag["InChildOperation"] = 1] = "InChildOperation";
|
|
8914
|
+
})(VisitorContextFlag || (VisitorContextFlag = {}));
|
|
8903
8915
|
/**
|
|
8904
8916
|
* Transform all `Expression`s in the AST of `op` with the `transform` function.
|
|
8905
8917
|
*
|
|
8906
8918
|
* All such operations will be replaced with the result of applying `transform`, which may be an
|
|
8907
8919
|
* identity transformation.
|
|
8908
8920
|
*/
|
|
8909
|
-
function transformExpressionsInOp(op, transform) {
|
|
8921
|
+
function transformExpressionsInOp(op, transform, flags) {
|
|
8910
8922
|
switch (op.kind) {
|
|
8911
8923
|
case OpKind.Property:
|
|
8912
|
-
op.expression = transformExpressionsInExpression(op.expression, transform);
|
|
8924
|
+
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
8913
8925
|
break;
|
|
8914
8926
|
case OpKind.Statement:
|
|
8915
|
-
transformExpressionsInStatement(op.statement, transform);
|
|
8927
|
+
transformExpressionsInStatement(op.statement, transform, flags);
|
|
8916
8928
|
break;
|
|
8917
8929
|
case OpKind.Variable:
|
|
8918
|
-
op.initializer = transformExpressionsInExpression(op.initializer, transform);
|
|
8930
|
+
op.initializer = transformExpressionsInExpression(op.initializer, transform, flags);
|
|
8919
8931
|
break;
|
|
8920
8932
|
case OpKind.InterpolateText:
|
|
8921
8933
|
for (let i = 0; i < op.expressions.length; i++) {
|
|
8922
|
-
op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform);
|
|
8934
|
+
op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform, flags);
|
|
8923
8935
|
}
|
|
8924
8936
|
break;
|
|
8925
8937
|
case OpKind.Listener:
|
|
8926
8938
|
for (const innerOp of op.handlerOps) {
|
|
8927
|
-
transformExpressionsInOp(innerOp, transform);
|
|
8939
|
+
transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
|
|
8928
8940
|
}
|
|
8929
8941
|
break;
|
|
8930
8942
|
case OpKind.Element:
|
|
@@ -8932,6 +8944,7 @@ function transformExpressionsInOp(op, transform) {
|
|
|
8932
8944
|
case OpKind.ElementEnd:
|
|
8933
8945
|
case OpKind.Template:
|
|
8934
8946
|
case OpKind.Text:
|
|
8947
|
+
case OpKind.Advance:
|
|
8935
8948
|
// These operations contain no expressions.
|
|
8936
8949
|
break;
|
|
8937
8950
|
default:
|
|
@@ -8944,22 +8957,22 @@ function transformExpressionsInOp(op, transform) {
|
|
|
8944
8957
|
* All such operations will be replaced with the result of applying `transform`, which may be an
|
|
8945
8958
|
* identity transformation.
|
|
8946
8959
|
*/
|
|
8947
|
-
function transformExpressionsInExpression(expr, transform) {
|
|
8960
|
+
function transformExpressionsInExpression(expr, transform, flags) {
|
|
8948
8961
|
if (expr instanceof ExpressionBase) {
|
|
8949
|
-
expr.transformInternalExpressions(transform);
|
|
8950
|
-
return transform(expr);
|
|
8962
|
+
expr.transformInternalExpressions(transform, flags);
|
|
8963
|
+
return transform(expr, flags);
|
|
8951
8964
|
}
|
|
8952
8965
|
else if (expr instanceof BinaryOperatorExpr) {
|
|
8953
|
-
expr.lhs = transformExpressionsInExpression(expr.lhs, transform);
|
|
8954
|
-
expr.rhs = transformExpressionsInExpression(expr.rhs, transform);
|
|
8966
|
+
expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
|
|
8967
|
+
expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
|
|
8955
8968
|
}
|
|
8956
8969
|
else if (expr instanceof ReadPropExpr) {
|
|
8957
|
-
expr.receiver = transformExpressionsInExpression(expr.receiver, transform);
|
|
8970
|
+
expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
|
|
8958
8971
|
}
|
|
8959
8972
|
else if (expr instanceof InvokeFunctionExpr) {
|
|
8960
|
-
expr.fn = transformExpressionsInExpression(expr.fn, transform);
|
|
8973
|
+
expr.fn = transformExpressionsInExpression(expr.fn, transform, flags);
|
|
8961
8974
|
for (let i = 0; i < expr.args.length; i++) {
|
|
8962
|
-
expr.args[i] = transformExpressionsInExpression(expr.args[i], transform);
|
|
8975
|
+
expr.args[i] = transformExpressionsInExpression(expr.args[i], transform, flags);
|
|
8963
8976
|
}
|
|
8964
8977
|
}
|
|
8965
8978
|
else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
|
|
@@ -8977,12 +8990,12 @@ function transformExpressionsInExpression(expr, transform) {
|
|
|
8977
8990
|
* All such operations will be replaced with the result of applying `transform`, which may be an
|
|
8978
8991
|
* identity transformation.
|
|
8979
8992
|
*/
|
|
8980
|
-
function transformExpressionsInStatement(stmt, transform) {
|
|
8993
|
+
function transformExpressionsInStatement(stmt, transform, flags) {
|
|
8981
8994
|
if (stmt instanceof ExpressionStatement) {
|
|
8982
|
-
stmt.expr = transformExpressionsInExpression(stmt.expr, transform);
|
|
8995
|
+
stmt.expr = transformExpressionsInExpression(stmt.expr, transform, flags);
|
|
8983
8996
|
}
|
|
8984
8997
|
else if (stmt instanceof ReturnStatement) {
|
|
8985
|
-
stmt.value = transformExpressionsInExpression(stmt.value, transform);
|
|
8998
|
+
stmt.value = transformExpressionsInExpression(stmt.value, transform, flags);
|
|
8986
8999
|
}
|
|
8987
9000
|
else {
|
|
8988
9001
|
throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`);
|
|
@@ -9070,12 +9083,21 @@ class OpList {
|
|
|
9070
9083
|
while (current !== this.tail) {
|
|
9071
9084
|
// Guards against corruption of the iterator state by mutations to the tail of the list during
|
|
9072
9085
|
// iteration.
|
|
9073
|
-
OpList.assertIsOwned(current);
|
|
9086
|
+
OpList.assertIsOwned(current, this.debugListId);
|
|
9074
9087
|
const next = current.next;
|
|
9075
9088
|
yield current;
|
|
9076
9089
|
current = next;
|
|
9077
9090
|
}
|
|
9078
9091
|
}
|
|
9092
|
+
*reversed() {
|
|
9093
|
+
let current = this.tail.prev;
|
|
9094
|
+
while (current !== this.head) {
|
|
9095
|
+
OpList.assertIsOwned(current, this.debugListId);
|
|
9096
|
+
const prev = current.prev;
|
|
9097
|
+
yield current;
|
|
9098
|
+
current = prev;
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9079
9101
|
/**
|
|
9080
9102
|
* Replace `oldOp` with `newOp` in the list.
|
|
9081
9103
|
*/
|
|
@@ -9164,7 +9186,7 @@ class OpList {
|
|
|
9164
9186
|
OpList.assertIsNotEnd(before);
|
|
9165
9187
|
OpList.assertIsNotEnd(op);
|
|
9166
9188
|
OpList.assertIsUnowned(op);
|
|
9167
|
-
OpList.assertIsOwned(before
|
|
9189
|
+
OpList.assertIsOwned(before);
|
|
9168
9190
|
op.debugListId = before.debugListId;
|
|
9169
9191
|
// Just in case.
|
|
9170
9192
|
op.prev = null;
|
|
@@ -9220,7 +9242,6 @@ function createVariableOp(xref, variable, initializer) {
|
|
|
9220
9242
|
return {
|
|
9221
9243
|
kind: OpKind.Variable,
|
|
9222
9244
|
xref,
|
|
9223
|
-
name: null,
|
|
9224
9245
|
variable,
|
|
9225
9246
|
initializer,
|
|
9226
9247
|
...NEW_OP,
|
|
@@ -9293,14 +9314,16 @@ function createTextOp(xref, initialValue) {
|
|
|
9293
9314
|
/**
|
|
9294
9315
|
* Create a `ListenerOp`.
|
|
9295
9316
|
*/
|
|
9296
|
-
function createListenerOp(
|
|
9317
|
+
function createListenerOp(target, name, tag) {
|
|
9297
9318
|
return {
|
|
9298
9319
|
kind: OpKind.Listener,
|
|
9299
|
-
|
|
9320
|
+
target,
|
|
9321
|
+
tag,
|
|
9300
9322
|
name,
|
|
9301
9323
|
handlerOps: new OpList(),
|
|
9302
9324
|
handlerFnName: null,
|
|
9303
9325
|
...NEW_OP,
|
|
9326
|
+
...TRAIT_USES_SLOT_INDEX,
|
|
9304
9327
|
};
|
|
9305
9328
|
}
|
|
9306
9329
|
|
|
@@ -9450,7 +9473,7 @@ function phaseGenerateAdvance(cpl) {
|
|
|
9450
9473
|
if (delta < 0) {
|
|
9451
9474
|
throw new Error(`AssertionError: slot counter should never need to move backwards`);
|
|
9452
9475
|
}
|
|
9453
|
-
OpList.insertBefore(
|
|
9476
|
+
OpList.insertBefore(createAdvanceOp(delta), op);
|
|
9454
9477
|
slotContext = slot;
|
|
9455
9478
|
}
|
|
9456
9479
|
}
|
|
@@ -9509,8 +9532,8 @@ function reference(slot) {
|
|
|
9509
9532
|
literal(slot),
|
|
9510
9533
|
]);
|
|
9511
9534
|
}
|
|
9512
|
-
function nextContext() {
|
|
9513
|
-
return importExpr(Identifiers.nextContext).callFn([]);
|
|
9535
|
+
function nextContext(steps) {
|
|
9536
|
+
return importExpr(Identifiers.nextContext).callFn(steps === 1 ? [] : [literal(steps)]);
|
|
9514
9537
|
}
|
|
9515
9538
|
function getCurrentView() {
|
|
9516
9539
|
return importExpr(Identifiers.getCurrentView).callFn([]);
|
|
@@ -9521,7 +9544,7 @@ function restoreView(savedView) {
|
|
|
9521
9544
|
]);
|
|
9522
9545
|
}
|
|
9523
9546
|
function resetView(returnValue) {
|
|
9524
|
-
return importExpr(Identifiers.
|
|
9547
|
+
return importExpr(Identifiers.resetView).callFn([
|
|
9525
9548
|
returnValue,
|
|
9526
9549
|
]);
|
|
9527
9550
|
}
|
|
@@ -9543,12 +9566,17 @@ function textInterpolate(strings, expressions) {
|
|
|
9543
9566
|
throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
|
|
9544
9567
|
}
|
|
9545
9568
|
const interpolationArgs = [];
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9569
|
+
if (expressions.length === 1 && strings[0] === '' && strings[1] === '') {
|
|
9570
|
+
interpolationArgs.push(expressions[0]);
|
|
9571
|
+
}
|
|
9572
|
+
else {
|
|
9573
|
+
let idx;
|
|
9574
|
+
for (idx = 0; idx < expressions.length; idx++) {
|
|
9575
|
+
interpolationArgs.push(literal(strings[idx]), expressions[idx]);
|
|
9576
|
+
}
|
|
9577
|
+
// idx points at the last string.
|
|
9578
|
+
interpolationArgs.push(literal(strings[idx]));
|
|
9549
9579
|
}
|
|
9550
|
-
// idx points at the last string.
|
|
9551
|
-
interpolationArgs.push(literal(strings[idx]));
|
|
9552
9580
|
return callInterpolation(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs);
|
|
9553
9581
|
}
|
|
9554
9582
|
function call(instruction, args) {
|
|
@@ -9602,7 +9630,7 @@ function phaseReify(cpl) {
|
|
|
9602
9630
|
}
|
|
9603
9631
|
function reifyCreateOperations(view, ops) {
|
|
9604
9632
|
for (const op of ops) {
|
|
9605
|
-
transformExpressionsInOp(op, reifyIrExpression);
|
|
9633
|
+
transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
|
|
9606
9634
|
switch (op.kind) {
|
|
9607
9635
|
case OpKind.Text:
|
|
9608
9636
|
OpList.replace(op, text(op.slot, op.initialValue));
|
|
@@ -9618,17 +9646,17 @@ function reifyCreateOperations(view, ops) {
|
|
|
9618
9646
|
break;
|
|
9619
9647
|
case OpKind.Template:
|
|
9620
9648
|
const childView = view.tpl.views.get(op.xref);
|
|
9621
|
-
OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.
|
|
9649
|
+
OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes));
|
|
9622
9650
|
break;
|
|
9623
9651
|
case OpKind.Listener:
|
|
9624
9652
|
const listenerFn = reifyListenerHandler(view, op.handlerFnName, op.handlerOps);
|
|
9625
9653
|
OpList.replace(op, listener(op.name, listenerFn));
|
|
9626
9654
|
break;
|
|
9627
9655
|
case OpKind.Variable:
|
|
9628
|
-
if (op.name === null) {
|
|
9656
|
+
if (op.variable.name === null) {
|
|
9629
9657
|
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
9630
9658
|
}
|
|
9631
|
-
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.name, op.initializer)));
|
|
9659
|
+
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
9632
9660
|
break;
|
|
9633
9661
|
case OpKind.Statement:
|
|
9634
9662
|
// Pass statement operations directly through.
|
|
@@ -9640,7 +9668,7 @@ function reifyCreateOperations(view, ops) {
|
|
|
9640
9668
|
}
|
|
9641
9669
|
function reifyUpdateOperations(_view, ops) {
|
|
9642
9670
|
for (const op of ops) {
|
|
9643
|
-
transformExpressionsInOp(op, reifyIrExpression);
|
|
9671
|
+
transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
|
|
9644
9672
|
switch (op.kind) {
|
|
9645
9673
|
case OpKind.Advance:
|
|
9646
9674
|
OpList.replace(op, advance(op.delta));
|
|
@@ -9652,10 +9680,10 @@ function reifyUpdateOperations(_view, ops) {
|
|
|
9652
9680
|
OpList.replace(op, textInterpolate(op.strings, op.expressions));
|
|
9653
9681
|
break;
|
|
9654
9682
|
case OpKind.Variable:
|
|
9655
|
-
if (op.name === null) {
|
|
9683
|
+
if (op.variable.name === null) {
|
|
9656
9684
|
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
9657
9685
|
}
|
|
9658
|
-
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.name, op.initializer)));
|
|
9686
|
+
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.variable.name, op.initializer, undefined, StmtModifier.Final)));
|
|
9659
9687
|
break;
|
|
9660
9688
|
case OpKind.Statement:
|
|
9661
9689
|
// Pass statement operations directly through.
|
|
@@ -9668,7 +9696,7 @@ function reifyUpdateOperations(_view, ops) {
|
|
|
9668
9696
|
function reifyIrExpression(expr) {
|
|
9669
9697
|
switch (expr.kind) {
|
|
9670
9698
|
case ExpressionKind.NextContext:
|
|
9671
|
-
return nextContext();
|
|
9699
|
+
return nextContext(expr.steps);
|
|
9672
9700
|
case ExpressionKind.Reference:
|
|
9673
9701
|
return reference(expr.slot + 1 + expr.offset);
|
|
9674
9702
|
case ExpressionKind.LexicalRead:
|
|
@@ -9736,7 +9764,7 @@ class LookForEventVisitor extends RecursiveAstVisitor$1 {
|
|
|
9736
9764
|
/**
|
|
9737
9765
|
* Assign data slots for all operations which implement `ConsumesSlotOpTrait`, and propagate the
|
|
9738
9766
|
* assigned data slots of those operations to any expressions which reference them via
|
|
9739
|
-
* `
|
|
9767
|
+
* `UsesSlotIndexTrait`.
|
|
9740
9768
|
*
|
|
9741
9769
|
* This phase is also responsible for counting the number of slots used for each view (its `decls`)
|
|
9742
9770
|
* and propagating that number into the `Template` operations which declare embedded views.
|
|
@@ -9781,6 +9809,13 @@ function phaseSlotAllocation(cpl) {
|
|
|
9781
9809
|
const childView = cpl.views.get(op.xref);
|
|
9782
9810
|
op.decls = childView.decls;
|
|
9783
9811
|
}
|
|
9812
|
+
if (hasUsesSlotIndexTrait(op) && op.slot === null) {
|
|
9813
|
+
if (!slotMap.has(op.target)) {
|
|
9814
|
+
// We do expect to find a slot allocated for everything which might be referenced.
|
|
9815
|
+
throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
|
|
9816
|
+
}
|
|
9817
|
+
op.slot = slotMap.get(op.target);
|
|
9818
|
+
}
|
|
9784
9819
|
// Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
|
|
9785
9820
|
visitExpressionsInOp(op, expr => {
|
|
9786
9821
|
if (!hasUsesSlotIndexTrait(expr) || expr.slot !== null) {
|
|
@@ -9859,50 +9894,67 @@ function varsUsedByIrExpression(expr) {
|
|
|
9859
9894
|
* the reads can be emitted correctly.
|
|
9860
9895
|
*/
|
|
9861
9896
|
function phaseNaming(cpl) {
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
// TODO(alxhub): convert this temporary name to match how the
|
|
9880
|
-
// `TemplateDefinitionBuilder` names listener functions.
|
|
9881
|
-
op.handlerFnName = `${view.fnName}_${op.name}_listener`;
|
|
9882
|
-
}
|
|
9883
|
-
break;
|
|
9884
|
-
case OpKind.Variable:
|
|
9885
|
-
if (op.name === null) {
|
|
9886
|
-
op.name = `_r${vIndex++}`;
|
|
9887
|
-
varNames.set(op.xref, op.name);
|
|
9897
|
+
addNamesToView(cpl.root, cpl.componentName, { index: 0 });
|
|
9898
|
+
}
|
|
9899
|
+
function addNamesToView(view, baseName, state) {
|
|
9900
|
+
if (view.fnName === null) {
|
|
9901
|
+
view.fnName = `${baseName}_Template`;
|
|
9902
|
+
}
|
|
9903
|
+
// Keep track of the names we assign to variables in the view. We'll need to propagate these
|
|
9904
|
+
// into reads of those variables afterwards.
|
|
9905
|
+
const varNames = new Map();
|
|
9906
|
+
for (const op of view.ops()) {
|
|
9907
|
+
switch (op.kind) {
|
|
9908
|
+
case OpKind.Listener:
|
|
9909
|
+
if (op.handlerFnName === null) {
|
|
9910
|
+
// TODO(alxhub): convert this temporary name to match how the
|
|
9911
|
+
// `TemplateDefinitionBuilder` names listener functions.
|
|
9912
|
+
if (op.slot === null) {
|
|
9913
|
+
throw new Error(`Expected a slot to be assigned`);
|
|
9888
9914
|
}
|
|
9889
|
-
|
|
9890
|
-
}
|
|
9891
|
-
}
|
|
9892
|
-
// Having named all variables declared in the view, now we can push those names into the
|
|
9893
|
-
// `ir.ReadVariableExpr` expressions which represent reads of those variables.
|
|
9894
|
-
for (const op of view.ops()) {
|
|
9895
|
-
visitExpressionsInOp(op, expr => {
|
|
9896
|
-
if (!(expr instanceof ReadVariableExpr) || expr.name !== null) {
|
|
9897
|
-
return;
|
|
9915
|
+
op.handlerFnName = `${view.fnName}_${op.tag}_${op.name}_${op.slot}_listener`;
|
|
9898
9916
|
}
|
|
9899
|
-
|
|
9900
|
-
|
|
9917
|
+
break;
|
|
9918
|
+
case OpKind.Variable:
|
|
9919
|
+
varNames.set(op.xref, getVariableName(op.variable, state));
|
|
9920
|
+
break;
|
|
9921
|
+
case OpKind.Template:
|
|
9922
|
+
const childView = view.tpl.views.get(op.xref);
|
|
9923
|
+
if (op.slot === null) {
|
|
9924
|
+
throw new Error(`Expected slot to be assigned`);
|
|
9901
9925
|
}
|
|
9902
|
-
|
|
9903
|
-
|
|
9926
|
+
// TODO: properly escape the tag name.
|
|
9927
|
+
const safeTagName = op.tag.replace('-', '_');
|
|
9928
|
+
addNamesToView(childView, `${baseName}_${safeTagName}_${op.slot}`, state);
|
|
9929
|
+
break;
|
|
9904
9930
|
}
|
|
9905
9931
|
}
|
|
9932
|
+
// Having named all variables declared in the view, now we can push those names into the
|
|
9933
|
+
// `ir.ReadVariableExpr` expressions which represent reads of those variables.
|
|
9934
|
+
for (const op of view.ops()) {
|
|
9935
|
+
visitExpressionsInOp(op, expr => {
|
|
9936
|
+
if (!(expr instanceof ReadVariableExpr) || expr.name !== null) {
|
|
9937
|
+
return;
|
|
9938
|
+
}
|
|
9939
|
+
if (!varNames.has(expr.xref)) {
|
|
9940
|
+
throw new Error(`Variable ${expr.xref} not yet named`);
|
|
9941
|
+
}
|
|
9942
|
+
expr.name = varNames.get(expr.xref);
|
|
9943
|
+
});
|
|
9944
|
+
}
|
|
9945
|
+
}
|
|
9946
|
+
function getVariableName(variable, state) {
|
|
9947
|
+
if (variable.name === null) {
|
|
9948
|
+
switch (variable.kind) {
|
|
9949
|
+
case SemanticVariableKind.Identifier:
|
|
9950
|
+
variable.name = `${variable.identifier}_${state.index++}`;
|
|
9951
|
+
break;
|
|
9952
|
+
default:
|
|
9953
|
+
variable.name = `_r${state.index++}`;
|
|
9954
|
+
break;
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
return variable.name;
|
|
9906
9958
|
}
|
|
9907
9959
|
|
|
9908
9960
|
/**
|
|
@@ -9970,10 +10022,7 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
9970
10022
|
// Start the view creation block with an operation to save the current view context. This may be
|
|
9971
10023
|
// used to restore the view context in any listeners that may be present.
|
|
9972
10024
|
view.create.prepend([
|
|
9973
|
-
createVariableOp(view.tpl.allocateXrefId(),
|
|
9974
|
-
kind: SemanticVariableKind.SavedView,
|
|
9975
|
-
view: view.xref,
|
|
9976
|
-
}, new GetCurrentViewExpr()),
|
|
10025
|
+
createVariableOp(view.tpl.allocateXrefId(), scope.savedViewVariable, new GetCurrentViewExpr()),
|
|
9977
10026
|
]);
|
|
9978
10027
|
for (const op of view.create) {
|
|
9979
10028
|
switch (op.kind) {
|
|
@@ -9984,10 +10033,7 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
9984
10033
|
case OpKind.Listener:
|
|
9985
10034
|
// Listeners get a preamble which starts with a call to restore the view.
|
|
9986
10035
|
const preambleOps = [
|
|
9987
|
-
createVariableOp(view.tpl.allocateXrefId(),
|
|
9988
|
-
kind: SemanticVariableKind.Context,
|
|
9989
|
-
view: view.xref,
|
|
9990
|
-
}, new RestoreViewExpr(view.xref)),
|
|
10036
|
+
createVariableOp(view.tpl.allocateXrefId(), scope.viewContextVariable, new RestoreViewExpr(view.xref)),
|
|
9991
10037
|
// And includes all variables available to this view.
|
|
9992
10038
|
...generateVariablesInScopeForView(view, scope)
|
|
9993
10039
|
];
|
|
@@ -10015,9 +10061,27 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
10015
10061
|
function getScopeForView(view, parent) {
|
|
10016
10062
|
const scope = {
|
|
10017
10063
|
view: view.xref,
|
|
10064
|
+
viewContextVariable: {
|
|
10065
|
+
kind: SemanticVariableKind.Context,
|
|
10066
|
+
name: null,
|
|
10067
|
+
view: view.xref,
|
|
10068
|
+
},
|
|
10069
|
+
savedViewVariable: {
|
|
10070
|
+
kind: SemanticVariableKind.SavedView,
|
|
10071
|
+
name: null,
|
|
10072
|
+
view: view.xref,
|
|
10073
|
+
},
|
|
10074
|
+
contextVariables: new Map(),
|
|
10018
10075
|
references: [],
|
|
10019
10076
|
parent,
|
|
10020
10077
|
};
|
|
10078
|
+
for (const identifier of view.contextVariables.keys()) {
|
|
10079
|
+
scope.contextVariables.set(identifier, {
|
|
10080
|
+
kind: SemanticVariableKind.Identifier,
|
|
10081
|
+
name: null,
|
|
10082
|
+
identifier,
|
|
10083
|
+
});
|
|
10084
|
+
}
|
|
10021
10085
|
for (const op of view.create) {
|
|
10022
10086
|
switch (op.kind) {
|
|
10023
10087
|
case OpKind.Element:
|
|
@@ -10032,6 +10096,11 @@ function getScopeForView(view, parent) {
|
|
|
10032
10096
|
name: op.localRefs[offset].name,
|
|
10033
10097
|
targetId: op.xref,
|
|
10034
10098
|
offset,
|
|
10099
|
+
variable: {
|
|
10100
|
+
kind: SemanticVariableKind.Identifier,
|
|
10101
|
+
name: null,
|
|
10102
|
+
identifier: op.localRefs[offset].name,
|
|
10103
|
+
},
|
|
10035
10104
|
});
|
|
10036
10105
|
}
|
|
10037
10106
|
break;
|
|
@@ -10051,24 +10120,15 @@ function generateVariablesInScopeForView(view, scope) {
|
|
|
10051
10120
|
// Before generating variables for a parent view, we need to switch to the context of the parent
|
|
10052
10121
|
// view with a `nextContext` expression. This context switching operation itself declares a
|
|
10053
10122
|
// variable, because the context of the view may be referenced directly.
|
|
10054
|
-
newOps.push(createVariableOp(view.tpl.allocateXrefId(),
|
|
10055
|
-
kind: SemanticVariableKind.Context,
|
|
10056
|
-
view: scope.view,
|
|
10057
|
-
}, new NextContextExpr()));
|
|
10123
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), scope.viewContextVariable, new NextContextExpr()));
|
|
10058
10124
|
}
|
|
10059
10125
|
// Add variables for all context variables available in this scope's view.
|
|
10060
10126
|
for (const [name, value] of view.tpl.views.get(scope.view).contextVariables) {
|
|
10061
|
-
newOps.push(createVariableOp(view.tpl.allocateXrefId(),
|
|
10062
|
-
kind: SemanticVariableKind.Identifier,
|
|
10063
|
-
name,
|
|
10064
|
-
}, new ReadPropExpr(new ContextExpr(view.xref), value)));
|
|
10127
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), scope.contextVariables.get(name), new ReadPropExpr(new ContextExpr(scope.view), value)));
|
|
10065
10128
|
}
|
|
10066
10129
|
// Add variables for all local references declared for elements in this scope.
|
|
10067
10130
|
for (const ref of scope.references) {
|
|
10068
|
-
newOps.push(createVariableOp(view.tpl.allocateXrefId(),
|
|
10069
|
-
kind: SemanticVariableKind.Identifier,
|
|
10070
|
-
name: ref.name,
|
|
10071
|
-
}, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
10131
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
10072
10132
|
}
|
|
10073
10133
|
if (scope.parent !== null) {
|
|
10074
10134
|
// Recursively add variables from the parent scope.
|
|
@@ -10106,10 +10166,10 @@ function processLexicalScope$1(view, ops, savedView) {
|
|
|
10106
10166
|
switch (op.variable.kind) {
|
|
10107
10167
|
case SemanticVariableKind.Identifier:
|
|
10108
10168
|
// This variable represents some kind of identifier which can be used in the template.
|
|
10109
|
-
if (scope.has(op.variable.
|
|
10169
|
+
if (scope.has(op.variable.identifier)) {
|
|
10110
10170
|
continue;
|
|
10111
10171
|
}
|
|
10112
|
-
scope.set(op.variable.
|
|
10172
|
+
scope.set(op.variable.identifier, op.xref);
|
|
10113
10173
|
break;
|
|
10114
10174
|
case SemanticVariableKind.SavedView:
|
|
10115
10175
|
// This variable represents a snapshot of the current view context, and can be used to
|
|
@@ -10159,7 +10219,7 @@ function processLexicalScope$1(view, ops, savedView) {
|
|
|
10159
10219
|
else {
|
|
10160
10220
|
return expr;
|
|
10161
10221
|
}
|
|
10162
|
-
});
|
|
10222
|
+
}, VisitorContextFlag.None);
|
|
10163
10223
|
}
|
|
10164
10224
|
}
|
|
10165
10225
|
|
|
@@ -10205,7 +10265,486 @@ function processLexicalScope(view, ops) {
|
|
|
10205
10265
|
else {
|
|
10206
10266
|
return expr;
|
|
10207
10267
|
}
|
|
10208
|
-
});
|
|
10268
|
+
}, VisitorContextFlag.None);
|
|
10269
|
+
}
|
|
10270
|
+
}
|
|
10271
|
+
|
|
10272
|
+
/**
|
|
10273
|
+
* Optimize variables declared and used in the IR.
|
|
10274
|
+
*
|
|
10275
|
+
* Variables are eagerly generated by pipeline stages for all possible values that could be
|
|
10276
|
+
* referenced. This stage processes the list of declared variables and all variable usages,
|
|
10277
|
+
* and optimizes where possible. It performs 3 main optimizations:
|
|
10278
|
+
*
|
|
10279
|
+
* * It transforms variable declarations to side effectful expressions when the
|
|
10280
|
+
* variable is not used, but its initializer has global effects which other
|
|
10281
|
+
* operations rely upon.
|
|
10282
|
+
* * It removes variable declarations if those variables are not referenced and
|
|
10283
|
+
* either they do not have global effects, or nothing relies on them.
|
|
10284
|
+
* * It inlines variable declarations when those variables are only used once
|
|
10285
|
+
* and the inlining is semantically safe.
|
|
10286
|
+
*
|
|
10287
|
+
* To guarantee correctness, analysis of "fences" in the instruction lists is used to determine
|
|
10288
|
+
* which optimizations are safe to perform.
|
|
10289
|
+
*/
|
|
10290
|
+
function phaseVariableOptimization(cpl, options) {
|
|
10291
|
+
for (const [_, view] of cpl.views) {
|
|
10292
|
+
optimizeVariablesInOpList(view.create, options);
|
|
10293
|
+
optimizeVariablesInOpList(view.update, options);
|
|
10294
|
+
for (const op of view.create) {
|
|
10295
|
+
if (op.kind === OpKind.Listener) {
|
|
10296
|
+
optimizeVariablesInOpList(op.handlerOps, options);
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
}
|
|
10300
|
+
}
|
|
10301
|
+
/**
|
|
10302
|
+
* A [fence](https://en.wikipedia.org/wiki/Memory_barrier) flag for an expression which indicates
|
|
10303
|
+
* how that expression can be optimized in relation to other expressions or instructions.
|
|
10304
|
+
*
|
|
10305
|
+
* `Fence`s are a bitfield, so multiple flags may be set on a single expression.
|
|
10306
|
+
*/
|
|
10307
|
+
var Fence;
|
|
10308
|
+
(function (Fence) {
|
|
10309
|
+
/**
|
|
10310
|
+
* Empty flag (no fence exists).
|
|
10311
|
+
*/
|
|
10312
|
+
Fence[Fence["None"] = 0] = "None";
|
|
10313
|
+
/**
|
|
10314
|
+
* A context read fence, meaning that the expression in question reads from the "current view"
|
|
10315
|
+
* context of the runtime.
|
|
10316
|
+
*/
|
|
10317
|
+
Fence[Fence["ViewContextRead"] = 1] = "ViewContextRead";
|
|
10318
|
+
/**
|
|
10319
|
+
* A context write fence, meaning that the expression in question writes to the "current view"
|
|
10320
|
+
* context of the runtime.
|
|
10321
|
+
*
|
|
10322
|
+
* Note that all `ContextWrite` fences are implicitly `ContextRead` fences as operations which
|
|
10323
|
+
* change the view context do so based on the current one.
|
|
10324
|
+
*/
|
|
10325
|
+
Fence[Fence["ViewContextWrite"] = 3] = "ViewContextWrite";
|
|
10326
|
+
/**
|
|
10327
|
+
* Indicates that a call is required for its side-effects, even if nothing reads its result.
|
|
10328
|
+
*
|
|
10329
|
+
* This is also true of `ViewContextWrite` operations **if** they are followed by a
|
|
10330
|
+
* `ViewContextRead`.
|
|
10331
|
+
*/
|
|
10332
|
+
Fence[Fence["SideEffectful"] = 4] = "SideEffectful";
|
|
10333
|
+
})(Fence || (Fence = {}));
|
|
10334
|
+
/**
|
|
10335
|
+
* Process a list of operations and optimize variables within that list.
|
|
10336
|
+
*/
|
|
10337
|
+
function optimizeVariablesInOpList(ops, options) {
|
|
10338
|
+
const varDecls = new Map();
|
|
10339
|
+
const varUsages = new Map();
|
|
10340
|
+
// Track variables that are used outside of the immediate operation list. For example, within
|
|
10341
|
+
// `ListenerOp` handler operations of listeners in the current operation list.
|
|
10342
|
+
const varRemoteUsages = new Set();
|
|
10343
|
+
const opMap = new Map();
|
|
10344
|
+
// First, extract information about variables declared or used within the whole list.
|
|
10345
|
+
for (const op of ops) {
|
|
10346
|
+
if (op.kind === OpKind.Variable) {
|
|
10347
|
+
if (varDecls.has(op.xref) || varUsages.has(op.xref)) {
|
|
10348
|
+
throw new Error(`Should not see two declarations of the same variable: ${op.xref}`);
|
|
10349
|
+
}
|
|
10350
|
+
varDecls.set(op.xref, op);
|
|
10351
|
+
varUsages.set(op.xref, 0);
|
|
10352
|
+
}
|
|
10353
|
+
opMap.set(op, collectOpInfo(op));
|
|
10354
|
+
countVariableUsages(op, varUsages, varRemoteUsages);
|
|
10355
|
+
}
|
|
10356
|
+
// The next step is to remove any variable declarations for variables that aren't used. The
|
|
10357
|
+
// variable initializer expressions may be side-effectful, so they may need to be retained as
|
|
10358
|
+
// expression statements.
|
|
10359
|
+
// Track whether we've seen an operation which reads from the view context yet. This is used to
|
|
10360
|
+
// determine whether a write to the view context in a variable initializer can be observed.
|
|
10361
|
+
let contextIsUsed = false;
|
|
10362
|
+
// Note that iteration through the list happens in reverse, which guarantees that we'll process
|
|
10363
|
+
// all reads of a variable prior to processing its declaration.
|
|
10364
|
+
for (const op of ops.reversed()) {
|
|
10365
|
+
const opInfo = opMap.get(op);
|
|
10366
|
+
if (op.kind === OpKind.Variable && varUsages.get(op.xref) === 0) {
|
|
10367
|
+
// This variable is unused and can be removed. We might need to keep the initializer around,
|
|
10368
|
+
// though, if something depends on it running.
|
|
10369
|
+
if ((contextIsUsed && opInfo.fences & Fence.ViewContextWrite) ||
|
|
10370
|
+
(opInfo.fences & Fence.SideEffectful)) {
|
|
10371
|
+
// This variable initializer has a side effect which must be retained. Either:
|
|
10372
|
+
// * it writes to the view context, and we know there is a future operation which depends
|
|
10373
|
+
// on that write, or
|
|
10374
|
+
// * it's an operation which is inherently side-effectful.
|
|
10375
|
+
// We can't remove the initializer, but we can remove the variable declaration itself and
|
|
10376
|
+
// replace it with a side-effectful statement.
|
|
10377
|
+
const stmtOp = createStatementOp(op.initializer.toStmt());
|
|
10378
|
+
opMap.set(stmtOp, opInfo);
|
|
10379
|
+
OpList.replace(op, stmtOp);
|
|
10380
|
+
}
|
|
10381
|
+
else {
|
|
10382
|
+
// It's safe to delete this entire variable declaration as nothing depends on it, even
|
|
10383
|
+
// side-effectfully. Note that doing this might make other variables unused. Since we're
|
|
10384
|
+
// iterating in reverse order, we should always be processing usages before declarations
|
|
10385
|
+
// and therefore by the time we get to a declaration, all removable usages will have been
|
|
10386
|
+
// removed.
|
|
10387
|
+
uncountVariableUsages(op, varUsages);
|
|
10388
|
+
OpList.remove(op);
|
|
10389
|
+
}
|
|
10390
|
+
opMap.delete(op);
|
|
10391
|
+
varDecls.delete(op.xref);
|
|
10392
|
+
varUsages.delete(op.xref);
|
|
10393
|
+
continue;
|
|
10394
|
+
}
|
|
10395
|
+
// Does this operation depend on the view context?
|
|
10396
|
+
if (opInfo.fences & Fence.ViewContextRead) {
|
|
10397
|
+
contextIsUsed = true;
|
|
10398
|
+
}
|
|
10399
|
+
}
|
|
10400
|
+
// Next, inline any remaining variables with exactly one usage.
|
|
10401
|
+
const toInline = [];
|
|
10402
|
+
for (const [id, count] of varUsages) {
|
|
10403
|
+
// We can inline variables that:
|
|
10404
|
+
// - are used once
|
|
10405
|
+
// - are not used remotely
|
|
10406
|
+
if (count !== 1) {
|
|
10407
|
+
// We can't inline this variable as it's used more than once.
|
|
10408
|
+
continue;
|
|
10409
|
+
}
|
|
10410
|
+
if (varRemoteUsages.has(id)) {
|
|
10411
|
+
// This variable is used once, but across an operation boundary, so it can't be inlined.
|
|
10412
|
+
continue;
|
|
10413
|
+
}
|
|
10414
|
+
toInline.push(id);
|
|
10415
|
+
}
|
|
10416
|
+
let candidate;
|
|
10417
|
+
while (candidate = toInline.pop()) {
|
|
10418
|
+
// We will attempt to inline this variable. If inlining fails (due to fences for example),
|
|
10419
|
+
// no future operation will make inlining legal.
|
|
10420
|
+
const decl = varDecls.get(candidate);
|
|
10421
|
+
const varInfo = opMap.get(decl);
|
|
10422
|
+
// Scan operations following the variable declaration and look for the point where that variable
|
|
10423
|
+
// is used. There should only be one usage given the precondition above.
|
|
10424
|
+
for (let targetOp = decl.next; targetOp.kind !== OpKind.ListEnd; targetOp = targetOp.next) {
|
|
10425
|
+
const opInfo = opMap.get(targetOp);
|
|
10426
|
+
// Is the variable used in this operation?
|
|
10427
|
+
if (opInfo.variablesUsed.has(candidate)) {
|
|
10428
|
+
if (options.conservative && !allowConservativeInlining(decl, targetOp)) {
|
|
10429
|
+
// We're in conservative mode, and this variable is not eligible for inlining into the
|
|
10430
|
+
// target operation in this mode.
|
|
10431
|
+
break;
|
|
10432
|
+
}
|
|
10433
|
+
// Yes, try to inline it. Inlining may not be successful if fences in this operation before
|
|
10434
|
+
// the variable's usage cannot be safely crossed.
|
|
10435
|
+
if (tryInlineVariableInitializer(candidate, decl.initializer, targetOp, varInfo.fences)) {
|
|
10436
|
+
// Inlining was successful! Update the tracking structures to reflect the inlined
|
|
10437
|
+
// variable.
|
|
10438
|
+
opInfo.variablesUsed.delete(candidate);
|
|
10439
|
+
// Add all variables used in the variable's initializer to its new usage site.
|
|
10440
|
+
for (const id of varInfo.variablesUsed) {
|
|
10441
|
+
opInfo.variablesUsed.add(id);
|
|
10442
|
+
}
|
|
10443
|
+
// Merge fences in the variable's initializer into its new usage site.
|
|
10444
|
+
opInfo.fences |= varInfo.fences;
|
|
10445
|
+
// Delete tracking info related to the declaration.
|
|
10446
|
+
varDecls.delete(candidate);
|
|
10447
|
+
varUsages.delete(candidate);
|
|
10448
|
+
opMap.delete(decl);
|
|
10449
|
+
// And finally, delete the original declaration from the operation list.
|
|
10450
|
+
OpList.remove(decl);
|
|
10451
|
+
}
|
|
10452
|
+
// Whether inlining succeeded or failed, we're done processing this variable.
|
|
10453
|
+
break;
|
|
10454
|
+
}
|
|
10455
|
+
// If the variable is not used in this operation, then we'd need to inline across it. Check if
|
|
10456
|
+
// that's safe to do.
|
|
10457
|
+
if (!safeToInlinePastFences(opInfo.fences, varInfo.fences)) {
|
|
10458
|
+
// We can't safely inline this variable beyond this operation, so don't proceed with
|
|
10459
|
+
// inlining this variable.
|
|
10460
|
+
break;
|
|
10461
|
+
}
|
|
10462
|
+
}
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10465
|
+
/**
|
|
10466
|
+
* Given an `ir.Expression`, returns the `Fence` flags for that expression type.
|
|
10467
|
+
*/
|
|
10468
|
+
function fencesForIrExpression(expr) {
|
|
10469
|
+
switch (expr.kind) {
|
|
10470
|
+
case ExpressionKind.NextContext:
|
|
10471
|
+
return Fence.ViewContextWrite;
|
|
10472
|
+
case ExpressionKind.RestoreView:
|
|
10473
|
+
return Fence.ViewContextWrite | Fence.SideEffectful;
|
|
10474
|
+
case ExpressionKind.Reference:
|
|
10475
|
+
return Fence.ViewContextRead;
|
|
10476
|
+
default:
|
|
10477
|
+
return Fence.None;
|
|
10478
|
+
}
|
|
10479
|
+
}
|
|
10480
|
+
/**
|
|
10481
|
+
* Build the `OpInfo` structure for the given `op`. This performs two operations:
|
|
10482
|
+
*
|
|
10483
|
+
* * It tracks which variables are used in the operation's expressions.
|
|
10484
|
+
* * It rolls up fence flags for expressions within the operation.
|
|
10485
|
+
*/
|
|
10486
|
+
function collectOpInfo(op) {
|
|
10487
|
+
let fences = Fence.None;
|
|
10488
|
+
const variablesUsed = new Set();
|
|
10489
|
+
visitExpressionsInOp(op, expr => {
|
|
10490
|
+
switch (expr.kind) {
|
|
10491
|
+
case ExpressionKind.ReadVariable:
|
|
10492
|
+
variablesUsed.add(expr.xref);
|
|
10493
|
+
break;
|
|
10494
|
+
default:
|
|
10495
|
+
fences |= fencesForIrExpression(expr);
|
|
10496
|
+
}
|
|
10497
|
+
});
|
|
10498
|
+
return { fences, variablesUsed };
|
|
10499
|
+
}
|
|
10500
|
+
/**
|
|
10501
|
+
* Count the number of usages of each variable, being careful to track whether those usages are
|
|
10502
|
+
* local or remote.
|
|
10503
|
+
*/
|
|
10504
|
+
function countVariableUsages(op, varUsages, varRemoteUsage) {
|
|
10505
|
+
visitExpressionsInOp(op, (expr, flags) => {
|
|
10506
|
+
if (expr.kind !== ExpressionKind.ReadVariable) {
|
|
10507
|
+
return;
|
|
10508
|
+
}
|
|
10509
|
+
const count = varUsages.get(expr.xref);
|
|
10510
|
+
if (count === undefined) {
|
|
10511
|
+
// This variable is declared outside the current scope of optimization.
|
|
10512
|
+
return;
|
|
10513
|
+
}
|
|
10514
|
+
varUsages.set(expr.xref, count + 1);
|
|
10515
|
+
if (flags & VisitorContextFlag.InChildOperation) {
|
|
10516
|
+
varRemoteUsage.add(expr.xref);
|
|
10517
|
+
}
|
|
10518
|
+
});
|
|
10519
|
+
}
|
|
10520
|
+
/**
|
|
10521
|
+
* Remove usages of a variable in `op` from the `varUsages` tracking.
|
|
10522
|
+
*/
|
|
10523
|
+
function uncountVariableUsages(op, varUsages) {
|
|
10524
|
+
visitExpressionsInOp(op, expr => {
|
|
10525
|
+
if (expr.kind !== ExpressionKind.ReadVariable) {
|
|
10526
|
+
return;
|
|
10527
|
+
}
|
|
10528
|
+
const count = varUsages.get(expr.xref);
|
|
10529
|
+
if (count === undefined) {
|
|
10530
|
+
// This variable is declared outside the current scope of optimization.
|
|
10531
|
+
return;
|
|
10532
|
+
}
|
|
10533
|
+
else if (count === 0) {
|
|
10534
|
+
throw new Error(`Inaccurate variable count: ${expr.xref} - found another read but count is already 0`);
|
|
10535
|
+
}
|
|
10536
|
+
varUsages.set(expr.xref, count - 1);
|
|
10537
|
+
});
|
|
10538
|
+
}
|
|
10539
|
+
/**
|
|
10540
|
+
* Checks whether it's safe to inline a variable across a particular operation.
|
|
10541
|
+
*
|
|
10542
|
+
* @param fences the fences of the operation which the inlining will cross
|
|
10543
|
+
* @param declFences the fences of the variable being inlined.
|
|
10544
|
+
*/
|
|
10545
|
+
function safeToInlinePastFences(fences, declFences) {
|
|
10546
|
+
if (fences & Fence.ViewContextWrite) {
|
|
10547
|
+
// It's not safe to inline context reads across context writes.
|
|
10548
|
+
if (declFences & Fence.ViewContextRead) {
|
|
10549
|
+
return false;
|
|
10550
|
+
}
|
|
10551
|
+
}
|
|
10552
|
+
else if (fences & Fence.ViewContextRead) {
|
|
10553
|
+
// It's not safe to inline context writes across context reads.
|
|
10554
|
+
if (declFences & Fence.ViewContextWrite) {
|
|
10555
|
+
return false;
|
|
10556
|
+
}
|
|
10557
|
+
}
|
|
10558
|
+
return true;
|
|
10559
|
+
}
|
|
10560
|
+
/**
|
|
10561
|
+
* Attempt to inline the initializer of a variable into a target operation's expressions.
|
|
10562
|
+
*
|
|
10563
|
+
* This may or may not be safe to do. For example, the variable could be read following the
|
|
10564
|
+
* execution of an expression with fences that don't permit the variable to be inlined across them.
|
|
10565
|
+
*/
|
|
10566
|
+
function tryInlineVariableInitializer(id, initializer, target, declFences) {
|
|
10567
|
+
// We use `ir.transformExpressionsInOp` to walk the expressions and inline the variable if
|
|
10568
|
+
// possible. Since this operation is callback-based, once inlining succeeds or fails we can't
|
|
10569
|
+
// "stop" the expression processing, and have to keep track of whether inlining has succeeded or
|
|
10570
|
+
// is no longer allowed.
|
|
10571
|
+
let inlined = false;
|
|
10572
|
+
let inliningAllowed = true;
|
|
10573
|
+
transformExpressionsInOp(target, (expr, flags) => {
|
|
10574
|
+
if (inlined || !inliningAllowed) {
|
|
10575
|
+
// Either the inlining has already succeeded, or we've passed a fence that disallows inlining
|
|
10576
|
+
// at this point, so don't try.
|
|
10577
|
+
return expr;
|
|
10578
|
+
}
|
|
10579
|
+
else if ((flags & VisitorContextFlag.InChildOperation) && (declFences & Fence.ViewContextRead)) {
|
|
10580
|
+
// We cannot inline variables that are sensitive to the current context across operation
|
|
10581
|
+
// boundaries.
|
|
10582
|
+
return expr;
|
|
10583
|
+
}
|
|
10584
|
+
switch (expr.kind) {
|
|
10585
|
+
case ExpressionKind.ReadVariable:
|
|
10586
|
+
if (expr.xref === id) {
|
|
10587
|
+
// This is the usage site of the variable. Since nothing has disallowed inlining, it's
|
|
10588
|
+
// safe to inline the initializer here.
|
|
10589
|
+
inlined = true;
|
|
10590
|
+
return initializer;
|
|
10591
|
+
}
|
|
10592
|
+
break;
|
|
10593
|
+
default:
|
|
10594
|
+
// For other types of `ir.Expression`s, whether inlining is allowed depends on their fences.
|
|
10595
|
+
const exprFences = fencesForIrExpression(expr);
|
|
10596
|
+
inliningAllowed = inliningAllowed && safeToInlinePastFences(exprFences, declFences);
|
|
10597
|
+
break;
|
|
10598
|
+
}
|
|
10599
|
+
return expr;
|
|
10600
|
+
}, VisitorContextFlag.None);
|
|
10601
|
+
return inlined;
|
|
10602
|
+
}
|
|
10603
|
+
/**
|
|
10604
|
+
* Determines whether inlining of `decl` should be allowed in "conservative" mode.
|
|
10605
|
+
*
|
|
10606
|
+
* In conservative mode, inlining behavior is limited to those operations which the
|
|
10607
|
+
* `TemplateDefinitionBuilder` supported, with the goal of producing equivalent output.
|
|
10608
|
+
*/
|
|
10609
|
+
function allowConservativeInlining(decl, target) {
|
|
10610
|
+
// TODO(alxhub): understand exactly how TemplateDefinitionBuilder approaches inlining, and record
|
|
10611
|
+
// that behavior here.
|
|
10612
|
+
switch (decl.variable.kind) {
|
|
10613
|
+
case SemanticVariableKind.Identifier:
|
|
10614
|
+
return false;
|
|
10615
|
+
case SemanticVariableKind.Context:
|
|
10616
|
+
// Context can only be inlined into other variables.
|
|
10617
|
+
return target.kind === OpKind.Variable;
|
|
10618
|
+
default:
|
|
10619
|
+
return true;
|
|
10620
|
+
}
|
|
10621
|
+
}
|
|
10622
|
+
|
|
10623
|
+
const CHAINABLE = new Set([
|
|
10624
|
+
Identifiers.elementStart,
|
|
10625
|
+
Identifiers.elementEnd,
|
|
10626
|
+
Identifiers.property,
|
|
10627
|
+
]);
|
|
10628
|
+
/**
|
|
10629
|
+
* Post-process a reified view compilation and convert sequential calls to chainable instructions
|
|
10630
|
+
* into chain calls.
|
|
10631
|
+
*
|
|
10632
|
+
* For example, two `elementStart` operations in sequence:
|
|
10633
|
+
*
|
|
10634
|
+
* ```typescript
|
|
10635
|
+
* elementStart(0, 'div');
|
|
10636
|
+
* elementStart(1, 'span');
|
|
10637
|
+
* ```
|
|
10638
|
+
*
|
|
10639
|
+
* Can be called as a chain instead:
|
|
10640
|
+
*
|
|
10641
|
+
* ```typescript
|
|
10642
|
+
* elementStart(0, 'div')(1, 'span');
|
|
10643
|
+
* ```
|
|
10644
|
+
*/
|
|
10645
|
+
function phaseChaining(cpl) {
|
|
10646
|
+
for (const [_, view] of cpl.views) {
|
|
10647
|
+
chainOperationsInList(view.create);
|
|
10648
|
+
chainOperationsInList(view.update);
|
|
10649
|
+
}
|
|
10650
|
+
}
|
|
10651
|
+
function chainOperationsInList(opList) {
|
|
10652
|
+
let chain = null;
|
|
10653
|
+
for (const op of opList) {
|
|
10654
|
+
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement)) {
|
|
10655
|
+
// This type of statement isn't chainable.
|
|
10656
|
+
chain = null;
|
|
10657
|
+
continue;
|
|
10658
|
+
}
|
|
10659
|
+
if (!(op.statement.expr instanceof InvokeFunctionExpr) ||
|
|
10660
|
+
!(op.statement.expr.fn instanceof ExternalExpr)) {
|
|
10661
|
+
// This is a statement, but not an instruction-type call, so not chainable.
|
|
10662
|
+
chain = null;
|
|
10663
|
+
continue;
|
|
10664
|
+
}
|
|
10665
|
+
const instruction = op.statement.expr.fn.value;
|
|
10666
|
+
if (!CHAINABLE.has(instruction)) {
|
|
10667
|
+
// This instruction isn't chainable.
|
|
10668
|
+
chain = null;
|
|
10669
|
+
continue;
|
|
10670
|
+
}
|
|
10671
|
+
// This instruction can be chained. It can either be added on to the previous chain (if
|
|
10672
|
+
// compatible) or it can be the start of a new chain.
|
|
10673
|
+
if (chain !== null && chain.instruction === instruction) {
|
|
10674
|
+
// This instruction can be added onto the previous chain.
|
|
10675
|
+
const expression = chain.expression.callFn(op.statement.expr.args, op.statement.expr.sourceSpan, op.statement.expr.pure);
|
|
10676
|
+
chain.expression = expression;
|
|
10677
|
+
chain.op.statement = expression.toStmt();
|
|
10678
|
+
OpList.remove(op);
|
|
10679
|
+
}
|
|
10680
|
+
else {
|
|
10681
|
+
// Leave this instruction alone for now, but consider it the start of a new chain.
|
|
10682
|
+
chain = {
|
|
10683
|
+
op,
|
|
10684
|
+
instruction,
|
|
10685
|
+
expression: op.statement.expr,
|
|
10686
|
+
};
|
|
10687
|
+
}
|
|
10688
|
+
}
|
|
10689
|
+
}
|
|
10690
|
+
|
|
10691
|
+
/**
|
|
10692
|
+
* Merges logically sequential `NextContextExpr` operations.
|
|
10693
|
+
*
|
|
10694
|
+
* `NextContextExpr` can be referenced repeatedly, "popping" the runtime's context stack each time.
|
|
10695
|
+
* When two such expressions appear back-to-back, it's possible to merge them together into a single
|
|
10696
|
+
* `NextContextExpr` that steps multiple contexts. This merging is possible if all conditions are
|
|
10697
|
+
* met:
|
|
10698
|
+
*
|
|
10699
|
+
* * The result of the `NextContextExpr` that's folded into the subsequent one is not stored (that
|
|
10700
|
+
* is, the call is purely side-effectful).
|
|
10701
|
+
* * No operations in between them uses the implicit context.
|
|
10702
|
+
*/
|
|
10703
|
+
function phaseMergeNextContext(cpl) {
|
|
10704
|
+
for (const view of cpl.views.values()) {
|
|
10705
|
+
for (const op of view.create) {
|
|
10706
|
+
if (op.kind === OpKind.Listener) {
|
|
10707
|
+
mergeNextContextsInOps(op.handlerOps);
|
|
10708
|
+
}
|
|
10709
|
+
}
|
|
10710
|
+
mergeNextContextsInOps(view.update);
|
|
10711
|
+
}
|
|
10712
|
+
}
|
|
10713
|
+
function mergeNextContextsInOps(ops) {
|
|
10714
|
+
for (const op of ops) {
|
|
10715
|
+
// Look for a candidate operation to maybe merge.
|
|
10716
|
+
if (op.kind !== OpKind.Statement || !(op.statement instanceof ExpressionStatement) ||
|
|
10717
|
+
!(op.statement.expr instanceof NextContextExpr)) {
|
|
10718
|
+
continue;
|
|
10719
|
+
}
|
|
10720
|
+
const mergeSteps = op.statement.expr.steps;
|
|
10721
|
+
// Try to merge this `ir.NextContextExpr`.
|
|
10722
|
+
let tryToMerge = true;
|
|
10723
|
+
for (let candidate = op.next; candidate.kind !== OpKind.ListEnd && tryToMerge; candidate = candidate.next) {
|
|
10724
|
+
visitExpressionsInOp(candidate, (expr, flags) => {
|
|
10725
|
+
if (!tryToMerge) {
|
|
10726
|
+
// Either we've already merged, or failed to merge.
|
|
10727
|
+
return;
|
|
10728
|
+
}
|
|
10729
|
+
if (flags & VisitorContextFlag.InChildOperation) {
|
|
10730
|
+
// We cannot merge into child operations.
|
|
10731
|
+
return;
|
|
10732
|
+
}
|
|
10733
|
+
switch (expr.kind) {
|
|
10734
|
+
case ExpressionKind.NextContext:
|
|
10735
|
+
// Merge the previous `ir.NextContextExpr` into this one.
|
|
10736
|
+
expr.steps += mergeSteps;
|
|
10737
|
+
OpList.remove(op);
|
|
10738
|
+
tryToMerge = false;
|
|
10739
|
+
break;
|
|
10740
|
+
case ExpressionKind.GetCurrentView:
|
|
10741
|
+
case ExpressionKind.Reference:
|
|
10742
|
+
// Can't merge past a dependency on the context.
|
|
10743
|
+
tryToMerge = false;
|
|
10744
|
+
break;
|
|
10745
|
+
}
|
|
10746
|
+
});
|
|
10747
|
+
}
|
|
10209
10748
|
}
|
|
10210
10749
|
}
|
|
10211
10750
|
|
|
@@ -10224,7 +10763,10 @@ function transformTemplate(cpl) {
|
|
|
10224
10763
|
phaseVarCounting(cpl);
|
|
10225
10764
|
phaseGenerateAdvance(cpl);
|
|
10226
10765
|
phaseNaming(cpl);
|
|
10766
|
+
phaseVariableOptimization(cpl, { conservative: true });
|
|
10767
|
+
phaseMergeNextContext(cpl);
|
|
10227
10768
|
phaseReify(cpl);
|
|
10769
|
+
phaseChaining(cpl);
|
|
10228
10770
|
}
|
|
10229
10771
|
/**
|
|
10230
10772
|
* Compile all views in the given `ComponentCompilation` into the final template function, which may
|
|
@@ -10232,14 +10774,19 @@ function transformTemplate(cpl) {
|
|
|
10232
10774
|
*/
|
|
10233
10775
|
function emitTemplateFn(tpl, pool) {
|
|
10234
10776
|
const rootFn = emitView(tpl.root);
|
|
10235
|
-
|
|
10236
|
-
|
|
10777
|
+
emitChildViews(tpl.root, pool);
|
|
10778
|
+
return rootFn;
|
|
10779
|
+
}
|
|
10780
|
+
function emitChildViews(parent, pool) {
|
|
10781
|
+
for (const view of parent.tpl.views.values()) {
|
|
10782
|
+
if (view.parent !== parent.xref) {
|
|
10237
10783
|
continue;
|
|
10238
10784
|
}
|
|
10785
|
+
// Child views are emitted depth-first.
|
|
10786
|
+
emitChildViews(view, pool);
|
|
10239
10787
|
const viewFn = emitView(view);
|
|
10240
10788
|
pool.statements.push(viewFn.toDeclStmt(viewFn.name));
|
|
10241
10789
|
}
|
|
10242
|
-
return rootFn;
|
|
10243
10790
|
}
|
|
10244
10791
|
/**
|
|
10245
10792
|
* Emit a template function for an individual `ViewCompilation` (which may be either the root view
|
|
@@ -10263,13 +10810,24 @@ function emitView(view) {
|
|
|
10263
10810
|
}
|
|
10264
10811
|
updateStatements.push(op.statement);
|
|
10265
10812
|
}
|
|
10266
|
-
const
|
|
10267
|
-
const
|
|
10268
|
-
const updateCond = ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, rf, literal(2)), updateStatements);
|
|
10813
|
+
const createCond = maybeGenerateRfBlock(1, createStatements);
|
|
10814
|
+
const updateCond = maybeGenerateRfBlock(2, updateStatements);
|
|
10269
10815
|
return fn([
|
|
10270
10816
|
new FnParam('rf'),
|
|
10271
10817
|
new FnParam('ctx'),
|
|
10272
|
-
], [
|
|
10818
|
+
], [
|
|
10819
|
+
...createCond,
|
|
10820
|
+
...updateCond,
|
|
10821
|
+
],
|
|
10822
|
+
/* type */ undefined, /* sourceSpan */ undefined, view.fnName);
|
|
10823
|
+
}
|
|
10824
|
+
function maybeGenerateRfBlock(flag, statements) {
|
|
10825
|
+
if (statements.length === 0) {
|
|
10826
|
+
return [];
|
|
10827
|
+
}
|
|
10828
|
+
return [
|
|
10829
|
+
ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, variable('rf'), literal(flag)), statements),
|
|
10830
|
+
];
|
|
10273
10831
|
}
|
|
10274
10832
|
|
|
10275
10833
|
/**
|
|
@@ -10469,21 +11027,21 @@ function ingestBoundText(view, text) {
|
|
|
10469
11027
|
}
|
|
10470
11028
|
const textXref = view.tpl.allocateXrefId();
|
|
10471
11029
|
view.create.push(createTextOp(textXref, ''));
|
|
10472
|
-
view.update.push(createInterpolateTextOp(textXref, value.strings, value.expressions.map(expr => convertAst(expr))));
|
|
11030
|
+
view.update.push(createInterpolateTextOp(textXref, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl))));
|
|
10473
11031
|
}
|
|
10474
11032
|
/**
|
|
10475
11033
|
* Convert a template AST expression into an output AST expression.
|
|
10476
11034
|
*/
|
|
10477
|
-
function convertAst(ast) {
|
|
11035
|
+
function convertAst(ast, cpl) {
|
|
10478
11036
|
if (ast instanceof ASTWithSource) {
|
|
10479
|
-
return convertAst(ast.ast);
|
|
11037
|
+
return convertAst(ast.ast, cpl);
|
|
10480
11038
|
}
|
|
10481
11039
|
else if (ast instanceof PropertyRead) {
|
|
10482
11040
|
if (ast.receiver instanceof ImplicitReceiver) {
|
|
10483
11041
|
return new LexicalReadExpr(ast.name);
|
|
10484
11042
|
}
|
|
10485
11043
|
else {
|
|
10486
|
-
return new ReadPropExpr(convertAst(ast.receiver), ast.name);
|
|
11044
|
+
return new ReadPropExpr(convertAst(ast.receiver, cpl), ast.name);
|
|
10487
11045
|
}
|
|
10488
11046
|
}
|
|
10489
11047
|
else if (ast instanceof Call) {
|
|
@@ -10491,9 +11049,15 @@ function convertAst(ast) {
|
|
|
10491
11049
|
throw new Error(`Unexpected ImplicitReceiver`);
|
|
10492
11050
|
}
|
|
10493
11051
|
else {
|
|
10494
|
-
return new InvokeFunctionExpr(convertAst(ast.receiver), ast.args.map(arg => convertAst(arg)));
|
|
11052
|
+
return new InvokeFunctionExpr(convertAst(ast.receiver, cpl), ast.args.map(arg => convertAst(arg, cpl)));
|
|
10495
11053
|
}
|
|
10496
11054
|
}
|
|
11055
|
+
else if (ast instanceof LiteralPrimitive) {
|
|
11056
|
+
return literal(ast.value);
|
|
11057
|
+
}
|
|
11058
|
+
else if (ast instanceof ThisReceiver) {
|
|
11059
|
+
return new ContextExpr(cpl.root.xref);
|
|
11060
|
+
}
|
|
10497
11061
|
else {
|
|
10498
11062
|
throw new Error(`Unhandled expression type: ${ast.constructor.name}`);
|
|
10499
11063
|
}
|
|
@@ -10528,20 +11092,20 @@ function ingestBindings(view, op, element) {
|
|
|
10528
11092
|
if (element instanceof Template) {
|
|
10529
11093
|
for (const attr of element.templateAttrs) {
|
|
10530
11094
|
if (typeof attr.value === 'string') {
|
|
10531
|
-
|
|
11095
|
+
// TODO: do we need to handle static attribute bindings here?
|
|
10532
11096
|
}
|
|
10533
11097
|
else {
|
|
10534
|
-
view.update.push(createPropertyOp(op.xref, attr.name, convertAst(attr.value)));
|
|
11098
|
+
view.update.push(createPropertyOp(op.xref, attr.name, convertAst(attr.value, view.tpl)));
|
|
10535
11099
|
}
|
|
10536
11100
|
}
|
|
10537
11101
|
}
|
|
10538
11102
|
else {
|
|
10539
11103
|
for (const input of element.inputs) {
|
|
10540
|
-
view.update.push(createPropertyOp(op.xref, input.name, convertAst(input.value)));
|
|
11104
|
+
view.update.push(createPropertyOp(op.xref, input.name, convertAst(input.value, view.tpl)));
|
|
10541
11105
|
}
|
|
10542
11106
|
for (const output of element.outputs) {
|
|
10543
|
-
const listenerOp = createListenerOp(op.xref, output.name);
|
|
10544
|
-
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(output.handler))));
|
|
11107
|
+
const listenerOp = createListenerOp(op.xref, output.name, op.tag);
|
|
11108
|
+
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(output.handler, view.tpl))));
|
|
10545
11109
|
view.create.push(listenerOp);
|
|
10546
11110
|
}
|
|
10547
11111
|
}
|
|
@@ -15601,8 +16165,9 @@ const NAMED_ENTITIES = {
|
|
|
15601
16165
|
'zwj': '\u200D',
|
|
15602
16166
|
'zwnj': '\u200C'
|
|
15603
16167
|
};
|
|
15604
|
-
// The &ngsp; pseudo-entity is denoting a space.
|
|
15605
|
-
//
|
|
16168
|
+
// The &ngsp; pseudo-entity is denoting a space.
|
|
16169
|
+
// 0xE500 is a PUA (Private Use Areas) unicode character
|
|
16170
|
+
// This is inspired by the Angular Dart implementation.
|
|
15606
16171
|
const NGSP_UNICODE = '\uE500';
|
|
15607
16172
|
NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
|
|
15608
16173
|
|
|
@@ -17006,10 +17571,9 @@ function hasPreserveWhitespacesAttr(attrs) {
|
|
|
17006
17571
|
return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
|
|
17007
17572
|
}
|
|
17008
17573
|
/**
|
|
17009
|
-
*
|
|
17010
|
-
*
|
|
17011
|
-
*
|
|
17012
|
-
* and later on replaced by a space. We are re-implementing the same idea here.
|
|
17574
|
+
* &ngsp; is a placeholder for non-removable space
|
|
17575
|
+
* &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
|
|
17576
|
+
* and later on replaced by a space.
|
|
17013
17577
|
*/
|
|
17014
17578
|
function replaceNgsp(value) {
|
|
17015
17579
|
// lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
|
|
@@ -20923,10 +21487,12 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
|
20923
21487
|
transformTemplate(tpl);
|
|
20924
21488
|
// Finally we emit the template function:
|
|
20925
21489
|
const templateFn = emitTemplateFn(tpl, constantPool);
|
|
20926
|
-
definitionMap.set('template', templateFn);
|
|
20927
21490
|
definitionMap.set('decls', literal(tpl.root.decls));
|
|
20928
21491
|
definitionMap.set('vars', literal(tpl.root.vars));
|
|
20929
|
-
|
|
21492
|
+
if (tpl.consts.length > 0) {
|
|
21493
|
+
definitionMap.set('consts', literalArr(tpl.consts));
|
|
21494
|
+
}
|
|
21495
|
+
definitionMap.set('template', templateFn);
|
|
20930
21496
|
}
|
|
20931
21497
|
if (meta.declarations.length > 0) {
|
|
20932
21498
|
definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode));
|
|
@@ -22059,7 +22625,7 @@ function publishFacade(global) {
|
|
|
22059
22625
|
* @description
|
|
22060
22626
|
* Entry point for all public APIs of the compiler package.
|
|
22061
22627
|
*/
|
|
22062
|
-
const VERSION = new Version('16.0.0-rc.
|
|
22628
|
+
const VERSION = new Version('16.0.0-rc.3');
|
|
22063
22629
|
|
|
22064
22630
|
class CompilerConfig {
|
|
22065
22631
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -22467,6 +23033,7 @@ function _parseMessageMeta(i18n) {
|
|
|
22467
23033
|
class XmlTagDefinition {
|
|
22468
23034
|
constructor() {
|
|
22469
23035
|
this.closedByParent = false;
|
|
23036
|
+
this.implicitNamespacePrefix = null;
|
|
22470
23037
|
this.isVoid = false;
|
|
22471
23038
|
this.ignoreFirstLf = false;
|
|
22472
23039
|
this.canSelfClose = true;
|
|
@@ -22808,6 +23375,9 @@ class Xliff2 extends Serializer {
|
|
|
22808
23375
|
}
|
|
22809
23376
|
}
|
|
22810
23377
|
class _WriteVisitor {
|
|
23378
|
+
constructor() {
|
|
23379
|
+
this._nextPlaceholderId = 0;
|
|
23380
|
+
}
|
|
22811
23381
|
visitText(text, context) {
|
|
22812
23382
|
return [new Text$1(text.value)];
|
|
22813
23383
|
}
|
|
@@ -23237,8 +23807,8 @@ class I18nToHtmlVisitor {
|
|
|
23237
23807
|
this._mapperFactory = _mapperFactory;
|
|
23238
23808
|
this._missingTranslationStrategy = _missingTranslationStrategy;
|
|
23239
23809
|
this._console = _console;
|
|
23240
|
-
this._contextStack = [];
|
|
23241
23810
|
this._errors = [];
|
|
23811
|
+
this._contextStack = [];
|
|
23242
23812
|
}
|
|
23243
23813
|
convert(srcMsg) {
|
|
23244
23814
|
this._contextStack.length = 0;
|
|
@@ -23983,7 +24553,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
23983
24553
|
function compileDeclareClassMetadata(metadata) {
|
|
23984
24554
|
const definitionMap = new DefinitionMap();
|
|
23985
24555
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
23986
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
24556
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
23987
24557
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
23988
24558
|
definitionMap.set('type', metadata.type);
|
|
23989
24559
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -24086,7 +24656,7 @@ function compileDeclareDirectiveFromMetadata(meta) {
|
|
|
24086
24656
|
function createDirectiveDefinitionMap(meta) {
|
|
24087
24657
|
const definitionMap = new DefinitionMap();
|
|
24088
24658
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
24089
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
24659
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
24090
24660
|
// e.g. `type: MyDirective`
|
|
24091
24661
|
definitionMap.set('type', meta.type.value);
|
|
24092
24662
|
if (meta.isStandalone) {
|
|
@@ -24311,7 +24881,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
24311
24881
|
function compileDeclareFactoryFunction(meta) {
|
|
24312
24882
|
const definitionMap = new DefinitionMap();
|
|
24313
24883
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
24314
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
24884
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
24315
24885
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
24316
24886
|
definitionMap.set('type', meta.type.value);
|
|
24317
24887
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -24346,7 +24916,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
24346
24916
|
function createInjectableDefinitionMap(meta) {
|
|
24347
24917
|
const definitionMap = new DefinitionMap();
|
|
24348
24918
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
24349
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
24919
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
24350
24920
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
24351
24921
|
definitionMap.set('type', meta.type.value);
|
|
24352
24922
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -24397,7 +24967,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
24397
24967
|
function createInjectorDefinitionMap(meta) {
|
|
24398
24968
|
const definitionMap = new DefinitionMap();
|
|
24399
24969
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
24400
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
24970
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
24401
24971
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
24402
24972
|
definitionMap.set('type', meta.type.value);
|
|
24403
24973
|
definitionMap.set('providers', meta.providers);
|
|
@@ -24427,7 +24997,7 @@ function compileDeclareNgModuleFromMetadata(meta) {
|
|
|
24427
24997
|
function createNgModuleDefinitionMap(meta) {
|
|
24428
24998
|
const definitionMap = new DefinitionMap();
|
|
24429
24999
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
24430
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
25000
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
24431
25001
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
24432
25002
|
definitionMap.set('type', meta.type.value);
|
|
24433
25003
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -24478,7 +25048,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
24478
25048
|
function createPipeDefinitionMap(meta) {
|
|
24479
25049
|
const definitionMap = new DefinitionMap();
|
|
24480
25050
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
24481
|
-
definitionMap.set('version', literal('16.0.0-rc.
|
|
25051
|
+
definitionMap.set('version', literal('16.0.0-rc.3'));
|
|
24482
25052
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
24483
25053
|
// e.g. `type: MyPipe`
|
|
24484
25054
|
definitionMap.set('type', meta.type.value);
|