@angular/compiler 16.0.0-rc.1 → 16.0.0-rc.2

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.
Files changed (45) hide show
  1. package/esm2022/src/constant_pool.mjs +2 -1
  2. package/esm2022/src/i18n/extractor_merger.mjs +1 -1
  3. package/esm2022/src/i18n/i18n_ast.mjs +4 -4
  4. package/esm2022/src/i18n/serializers/xliff.mjs +1 -1
  5. package/esm2022/src/i18n/serializers/xliff2.mjs +4 -1
  6. package/esm2022/src/i18n/serializers/xtb.mjs +1 -1
  7. package/esm2022/src/i18n/translation_bundle.mjs +2 -2
  8. package/esm2022/src/jit_compiler_facade.mjs +1 -1
  9. package/esm2022/src/ml_parser/entities.mjs +4 -3
  10. package/esm2022/src/ml_parser/html_whitespaces.mjs +4 -5
  11. package/esm2022/src/ml_parser/xml_tags.mjs +2 -1
  12. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  13. package/esm2022/src/render3/partial/directive.mjs +1 -1
  14. package/esm2022/src/render3/partial/factory.mjs +1 -1
  15. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  16. package/esm2022/src/render3/partial/injector.mjs +1 -1
  17. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  18. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  19. package/esm2022/src/render3/view/compiler.mjs +5 -3
  20. package/esm2022/src/template/pipeline/ir/src/expression.mjs +33 -26
  21. package/esm2022/src/template/pipeline/ir/src/operations.mjs +12 -3
  22. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +6 -4
  23. package/esm2022/src/template/pipeline/ir/src/ops/shared.mjs +1 -2
  24. package/esm2022/src/template/pipeline/ir/src/traits.mjs +11 -6
  25. package/esm2022/src/template/pipeline/ir/src/variable.mjs +1 -1
  26. package/esm2022/src/template/pipeline/src/emit.mjs +30 -8
  27. package/esm2022/src/template/pipeline/src/ingest.mjs +17 -11
  28. package/esm2022/src/template/pipeline/src/instruction.mjs +14 -9
  29. package/esm2022/src/template/pipeline/src/phases/chaining.mjs +78 -0
  30. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +2 -2
  31. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +29 -21
  32. package/esm2022/src/template/pipeline/src/phases/naming.mjs +57 -40
  33. package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +69 -0
  34. package/esm2022/src/template/pipeline/src/phases/reify.mjs +9 -9
  35. package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +2 -2
  36. package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +4 -4
  37. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +9 -2
  38. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +359 -0
  39. package/esm2022/src/version.mjs +1 -1
  40. package/fesm2022/compiler.mjs +724 -154
  41. package/fesm2022/compiler.mjs.map +1 -1
  42. package/fesm2022/testing.mjs +1 -1
  43. package/index.d.ts +3 -3
  44. package/package.json +2 -2
  45. package/testing/index.d.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.0.0-rc.1
2
+ * @license Angular v16.0.0-rc.2
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
- * Test whether an expression implements `UsesSlotIndexExprTrait`.
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, op.debugListId);
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(xref, name) {
9317
+ function createListenerOp(target, name, tag) {
9297
9318
  return {
9298
9319
  kind: OpKind.Listener,
9299
- xref,
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(op, createAdvanceOp(delta));
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.reference).callFn([
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
- let idx;
9547
- for (idx = 0; idx < expressions.length; idx++) {
9548
- interpolationArgs.push(literal(strings[idx]), expressions[idx]);
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.localRefs));
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
- * `UsesSlotIndexExprTrait`.
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
- // TODO(alxhub): convert this temporary name to match how the `TemplateDefinitionBuilder`
9863
- // names the main component template function.
9864
- cpl.root.fnName = `${cpl.componentName}_Template`;
9865
- for (const [id, view] of cpl.views) {
9866
- let vIndex = 0;
9867
- if (view.fnName === null) {
9868
- // TODO(alxhub): convert this temporary name to match how the `TemplateDefinitionBuilder`
9869
- // names embedded view functions.
9870
- view.fnName = `${cpl.componentName}_EmbeddedView_${id}`;
9871
- }
9872
- // Keep track of the names we assign to variables in the view. We'll need to propagate these
9873
- // into reads of those variables afterwards.
9874
- const varNames = new Map();
9875
- for (const op of view.ops()) {
9876
- switch (op.kind) {
9877
- case OpKind.Listener:
9878
- if (op.handlerFnName === null) {
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
- break;
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
- if (!varNames.has(expr.xref)) {
9900
- throw new Error(`Variable ${expr.xref} not yet named`);
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
- expr.name = varNames.get(expr.xref);
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.name)) {
10169
+ if (scope.has(op.variable.identifier)) {
10110
10170
  continue;
10111
10171
  }
10112
- scope.set(op.variable.name, op.xref);
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
- for (const view of tpl.views.values()) {
10236
- if (view === tpl.root) {
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 rf = variable('rf');
10267
- const createCond = ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, rf, literal(1)), createStatements);
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
- ], [createCond, updateCond], /* type */ undefined, /* sourceSpan */ undefined, view.fnName);
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
- throw new Error(`TODO: unhandled static attribute bindings (is this a thing?)`);
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. see:
15605
- // https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart
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
- * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
17010
- * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
17011
- * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
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
- definitionMap.set('consts', literalArr(tpl.consts));
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.1');
22628
+ const VERSION = new Version('16.0.0-rc.2');
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.1'));
24556
+ definitionMap.set('version', literal('16.0.0-rc.2'));
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.1'));
24659
+ definitionMap.set('version', literal('16.0.0-rc.2'));
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.1'));
24884
+ definitionMap.set('version', literal('16.0.0-rc.2'));
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.1'));
24919
+ definitionMap.set('version', literal('16.0.0-rc.2'));
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.1'));
24970
+ definitionMap.set('version', literal('16.0.0-rc.2'));
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.1'));
25000
+ definitionMap.set('version', literal('16.0.0-rc.2'));
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.1'));
25051
+ definitionMap.set('version', literal('16.0.0-rc.2'));
24482
25052
  definitionMap.set('ngImport', importExpr(Identifiers.core));
24483
25053
  // e.g. `type: MyPipe`
24484
25054
  definitionMap.set('type', meta.type.value);