@angular/compiler 16.1.0-next.3 → 16.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/esm2022/src/compiler_util/expression_converter.mjs +2 -3
  2. package/esm2022/src/constant_pool.mjs +53 -60
  3. package/esm2022/src/jit_compiler_facade.mjs +2 -2
  4. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  5. package/esm2022/src/render3/partial/directive.mjs +1 -1
  6. package/esm2022/src/render3/partial/factory.mjs +1 -1
  7. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  8. package/esm2022/src/render3/partial/injector.mjs +1 -1
  9. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  10. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  11. package/esm2022/src/render3/r3_identifiers.mjs +2 -3
  12. package/esm2022/src/render3/view/compiler.mjs +2 -2
  13. package/esm2022/src/shadow_css.mjs +34 -21
  14. package/esm2022/src/template/pipeline/ir/src/enums.mjs +26 -2
  15. package/esm2022/src/template/pipeline/ir/src/expression.mjs +156 -5
  16. package/esm2022/src/template/pipeline/ir/src/operations.mjs +6 -5
  17. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +10 -1
  18. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +16 -1
  19. package/esm2022/src/template/pipeline/ir/src/traits.mjs +20 -2
  20. package/esm2022/src/template/pipeline/src/compilation.mjs +3 -2
  21. package/esm2022/src/template/pipeline/src/emit.mjs +11 -1
  22. package/esm2022/src/template/pipeline/src/ingest.mjs +43 -10
  23. package/esm2022/src/template/pipeline/src/instruction.mjs +111 -10
  24. package/esm2022/src/template/pipeline/src/phases/align_pipe_variadic_var_offset.mjs +41 -0
  25. package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +4 -1
  26. package/esm2022/src/template/pipeline/src/phases/pipe_creation.mjs +57 -0
  27. package/esm2022/src/template/pipeline/src/phases/pipe_variadic.mjs +26 -0
  28. package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +54 -0
  29. package/esm2022/src/template/pipeline/src/phases/pure_literal_structures.mjs +58 -0
  30. package/esm2022/src/template/pipeline/src/phases/reify.mjs +21 -1
  31. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +4 -1
  32. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +23 -3
  33. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +13 -1
  34. package/esm2022/src/version.mjs +1 -1
  35. package/fesm2022/compiler.mjs +731 -117
  36. package/fesm2022/compiler.mjs.map +1 -1
  37. package/fesm2022/testing.mjs +1 -1
  38. package/index.d.ts +11 -2
  39. package/package.json +2 -2
  40. package/testing/index.d.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.1.0-next.3
2
+ * @license Angular v16.1.0
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2272,6 +2272,7 @@ class ConstantPool {
2272
2272
  this.statements = [];
2273
2273
  this.literals = new Map();
2274
2274
  this.literalFactories = new Map();
2275
+ this.sharedConstants = new Map();
2275
2276
  this.nextNameIndex = 0;
2276
2277
  }
2277
2278
  getConstLiteral(literal, forceShared) {
@@ -2281,7 +2282,7 @@ class ConstantPool {
2281
2282
  // reference to a constant.
2282
2283
  return literal;
2283
2284
  }
2284
- const key = this.keyOf(literal);
2285
+ const key = GenericKeyFn.INSTANCE.keyOf(literal);
2285
2286
  let fixup = this.literals.get(key);
2286
2287
  let newValue = false;
2287
2288
  if (!fixup) {
@@ -2326,11 +2327,20 @@ class ConstantPool {
2326
2327
  }
2327
2328
  return fixup;
2328
2329
  }
2330
+ getSharedConstant(def, expr) {
2331
+ const key = def.keyOf(expr);
2332
+ if (!this.sharedConstants.has(key)) {
2333
+ const id = this.freshName();
2334
+ this.sharedConstants.set(key, variable(id));
2335
+ this.statements.push(def.toSharedConstantDeclaration(id, expr));
2336
+ }
2337
+ return this.sharedConstants.get(key);
2338
+ }
2329
2339
  getLiteralFactory(literal) {
2330
2340
  // Create a pure function that builds an array of a mix of constant and variable expressions
2331
2341
  if (literal instanceof LiteralArrayExpr) {
2332
2342
  const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
2333
- const key = this.keyOf(literalArr(argumentsForKey));
2343
+ const key = GenericKeyFn.INSTANCE.keyOf(literalArr(argumentsForKey));
2334
2344
  return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries));
2335
2345
  }
2336
2346
  else {
@@ -2339,7 +2349,7 @@ class ConstantPool {
2339
2349
  value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
2340
2350
  quoted: e.quoted
2341
2351
  })));
2342
- const key = this.keyOf(expressionForKey);
2352
+ const key = GenericKeyFn.INSTANCE.keyOf(expressionForKey);
2343
2353
  return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({
2344
2354
  key: literal.entries[index].key,
2345
2355
  value,
@@ -2376,65 +2386,48 @@ class ConstantPool {
2376
2386
  freshName() {
2377
2387
  return this.uniqueName(CONSTANT_PREFIX);
2378
2388
  }
2379
- keyOf(expression) {
2380
- return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT);
2381
- }
2382
2389
  }
2383
- /**
2384
- * Visitor used to determine if 2 expressions are equivalent and can be shared in the
2385
- * `ConstantPool`.
2386
- *
2387
- * When the id (string) generated by the visitor is equal, expressions are considered equivalent.
2388
- */
2389
- class KeyVisitor {
2390
- constructor() {
2391
- this.visitWrappedNodeExpr = invalid$1;
2392
- this.visitWriteVarExpr = invalid$1;
2393
- this.visitWriteKeyExpr = invalid$1;
2394
- this.visitWritePropExpr = invalid$1;
2395
- this.visitInvokeFunctionExpr = invalid$1;
2396
- this.visitTaggedTemplateExpr = invalid$1;
2397
- this.visitInstantiateExpr = invalid$1;
2398
- this.visitConditionalExpr = invalid$1;
2399
- this.visitNotExpr = invalid$1;
2400
- this.visitAssertNotNullExpr = invalid$1;
2401
- this.visitCastExpr = invalid$1;
2402
- this.visitFunctionExpr = invalid$1;
2403
- this.visitUnaryOperatorExpr = invalid$1;
2404
- this.visitBinaryOperatorExpr = invalid$1;
2405
- this.visitReadPropExpr = invalid$1;
2406
- this.visitReadKeyExpr = invalid$1;
2407
- this.visitCommaExpr = invalid$1;
2408
- this.visitLocalizedString = invalid$1;
2409
- }
2410
- visitLiteralExpr(ast) {
2411
- return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
2412
- }
2413
- visitLiteralArrayExpr(ast, context) {
2414
- return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`;
2415
- }
2416
- visitLiteralMapExpr(ast, context) {
2417
- const mapKey = (entry) => {
2418
- const quote = entry.quoted ? '"' : '';
2419
- return `${quote}${entry.key}${quote}`;
2420
- };
2421
- const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`;
2422
- return `{${ast.entries.map(mapEntry).join(',')}`;
2423
- }
2424
- visitExternalExpr(ast) {
2425
- return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
2426
- `EX:${ast.value.runtime.name}`;
2427
- }
2428
- visitReadVarExpr(node) {
2429
- return `VAR:${node.name}`;
2430
- }
2431
- visitTypeofExpr(node, context) {
2432
- return `TYPEOF:${node.expr.visitExpression(this, context)}`;
2390
+ class GenericKeyFn {
2391
+ static { this.INSTANCE = new GenericKeyFn(); }
2392
+ keyOf(expr) {
2393
+ if (expr instanceof LiteralExpr && typeof expr.value === 'string') {
2394
+ return `"${expr.value}"`;
2395
+ }
2396
+ else if (expr instanceof LiteralExpr) {
2397
+ return String(expr.value);
2398
+ }
2399
+ else if (expr instanceof LiteralArrayExpr) {
2400
+ const entries = [];
2401
+ for (const entry of expr.entries) {
2402
+ entries.push(this.keyOf(entry));
2403
+ }
2404
+ return `[${entries.join(',')}]`;
2405
+ }
2406
+ else if (expr instanceof LiteralMapExpr) {
2407
+ const entries = [];
2408
+ for (const entry of expr.entries) {
2409
+ let key = entry.key;
2410
+ if (entry.quoted) {
2411
+ key = `"${key}"`;
2412
+ }
2413
+ entries.push(key + ':' + this.keyOf(entry.value));
2414
+ }
2415
+ return `{${entries.join(',')}}`;
2416
+ }
2417
+ else if (expr instanceof ExternalExpr) {
2418
+ return `import("${expr.value.moduleName}", ${expr.value.name})`;
2419
+ }
2420
+ else if (expr instanceof ReadVarExpr) {
2421
+ return `read(${expr.name})`;
2422
+ }
2423
+ else if (expr instanceof TypeofExpr) {
2424
+ return `typeof(${this.keyOf(expr.expr)})`;
2425
+ }
2426
+ else {
2427
+ throw new Error(`${this.constructor.name} does not handle expressions of type ${expr.constructor.name}`);
2428
+ }
2433
2429
  }
2434
2430
  }
2435
- function invalid$1(arg) {
2436
- throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
2437
- }
2438
2431
  function isVariable(e) {
2439
2432
  return e instanceof ReadVarExpr;
2440
2433
  }
@@ -7508,11 +7501,28 @@ class ShadowCss {
7508
7501
  * The hostSelector is the attribute added to the host itself.
7509
7502
  */
7510
7503
  shimCssText(cssText, selector, hostSelector = '') {
7511
- const commentsWithHash = extractCommentsWithHash(cssText);
7512
- cssText = stripComments(cssText);
7504
+ // **NOTE**: Do not strip comments as this will cause component sourcemaps to break
7505
+ // due to shift in lines.
7506
+ // Collect comments and replace them with a placeholder, this is done to avoid complicating
7507
+ // the rule parsing RegExp and keep it safer.
7508
+ const comments = [];
7509
+ cssText = cssText.replace(_commentRe, (m) => {
7510
+ if (m.match(_commentWithHashRe)) {
7511
+ comments.push(m);
7512
+ }
7513
+ else {
7514
+ // Replace non hash comments with empty lines.
7515
+ // This is done so that we do not leak any senstive data in comments.
7516
+ const newLinesMatches = m.match(_newLinesRe);
7517
+ comments.push((newLinesMatches?.join('') ?? '') + '\n');
7518
+ }
7519
+ return COMMENT_PLACEHOLDER;
7520
+ });
7513
7521
  cssText = this._insertDirectives(cssText);
7514
7522
  const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
7515
- return [scopedCssText, ...commentsWithHash].join('\n');
7523
+ // Add back comments at the original position.
7524
+ let commentIdx = 0;
7525
+ return scopedCssText.replace(_commentWithHashPlaceHolderRe, () => comments[commentIdx++]);
7516
7526
  }
7517
7527
  _insertDirectives(cssText) {
7518
7528
  cssText = this._insertPolyfillDirectivesInCssText(cssText);
@@ -7753,7 +7763,7 @@ class ShadowCss {
7753
7763
  return cssText.replace(_cssColonHostRe, (_, hostSelectors, otherSelectors) => {
7754
7764
  if (hostSelectors) {
7755
7765
  const convertedSelectors = [];
7756
- const hostSelectorArray = hostSelectors.split(',').map(p => p.trim());
7766
+ const hostSelectorArray = hostSelectors.split(',').map((p) => p.trim());
7757
7767
  for (const hostSelector of hostSelectorArray) {
7758
7768
  if (!hostSelector)
7759
7769
  break;
@@ -7783,7 +7793,7 @@ class ShadowCss {
7783
7793
  * .foo<scopeName> .bar { ... }
7784
7794
  */
7785
7795
  _convertColonHostContext(cssText) {
7786
- return cssText.replace(_cssColonHostContextReGlobal, selectorText => {
7796
+ return cssText.replace(_cssColonHostContextReGlobal, (selectorText) => {
7787
7797
  // We have captured a selector that contains a `:host-context` rule.
7788
7798
  // For backward compatibility `:host-context` may contain a comma separated list of selectors.
7789
7799
  // Each context selector group will contain a list of host-context selectors that must match
@@ -7795,10 +7805,10 @@ class ShadowCss {
7795
7805
  // Execute `_cssColonHostContextRe` over and over until we have extracted all the
7796
7806
  // `:host-context` selectors from this selector.
7797
7807
  let match;
7798
- while (match = _cssColonHostContextRe.exec(selectorText)) {
7808
+ while ((match = _cssColonHostContextRe.exec(selectorText))) {
7799
7809
  // `match` = [':host-context(<selectors>)<rest>', <selectors>, <rest>]
7800
7810
  // The `<selectors>` could actually be a comma separated list: `:host-context(.one, .two)`.
7801
- const newContextSelectors = (match[1] ?? '').trim().split(',').map(m => m.trim()).filter(m => m !== '');
7811
+ const newContextSelectors = (match[1] ?? '').trim().split(',').map((m) => m.trim()).filter((m) => m !== '');
7802
7812
  // We must duplicate the current selector group for each of these new selectors.
7803
7813
  // For example if the current groups are:
7804
7814
  // ```
@@ -7821,7 +7831,7 @@ class ShadowCss {
7821
7831
  repeatGroups(contextSelectorGroups, newContextSelectors.length);
7822
7832
  for (let i = 0; i < newContextSelectors.length; i++) {
7823
7833
  for (let j = 0; j < contextSelectorGroupsLength; j++) {
7824
- contextSelectorGroups[j + (i * contextSelectorGroupsLength)].push(newContextSelectors[i]);
7834
+ contextSelectorGroups[j + i * contextSelectorGroupsLength].push(newContextSelectors[i]);
7825
7835
  }
7826
7836
  }
7827
7837
  // Update the `selectorText` and see repeat to see if there are more `:host-context`s.
@@ -7831,7 +7841,7 @@ class ShadowCss {
7831
7841
  // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more
7832
7842
  // info about how this is done.
7833
7843
  return contextSelectorGroups
7834
- .map(contextSelectors => combineHostContextSelectors(contextSelectors, selectorText))
7844
+ .map((contextSelectors) => combineHostContextSelectors(contextSelectors, selectorText))
7835
7845
  .join(', ');
7836
7846
  });
7837
7847
  }
@@ -7883,7 +7893,7 @@ class ShadowCss {
7883
7893
  * ```
7884
7894
  */
7885
7895
  _stripScopingSelectors(cssText) {
7886
- return processRules(cssText, rule => {
7896
+ return processRules(cssText, (rule) => {
7887
7897
  const selector = rule.selector.replace(_shadowDeepSelectors, ' ')
7888
7898
  .replace(_polyfillHostNoCombinatorRe, ' ');
7889
7899
  return new CssRule(selector, rule.content);
@@ -7891,7 +7901,7 @@ class ShadowCss {
7891
7901
  }
7892
7902
  _scopeSelector(selector, scopeSelector, hostSelector) {
7893
7903
  return selector.split(',')
7894
- .map(part => part.trim().split(_shadowDeepSelectors))
7904
+ .map((part) => part.trim().split(_shadowDeepSelectors))
7895
7905
  .map((deepParts) => {
7896
7906
  const [shallowPart, ...otherParts] = deepParts;
7897
7907
  const applyScope = (shallowPart) => {
@@ -8074,17 +8084,14 @@ const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$';
8074
8084
  const _polyfillHostRe = /-shadowcsshost/gim;
8075
8085
  const _colonHostRe = /:host/gim;
8076
8086
  const _colonHostContextRe = /:host-context/gim;
8087
+ const _newLinesRe = /\r?\n/g;
8077
8088
  const _commentRe = /\/\*[\s\S]*?\*\//g;
8089
+ const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=/g;
8090
+ const COMMENT_PLACEHOLDER = '%COMMENT%';
8091
+ const _commentWithHashPlaceHolderRe = new RegExp(COMMENT_PLACEHOLDER, 'g');
8078
8092
  const _placeholderRe = /__ph-(\d+)__/g;
8079
- function stripComments(input) {
8080
- return input.replace(_commentRe, '');
8081
- }
8082
- const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;
8083
- function extractCommentsWithHash(input) {
8084
- return input.match(_commentWithHashRe) || [];
8085
- }
8086
8093
  const BLOCK_PLACEHOLDER = '%BLOCK%';
8087
- const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
8094
+ const _ruleRe = new RegExp(`(\\s*(?:${COMMENT_PLACEHOLDER}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`, 'g');
8088
8095
  const CONTENT_PAIRS = new Map([['{', '}']]);
8089
8096
  const COMMA_IN_PLACEHOLDER = '%COMMA_IN_PLACEHOLDER%';
8090
8097
  const SEMI_IN_PLACEHOLDER = '%SEMI_IN_PLACEHOLDER%';
@@ -8293,7 +8300,6 @@ function unescapeQuotes(str, isQuoted) {
8293
8300
  *
8294
8301
  * And so on...
8295
8302
  *
8296
- * @param hostMarker the string that selects the host element.
8297
8303
  * @param contextSelectors an array of context selectors that will be combined.
8298
8304
  * @param otherSelectors the rest of the selectors that are not context selectors.
8299
8305
  */
@@ -8538,10 +8544,18 @@ var OpKind;
8538
8544
  * An operation to bind an expression to a property of an element.
8539
8545
  */
8540
8546
  OpKind[OpKind["Property"] = 13] = "Property";
8547
+ /**
8548
+ * An operation to interpolate text into a property binding.
8549
+ */
8550
+ OpKind[OpKind["InterpolateProperty"] = 14] = "InterpolateProperty";
8541
8551
  /**
8542
8552
  * An operation to advance the runtime's implicit slot context during the update phase of a view.
8543
8553
  */
8544
- OpKind[OpKind["Advance"] = 14] = "Advance";
8554
+ OpKind[OpKind["Advance"] = 15] = "Advance";
8555
+ /**
8556
+ * An operation to instantiate a pipe.
8557
+ */
8558
+ OpKind[OpKind["Pipe"] = 16] = "Pipe";
8545
8559
  })(OpKind || (OpKind = {}));
8546
8560
  /**
8547
8561
  * Distinguishes different kinds of IR expressions.
@@ -8580,6 +8594,22 @@ var ExpressionKind;
8580
8594
  * Runtime operation to reset the current view context after `RestoreView`.
8581
8595
  */
8582
8596
  ExpressionKind[ExpressionKind["ResetView"] = 7] = "ResetView";
8597
+ /**
8598
+ * Defines and calls a function with change-detected arguments.
8599
+ */
8600
+ ExpressionKind[ExpressionKind["PureFunctionExpr"] = 8] = "PureFunctionExpr";
8601
+ /**
8602
+ * Indicates a positional parameter to a pure function definition.
8603
+ */
8604
+ ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] = 9] = "PureFunctionParameterExpr";
8605
+ /**
8606
+ * Binding to a pipe transformation.
8607
+ */
8608
+ ExpressionKind[ExpressionKind["PipeBinding"] = 10] = "PipeBinding";
8609
+ /**
8610
+ * Binding to a pipe transformation with a variable number of arguments.
8611
+ */
8612
+ ExpressionKind[ExpressionKind["PipeBindingVariadic"] = 11] = "PipeBindingVariadic";
8583
8613
  })(ExpressionKind || (ExpressionKind = {}));
8584
8614
  /**
8585
8615
  * Distinguishes between different kinds of `SemanticVariable`s.
@@ -8615,7 +8645,11 @@ const UsesSlotIndex = Symbol('UsesSlotIndex');
8615
8645
  /**
8616
8646
  * Marker symbol for `ConsumesVars` trait.
8617
8647
  */
8618
- const ConsumesVarsTrait = Symbol('UsesVars');
8648
+ const ConsumesVarsTrait = Symbol('ConsumesVars');
8649
+ /**
8650
+ * Marker symbol for `UsesVarOffset` trait.
8651
+ */
8652
+ const UsesVarOffset = Symbol('UsesVarOffset');
8619
8653
  /**
8620
8654
  * Default values for most `ConsumesSlotOpTrait` fields (used with the spread operator to initialize
8621
8655
  * implementors of the trait).
@@ -8647,6 +8681,14 @@ const TRAIT_DEPENDS_ON_SLOT_CONTEXT = {
8647
8681
  const TRAIT_CONSUMES_VARS = {
8648
8682
  [ConsumesVarsTrait]: true,
8649
8683
  };
8684
+ /**
8685
+ * Default values for `UsesVarOffset` fields (used with the spread operator to initialize
8686
+ * implementors of this trait).
8687
+ */
8688
+ const TRAIT_USES_VAR_OFFSET = {
8689
+ [UsesVarOffset]: true,
8690
+ varOffset: null,
8691
+ };
8650
8692
  /**
8651
8693
  * Test whether an operation implements `ConsumesSlotOpTrait`.
8652
8694
  */
@@ -8662,11 +8704,17 @@ function hasDependsOnSlotContextTrait(op) {
8662
8704
  function hasConsumesVarsTrait(value) {
8663
8705
  return value[ConsumesVarsTrait] === true;
8664
8706
  }
8707
+ /**
8708
+ * Test whether an expression implements `UsesVarOffsetTrait`.
8709
+ */
8710
+ function hasUsesVarOffsetTrait(expr) {
8711
+ return expr[UsesVarOffset] === true;
8712
+ }
8665
8713
  function hasUsesSlotIndexTrait(value) {
8666
8714
  return value[UsesSlotIndex] === true;
8667
8715
  }
8668
8716
 
8669
- var _a;
8717
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
8670
8718
  /**
8671
8719
  * Check whether a given `o.Expression` is a logical IR expression type.
8672
8720
  */
@@ -8852,6 +8900,125 @@ class ReadVariableExpr extends ExpressionBase {
8852
8900
  }
8853
8901
  transformInternalExpressions() { }
8854
8902
  }
8903
+ class PureFunctionExpr extends ExpressionBase {
8904
+ static { _b = ConsumesVarsTrait, _c = UsesVarOffset; }
8905
+ constructor(expression, args) {
8906
+ super();
8907
+ this.kind = ExpressionKind.PureFunctionExpr;
8908
+ this[_b] = true;
8909
+ this[_c] = true;
8910
+ this.varOffset = null;
8911
+ /**
8912
+ * Once extracted to the `ConstantPool`, a reference to the function which defines the computation
8913
+ * of `body`.
8914
+ */
8915
+ this.fn = null;
8916
+ this.body = expression;
8917
+ this.args = args;
8918
+ }
8919
+ visitExpression(visitor, context) {
8920
+ this.body?.visitExpression(visitor, context);
8921
+ for (const arg of this.args) {
8922
+ arg.visitExpression(visitor, context);
8923
+ }
8924
+ }
8925
+ isEquivalent(other) {
8926
+ if (!(other instanceof PureFunctionExpr) || other.args.length !== this.args.length) {
8927
+ return false;
8928
+ }
8929
+ return other.body !== null && this.body !== null && other.body.isEquivalent(this.body) &&
8930
+ other.args.every((arg, idx) => arg.isEquivalent(this.args[idx]));
8931
+ }
8932
+ isConstant() {
8933
+ return false;
8934
+ }
8935
+ transformInternalExpressions(transform, flags) {
8936
+ if (this.body !== null) {
8937
+ // TODO: figure out if this is the right flag to pass here.
8938
+ this.body = transformExpressionsInExpression(this.body, transform, flags | VisitorContextFlag.InChildOperation);
8939
+ }
8940
+ else if (this.fn !== null) {
8941
+ this.fn = transformExpressionsInExpression(this.fn, transform, flags);
8942
+ }
8943
+ for (let i = 0; i < this.args.length; i++) {
8944
+ this.args[i] = transformExpressionsInExpression(this.args[i], transform, flags);
8945
+ }
8946
+ }
8947
+ }
8948
+ class PureFunctionParameterExpr extends ExpressionBase {
8949
+ constructor(index) {
8950
+ super();
8951
+ this.index = index;
8952
+ this.kind = ExpressionKind.PureFunctionParameterExpr;
8953
+ }
8954
+ visitExpression() { }
8955
+ isEquivalent(other) {
8956
+ return other instanceof PureFunctionParameterExpr && other.index === this.index;
8957
+ }
8958
+ isConstant() {
8959
+ return true;
8960
+ }
8961
+ transformInternalExpressions() { }
8962
+ }
8963
+ class PipeBindingExpr extends ExpressionBase {
8964
+ static { _d = UsesSlotIndex, _e = ConsumesVarsTrait, _f = UsesVarOffset; }
8965
+ constructor(target, name, args) {
8966
+ super();
8967
+ this.target = target;
8968
+ this.name = name;
8969
+ this.args = args;
8970
+ this.kind = ExpressionKind.PipeBinding;
8971
+ this[_d] = true;
8972
+ this[_e] = true;
8973
+ this[_f] = true;
8974
+ this.slot = null;
8975
+ this.varOffset = null;
8976
+ }
8977
+ visitExpression(visitor, context) {
8978
+ for (const arg of this.args) {
8979
+ arg.visitExpression(visitor, context);
8980
+ }
8981
+ }
8982
+ isEquivalent() {
8983
+ return false;
8984
+ }
8985
+ isConstant() {
8986
+ return false;
8987
+ }
8988
+ transformInternalExpressions(transform, flags) {
8989
+ for (let idx = 0; idx < this.args.length; idx++) {
8990
+ this.args[idx] = transformExpressionsInExpression(this.args[idx], transform, flags);
8991
+ }
8992
+ }
8993
+ }
8994
+ class PipeBindingVariadicExpr extends ExpressionBase {
8995
+ static { _g = UsesSlotIndex, _h = ConsumesVarsTrait, _j = UsesVarOffset; }
8996
+ constructor(target, name, args, numArgs) {
8997
+ super();
8998
+ this.target = target;
8999
+ this.name = name;
9000
+ this.args = args;
9001
+ this.numArgs = numArgs;
9002
+ this.kind = ExpressionKind.PipeBindingVariadic;
9003
+ this[_g] = true;
9004
+ this[_h] = true;
9005
+ this[_j] = true;
9006
+ this.slot = null;
9007
+ this.varOffset = null;
9008
+ }
9009
+ visitExpression(visitor, context) {
9010
+ this.args.visitExpression(visitor, context);
9011
+ }
9012
+ isEquivalent() {
9013
+ return false;
9014
+ }
9015
+ isConstant() {
9016
+ return false;
9017
+ }
9018
+ transformInternalExpressions(transform, flags) {
9019
+ this.args = transformExpressionsInExpression(this.args, transform, flags);
9020
+ }
9021
+ }
8855
9022
  /**
8856
9023
  * Visits all `Expression`s in the AST of `op` with the `visitor` function.
8857
9024
  */
@@ -8877,6 +9044,11 @@ function transformExpressionsInOp(op, transform, flags) {
8877
9044
  case OpKind.Property:
8878
9045
  op.expression = transformExpressionsInExpression(op.expression, transform, flags);
8879
9046
  break;
9047
+ case OpKind.InterpolateProperty:
9048
+ for (let i = 0; i < op.expressions.length; i++) {
9049
+ op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform, flags);
9050
+ }
9051
+ break;
8880
9052
  case OpKind.Statement:
8881
9053
  transformExpressionsInStatement(op.statement, transform, flags);
8882
9054
  break;
@@ -8901,6 +9073,7 @@ function transformExpressionsInOp(op, transform, flags) {
8901
9073
  case OpKind.ContainerEnd:
8902
9074
  case OpKind.Template:
8903
9075
  case OpKind.Text:
9076
+ case OpKind.Pipe:
8904
9077
  case OpKind.Advance:
8905
9078
  // These operations contain no expressions.
8906
9079
  break;
@@ -8917,7 +9090,6 @@ function transformExpressionsInOp(op, transform, flags) {
8917
9090
  function transformExpressionsInExpression(expr, transform, flags) {
8918
9091
  if (expr instanceof ExpressionBase) {
8919
9092
  expr.transformInternalExpressions(transform, flags);
8920
- return transform(expr, flags);
8921
9093
  }
8922
9094
  else if (expr instanceof BinaryOperatorExpr) {
8923
9095
  expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
@@ -8930,12 +9102,39 @@ function transformExpressionsInExpression(expr, transform, flags) {
8930
9102
  expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
8931
9103
  expr.index = transformExpressionsInExpression(expr.index, transform, flags);
8932
9104
  }
9105
+ else if (expr instanceof WritePropExpr) {
9106
+ expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
9107
+ expr.value = transformExpressionsInExpression(expr.value, transform, flags);
9108
+ }
9109
+ else if (expr instanceof WriteKeyExpr) {
9110
+ expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
9111
+ expr.index = transformExpressionsInExpression(expr.index, transform, flags);
9112
+ expr.value = transformExpressionsInExpression(expr.value, transform, flags);
9113
+ }
8933
9114
  else if (expr instanceof InvokeFunctionExpr) {
8934
9115
  expr.fn = transformExpressionsInExpression(expr.fn, transform, flags);
8935
9116
  for (let i = 0; i < expr.args.length; i++) {
8936
9117
  expr.args[i] = transformExpressionsInExpression(expr.args[i], transform, flags);
8937
9118
  }
8938
9119
  }
9120
+ else if (expr instanceof LiteralArrayExpr) {
9121
+ for (let i = 0; i < expr.entries.length; i++) {
9122
+ expr.entries[i] = transformExpressionsInExpression(expr.entries[i], transform, flags);
9123
+ }
9124
+ }
9125
+ else if (expr instanceof LiteralMapExpr) {
9126
+ for (let i = 0; i < expr.entries.length; i++) {
9127
+ expr.entries[i].value =
9128
+ transformExpressionsInExpression(expr.entries[i].value, transform, flags);
9129
+ }
9130
+ }
9131
+ else if (expr instanceof ConditionalExpr) {
9132
+ expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
9133
+ expr.trueCase = transformExpressionsInExpression(expr.trueCase, transform, flags);
9134
+ if (expr.falseCase !== null) {
9135
+ expr.falseCase = transformExpressionsInExpression(expr.falseCase, transform, flags);
9136
+ }
9137
+ }
8939
9138
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
8940
9139
  expr instanceof LiteralExpr) {
8941
9140
  // No action for these types.
@@ -8943,7 +9142,7 @@ function transformExpressionsInExpression(expr, transform, flags) {
8943
9142
  else {
8944
9143
  throw new Error(`Unhandled expression kind: ${expr.constructor.name}`);
8945
9144
  }
8946
- return expr;
9145
+ return transform(expr, flags);
8947
9146
  }
8948
9147
  /**
8949
9148
  * Transform all `Expression`s in the AST of `stmt` with the `transform` function.
@@ -9144,10 +9343,12 @@ class OpList {
9144
9343
  * Insert `op` before `before`.
9145
9344
  */
9146
9345
  static insertBefore(op, before) {
9147
- OpList.assertIsNotEnd(before);
9346
+ OpList.assertIsOwned(before);
9347
+ if (before.prev === null) {
9348
+ throw new Error(`AssertionError: illegal operation on list start`);
9349
+ }
9148
9350
  OpList.assertIsNotEnd(op);
9149
9351
  OpList.assertIsUnowned(op);
9150
- OpList.assertIsOwned(before);
9151
9352
  op.debugListId = before.debugListId;
9152
9353
  // Just in case.
9153
9354
  op.prev = null;
@@ -9287,6 +9488,15 @@ function createListenerOp(target, name, tag) {
9287
9488
  ...TRAIT_USES_SLOT_INDEX,
9288
9489
  };
9289
9490
  }
9491
+ function createPipeOp(xref, name) {
9492
+ return {
9493
+ kind: OpKind.Pipe,
9494
+ xref,
9495
+ name,
9496
+ ...NEW_OP,
9497
+ ...TRAIT_CONSUMES_SLOT,
9498
+ };
9499
+ }
9290
9500
 
9291
9501
  /**
9292
9502
  * Create an `InterpolationTextOp`.
@@ -9316,6 +9526,21 @@ function createPropertyOp(xref, name, expression) {
9316
9526
  ...NEW_OP,
9317
9527
  };
9318
9528
  }
9529
+ /**
9530
+ * Create a `InterpolateProperty`.
9531
+ */
9532
+ function createInterpolatePropertyOp(xref, name, strings, expressions) {
9533
+ return {
9534
+ kind: OpKind.InterpolateProperty,
9535
+ target: xref,
9536
+ name,
9537
+ strings,
9538
+ expressions,
9539
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9540
+ ...TRAIT_CONSUMES_VARS,
9541
+ ...NEW_OP,
9542
+ };
9543
+ }
9319
9544
  /**
9320
9545
  * Create an `AdvanceOp`.
9321
9546
  */
@@ -9500,6 +9725,12 @@ function listener(name, handlerFn) {
9500
9725
  handlerFn,
9501
9726
  ]);
9502
9727
  }
9728
+ function pipe(slot, name) {
9729
+ return call(Identifiers.pipe, [
9730
+ literal(slot),
9731
+ literal(name),
9732
+ ]);
9733
+ }
9503
9734
  function advance(delta) {
9504
9735
  return call(Identifiers.advance, [
9505
9736
  literal(delta),
@@ -9539,6 +9770,30 @@ function property(name, expression) {
9539
9770
  expression,
9540
9771
  ]);
9541
9772
  }
9773
+ const PIPE_BINDINGS = [
9774
+ Identifiers.pipeBind1,
9775
+ Identifiers.pipeBind2,
9776
+ Identifiers.pipeBind3,
9777
+ Identifiers.pipeBind4,
9778
+ ];
9779
+ function pipeBind(slot, varOffset, args) {
9780
+ if (args.length < 1 || args.length > PIPE_BINDINGS.length) {
9781
+ throw new Error(`pipeBind() argument count out of bounds`);
9782
+ }
9783
+ const instruction = PIPE_BINDINGS[args.length - 1];
9784
+ return importExpr(instruction).callFn([
9785
+ literal(slot),
9786
+ literal(varOffset),
9787
+ ...args,
9788
+ ]);
9789
+ }
9790
+ function pipeBindV(slot, varOffset, args) {
9791
+ return importExpr(Identifiers.pipeBindV).callFn([
9792
+ literal(slot),
9793
+ literal(varOffset),
9794
+ args,
9795
+ ]);
9796
+ }
9542
9797
  function textInterpolate(strings, expressions) {
9543
9798
  if (strings.length < 1 || expressions.length !== strings.length - 1) {
9544
9799
  throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
@@ -9555,7 +9810,31 @@ function textInterpolate(strings, expressions) {
9555
9810
  // idx points at the last string.
9556
9811
  interpolationArgs.push(literal(strings[idx]));
9557
9812
  }
9558
- return callInterpolation(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs);
9813
+ return callVariadicInstruction(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs);
9814
+ }
9815
+ function propertyInterpolate(name, strings, expressions) {
9816
+ if (strings.length < 1 || expressions.length !== strings.length - 1) {
9817
+ throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
9818
+ }
9819
+ const interpolationArgs = [];
9820
+ if (expressions.length === 1 && strings[0] === '' && strings[1] === '') {
9821
+ interpolationArgs.push(expressions[0]);
9822
+ }
9823
+ else {
9824
+ let idx;
9825
+ for (idx = 0; idx < expressions.length; idx++) {
9826
+ interpolationArgs.push(literal(strings[idx]), expressions[idx]);
9827
+ }
9828
+ // idx points at the last string.
9829
+ interpolationArgs.push(literal(strings[idx]));
9830
+ }
9831
+ return callVariadicInstruction(PROPERTY_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs);
9832
+ }
9833
+ function pureFunction(varOffset, fn, args) {
9834
+ return callVariadicInstructionExpr(PURE_FUNCTION_CONFIG, [
9835
+ literal(varOffset),
9836
+ fn,
9837
+ ], args);
9559
9838
  }
9560
9839
  function call(instruction, args) {
9561
9840
  return createStatementOp(importExpr(instruction).callFn(args).toStmt());
@@ -9576,21 +9855,68 @@ const TEXT_INTERPOLATE_CONFIG = {
9576
9855
  Identifiers.textInterpolate8,
9577
9856
  ],
9578
9857
  variable: Identifiers.textInterpolateV,
9858
+ mapping: n => {
9859
+ if (n % 2 === 0) {
9860
+ throw new Error(`Expected odd number of arguments`);
9861
+ }
9862
+ return (n - 1) / 2;
9863
+ },
9579
9864
  };
9580
- function callInterpolation(config, baseArgs, interpolationArgs) {
9581
- if (interpolationArgs.length % 2 === 0) {
9582
- throw new Error(`Expected odd number of interpolation arguments`);
9583
- }
9584
- const n = (interpolationArgs.length - 1) / 2;
9865
+ /**
9866
+ * `InterpolationConfig` for the `propertyInterpolate` instruction.
9867
+ */
9868
+ const PROPERTY_INTERPOLATE_CONFIG = {
9869
+ constant: [
9870
+ Identifiers.propertyInterpolate,
9871
+ Identifiers.propertyInterpolate1,
9872
+ Identifiers.propertyInterpolate2,
9873
+ Identifiers.propertyInterpolate3,
9874
+ Identifiers.propertyInterpolate4,
9875
+ Identifiers.propertyInterpolate5,
9876
+ Identifiers.propertyInterpolate6,
9877
+ Identifiers.propertyInterpolate7,
9878
+ Identifiers.propertyInterpolate8,
9879
+ ],
9880
+ variable: Identifiers.propertyInterpolateV,
9881
+ mapping: n => {
9882
+ if (n % 2 === 0) {
9883
+ throw new Error(`Expected odd number of arguments`);
9884
+ }
9885
+ return (n - 1) / 2;
9886
+ },
9887
+ };
9888
+ const PURE_FUNCTION_CONFIG = {
9889
+ constant: [
9890
+ Identifiers.pureFunction0,
9891
+ Identifiers.pureFunction1,
9892
+ Identifiers.pureFunction2,
9893
+ Identifiers.pureFunction3,
9894
+ Identifiers.pureFunction4,
9895
+ Identifiers.pureFunction5,
9896
+ Identifiers.pureFunction6,
9897
+ Identifiers.pureFunction7,
9898
+ Identifiers.pureFunction8,
9899
+ ],
9900
+ variable: Identifiers.pureFunctionV,
9901
+ mapping: n => n,
9902
+ };
9903
+ function callVariadicInstructionExpr(config, baseArgs, interpolationArgs) {
9904
+ const n = config.mapping(interpolationArgs.length);
9585
9905
  if (n < config.constant.length) {
9586
9906
  // Constant calling pattern.
9587
- return call(config.constant[n], [...baseArgs, ...interpolationArgs]);
9907
+ return importExpr(config.constant[n]).callFn([...baseArgs, ...interpolationArgs]);
9588
9908
  }
9589
- else {
9909
+ else if (config.variable !== null) {
9590
9910
  // Variable calling pattern.
9591
- return call(config.variable, [...baseArgs, literalArr(interpolationArgs)]);
9911
+ return importExpr(config.variable).callFn([...baseArgs, literalArr(interpolationArgs)]);
9912
+ }
9913
+ else {
9914
+ throw new Error(`AssertionError: unable to call variadic function`);
9592
9915
  }
9593
9916
  }
9917
+ function callVariadicInstruction(config, baseArgs, interpolationArgs) {
9918
+ return createStatementOp(callVariadicInstructionExpr(config, baseArgs, interpolationArgs).toStmt());
9919
+ }
9594
9920
 
9595
9921
  /**
9596
9922
  * Compiles semantic operations across all views and generates output `o.Statement`s with actual
@@ -9635,6 +9961,9 @@ function reifyCreateOperations(view, ops) {
9635
9961
  const childView = view.tpl.views.get(op.xref);
9636
9962
  OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes));
9637
9963
  break;
9964
+ case OpKind.Pipe:
9965
+ OpList.replace(op, pipe(op.slot, op.name));
9966
+ break;
9638
9967
  case OpKind.Listener:
9639
9968
  const listenerFn = reifyListenerHandler(view, op.handlerFnName, op.handlerOps);
9640
9969
  OpList.replace(op, listener(op.name, listenerFn));
@@ -9663,6 +9992,9 @@ function reifyUpdateOperations(_view, ops) {
9663
9992
  case OpKind.Property:
9664
9993
  OpList.replace(op, property(op.name, op.expression));
9665
9994
  break;
9995
+ case OpKind.InterpolateProperty:
9996
+ OpList.replace(op, propertyInterpolate(op.name, op.strings, op.expressions));
9997
+ break;
9666
9998
  case OpKind.InterpolateText:
9667
9999
  OpList.replace(op, textInterpolate(op.strings, op.expressions));
9668
10000
  break;
@@ -9681,6 +10013,9 @@ function reifyUpdateOperations(_view, ops) {
9681
10013
  }
9682
10014
  }
9683
10015
  function reifyIrExpression(expr) {
10016
+ if (!isIrExpression(expr)) {
10017
+ return expr;
10018
+ }
9684
10019
  switch (expr.kind) {
9685
10020
  case ExpressionKind.NextContext:
9686
10021
  return nextContext(expr.steps);
@@ -9702,6 +10037,17 @@ function reifyIrExpression(expr) {
9702
10037
  throw new Error(`Read of unnamed variable ${expr.xref}`);
9703
10038
  }
9704
10039
  return variable(expr.name);
10040
+ case ExpressionKind.PureFunctionExpr:
10041
+ if (expr.fn === null) {
10042
+ throw new Error(`AssertionError: expected PureFunctions to have been extracted`);
10043
+ }
10044
+ return pureFunction(expr.varOffset, expr.fn, expr.args);
10045
+ case ExpressionKind.PureFunctionParameterExpr:
10046
+ throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
10047
+ case ExpressionKind.PipeBinding:
10048
+ return pipeBind(expr.slot, expr.varOffset, expr.args);
10049
+ case ExpressionKind.PipeBindingVariadic:
10050
+ return pipeBindV(expr.slot, expr.varOffset, expr.args);
9705
10051
  default:
9706
10052
  throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
9707
10053
  }
@@ -9805,6 +10151,9 @@ function phaseSlotAllocation(cpl) {
9805
10151
  }
9806
10152
  // Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
9807
10153
  visitExpressionsInOp(op, expr => {
10154
+ if (!isIrExpression(expr)) {
10155
+ return;
10156
+ }
9808
10157
  if (!hasUsesSlotIndexTrait(expr) || expr.slot !== null) {
9809
10158
  return;
9810
10159
  }
@@ -9835,6 +10184,13 @@ function phaseVarCounting(cpl) {
9835
10184
  varCount += varsUsedByOp(op);
9836
10185
  }
9837
10186
  visitExpressionsInOp(op, expr => {
10187
+ if (!isIrExpression(expr)) {
10188
+ return;
10189
+ }
10190
+ // Some expressions require knowledge of the number of variable slots consumed.
10191
+ if (hasUsesVarOffsetTrait(expr)) {
10192
+ expr.varOffset = varCount;
10193
+ }
9838
10194
  if (hasConsumesVarsTrait(expr)) {
9839
10195
  varCount += varsUsedByIrExpression(expr);
9840
10196
  }
@@ -9866,12 +10222,25 @@ function varsUsedByOp(op) {
9866
10222
  case OpKind.InterpolateText:
9867
10223
  // `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
9868
10224
  return op.expressions.length;
10225
+ case OpKind.InterpolateProperty:
10226
+ // `ir.InterpolatePropertyOp`s use a variable slot for each dynamic expression, plus one for
10227
+ // the result.
10228
+ return 1 + op.expressions.length;
9869
10229
  default:
9870
10230
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
9871
10231
  }
9872
10232
  }
9873
10233
  function varsUsedByIrExpression(expr) {
9874
- return 0;
10234
+ switch (expr.kind) {
10235
+ case ExpressionKind.PureFunctionExpr:
10236
+ return 1 + expr.args.length;
10237
+ case ExpressionKind.PipeBinding:
10238
+ return 1 + expr.args.length;
10239
+ case ExpressionKind.PipeBindingVariadic:
10240
+ return 1 + expr.numArgs;
10241
+ default:
10242
+ throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
10243
+ }
9875
10244
  }
9876
10245
 
9877
10246
  /**
@@ -10455,6 +10824,9 @@ function collectOpInfo(op) {
10455
10824
  let fences = Fence.None;
10456
10825
  const variablesUsed = new Set();
10457
10826
  visitExpressionsInOp(op, expr => {
10827
+ if (!isIrExpression(expr)) {
10828
+ return;
10829
+ }
10458
10830
  switch (expr.kind) {
10459
10831
  case ExpressionKind.ReadVariable:
10460
10832
  variablesUsed.add(expr.xref);
@@ -10471,6 +10843,9 @@ function collectOpInfo(op) {
10471
10843
  */
10472
10844
  function countVariableUsages(op, varUsages, varRemoteUsage) {
10473
10845
  visitExpressionsInOp(op, (expr, flags) => {
10846
+ if (!isIrExpression(expr)) {
10847
+ return;
10848
+ }
10474
10849
  if (expr.kind !== ExpressionKind.ReadVariable) {
10475
10850
  return;
10476
10851
  }
@@ -10490,6 +10865,9 @@ function countVariableUsages(op, varUsages, varRemoteUsage) {
10490
10865
  */
10491
10866
  function uncountVariableUsages(op, varUsages) {
10492
10867
  visitExpressionsInOp(op, expr => {
10868
+ if (!isIrExpression(expr)) {
10869
+ return;
10870
+ }
10493
10871
  if (expr.kind !== ExpressionKind.ReadVariable) {
10494
10872
  return;
10495
10873
  }
@@ -10539,6 +10917,9 @@ function tryInlineVariableInitializer(id, initializer, target, declFences) {
10539
10917
  let inlined = false;
10540
10918
  let inliningAllowed = true;
10541
10919
  transformExpressionsInOp(target, (expr, flags) => {
10920
+ if (!isIrExpression(expr)) {
10921
+ return expr;
10922
+ }
10542
10923
  if (inlined || !inliningAllowed) {
10543
10924
  // Either the inlining has already succeeded, or we've passed a fence that disallows inlining
10544
10925
  // at this point, so don't try.
@@ -10693,6 +11074,9 @@ function mergeNextContextsInOps(ops) {
10693
11074
  let tryToMerge = true;
10694
11075
  for (let candidate = op.next; candidate.kind !== OpKind.ListEnd && tryToMerge; candidate = candidate.next) {
10695
11076
  visitExpressionsInOp(candidate, (expr, flags) => {
11077
+ if (!isIrExpression(expr)) {
11078
+ return expr;
11079
+ }
10696
11080
  if (!tryToMerge) {
10697
11081
  // Either we've already merged, or failed to merge.
10698
11082
  return;
@@ -10777,11 +11161,205 @@ function phaseSaveRestoreView(cpl) {
10777
11161
  }
10778
11162
  }
10779
11163
 
11164
+ function phasePureFunctionExtraction(cpl) {
11165
+ for (const view of cpl.views.values()) {
11166
+ for (const op of view.ops()) {
11167
+ visitExpressionsInOp(op, expr => {
11168
+ if (!(expr instanceof PureFunctionExpr) || expr.body === null) {
11169
+ return;
11170
+ }
11171
+ const constantDef = new PureFunctionConstant(expr.args.length);
11172
+ expr.fn = cpl.pool.getSharedConstant(constantDef, expr.body);
11173
+ expr.body = null;
11174
+ });
11175
+ }
11176
+ }
11177
+ }
11178
+ class PureFunctionConstant extends GenericKeyFn {
11179
+ constructor(numArgs) {
11180
+ super();
11181
+ this.numArgs = numArgs;
11182
+ }
11183
+ keyOf(expr) {
11184
+ if (expr instanceof PureFunctionParameterExpr) {
11185
+ return `param(${expr.index})`;
11186
+ }
11187
+ else {
11188
+ return super.keyOf(expr);
11189
+ }
11190
+ }
11191
+ toSharedConstantDeclaration(declName, keyExpr) {
11192
+ const fnParams = [];
11193
+ for (let idx = 0; idx < this.numArgs; idx++) {
11194
+ fnParams.push(new FnParam('_p' + idx));
11195
+ }
11196
+ // We will never visit `ir.PureFunctionParameterExpr`s that don't belong to us, because this
11197
+ // transform runs inside another visitor which will visit nested pure functions before this one.
11198
+ const returnExpr = transformExpressionsInExpression(keyExpr, expr => {
11199
+ if (!(expr instanceof PureFunctionParameterExpr)) {
11200
+ return expr;
11201
+ }
11202
+ return variable('_p' + expr.index);
11203
+ }, VisitorContextFlag.None);
11204
+ return new DeclareFunctionStmt(declName, fnParams, [new ReturnStatement(returnExpr)]);
11205
+ }
11206
+ }
11207
+
11208
+ function phasePipeCreation(cpl) {
11209
+ for (const view of cpl.views.values()) {
11210
+ processPipeBindingsInView(view);
11211
+ }
11212
+ }
11213
+ function processPipeBindingsInView(view) {
11214
+ for (const updateOp of view.update) {
11215
+ visitExpressionsInOp(updateOp, (expr, flags) => {
11216
+ if (!isIrExpression(expr)) {
11217
+ return;
11218
+ }
11219
+ if (expr.kind !== ExpressionKind.PipeBinding) {
11220
+ return;
11221
+ }
11222
+ if (flags & VisitorContextFlag.InChildOperation) {
11223
+ throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
11224
+ }
11225
+ if (!hasDependsOnSlotContextTrait(updateOp)) {
11226
+ throw new Error(`AssertionError: pipe binding associated with non-slot operation ${OpKind[updateOp.kind]}`);
11227
+ }
11228
+ addPipeToCreationBlock(view, updateOp.target, expr);
11229
+ });
11230
+ }
11231
+ }
11232
+ function addPipeToCreationBlock(view, afterTargetXref, binding) {
11233
+ // Find the appropriate point to insert the Pipe creation operation.
11234
+ // We're looking for `afterTargetXref` (and also want to insert after any other pipe operations
11235
+ // which might be beyond it).
11236
+ for (let op = view.create.head.next; op.kind !== OpKind.ListEnd; op = op.next) {
11237
+ if (!hasConsumesSlotTrait(op)) {
11238
+ continue;
11239
+ }
11240
+ if (op.xref !== afterTargetXref) {
11241
+ continue;
11242
+ }
11243
+ // We've found a tentative insertion point; however, we also want to skip past any _other_ pipe
11244
+ // operations present.
11245
+ while (op.next.kind === OpKind.Pipe) {
11246
+ op = op.next;
11247
+ }
11248
+ const pipe = createPipeOp(binding.target, binding.name);
11249
+ OpList.insertBefore(pipe, op.next);
11250
+ // This completes adding the pipe to the creation block.
11251
+ return;
11252
+ }
11253
+ // At this point, we've failed to add the pipe to the creation block.
11254
+ throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`);
11255
+ }
11256
+
11257
+ function phasePipeVariadic(cpl) {
11258
+ for (const view of cpl.views.values()) {
11259
+ for (const op of view.update) {
11260
+ transformExpressionsInOp(op, expr => {
11261
+ if (!(expr instanceof PipeBindingExpr)) {
11262
+ return expr;
11263
+ }
11264
+ // Pipes are variadic if they have more than 4 arguments.
11265
+ if (expr.args.length <= 4) {
11266
+ return expr;
11267
+ }
11268
+ return new PipeBindingVariadicExpr(expr.target, expr.name, literalArr(expr.args), expr.args.length);
11269
+ }, VisitorContextFlag.None);
11270
+ }
11271
+ }
11272
+ }
11273
+
11274
+ function phasePureLiteralStructures(cpl) {
11275
+ for (const view of cpl.views.values()) {
11276
+ for (const op of view.update) {
11277
+ transformExpressionsInOp(op, (expr, flags) => {
11278
+ if (flags & VisitorContextFlag.InChildOperation) {
11279
+ return expr;
11280
+ }
11281
+ if (expr instanceof LiteralArrayExpr) {
11282
+ return transformLiteralArray(expr);
11283
+ }
11284
+ else if (expr instanceof LiteralMapExpr) {
11285
+ return transformLiteralMap(expr);
11286
+ }
11287
+ return expr;
11288
+ }, VisitorContextFlag.None);
11289
+ }
11290
+ }
11291
+ }
11292
+ function transformLiteralArray(expr) {
11293
+ const derivedEntries = [];
11294
+ const nonConstantArgs = [];
11295
+ for (const entry of expr.entries) {
11296
+ if (entry.isConstant()) {
11297
+ derivedEntries.push(entry);
11298
+ }
11299
+ else {
11300
+ const idx = nonConstantArgs.length;
11301
+ nonConstantArgs.push(entry);
11302
+ derivedEntries.push(new PureFunctionParameterExpr(idx));
11303
+ }
11304
+ }
11305
+ return new PureFunctionExpr(literalArr(derivedEntries), nonConstantArgs);
11306
+ }
11307
+ function transformLiteralMap(expr) {
11308
+ let derivedEntries = [];
11309
+ const nonConstantArgs = [];
11310
+ for (const entry of expr.entries) {
11311
+ if (entry.value.isConstant()) {
11312
+ derivedEntries.push(entry);
11313
+ }
11314
+ else {
11315
+ const idx = nonConstantArgs.length;
11316
+ nonConstantArgs.push(entry.value);
11317
+ derivedEntries.push(new LiteralMapEntry(entry.key, new PureFunctionParameterExpr(idx), entry.quoted));
11318
+ }
11319
+ }
11320
+ return new PureFunctionExpr(literalMap(derivedEntries), nonConstantArgs);
11321
+ }
11322
+
11323
+ function phaseAlignPipeVariadicVarOffset(cpl) {
11324
+ for (const view of cpl.views.values()) {
11325
+ for (const op of view.update) {
11326
+ visitExpressionsInOp(op, expr => {
11327
+ if (!(expr instanceof PipeBindingVariadicExpr)) {
11328
+ return expr;
11329
+ }
11330
+ if (!(expr.args instanceof PureFunctionExpr)) {
11331
+ return expr;
11332
+ }
11333
+ if (expr.varOffset === null || expr.args.varOffset === null) {
11334
+ throw new Error(`Must run after variable counting`);
11335
+ }
11336
+ // The structure of this variadic pipe expression is:
11337
+ // PipeBindingVariadic(#, Y, PureFunction(X, ...ARGS))
11338
+ // Where X and Y are the slot offsets for the variables used by these operations, and Y > X.
11339
+ // In `TemplateDefinitionBuilder` the PipeBindingVariadic variable slots are allocated
11340
+ // before the PureFunction slots, which is unusually out-of-order.
11341
+ //
11342
+ // To maintain identical output for the tests in question, we adjust the variable offsets of
11343
+ // these two calls to emulate TDB's behavior. This is not perfect, because the ARGS of the
11344
+ // PureFunction call may also allocate slots which by TDB's ordering would come after X, and
11345
+ // we don't account for that. Still, this should be enough to pass the existing pipe tests.
11346
+ // Put the PipeBindingVariadic vars where the PureFunction vars were previously allocated.
11347
+ expr.varOffset = expr.args.varOffset;
11348
+ // Put the PureFunction vars following the PipeBindingVariadic vars.
11349
+ expr.args.varOffset = expr.varOffset + varsUsedByIrExpression(expr);
11350
+ });
11351
+ }
11352
+ }
11353
+ }
11354
+
10780
11355
  /**
10781
11356
  * Run all transformation phases in the correct order against a `ComponentCompilation`. After this
10782
11357
  * processing, the compilation should be in a state where it can be emitted via `emitTemplateFn`.s
10783
11358
  */
10784
11359
  function transformTemplate(cpl) {
11360
+ phasePipeCreation(cpl);
11361
+ phasePipeVariadic(cpl);
11362
+ phasePureLiteralStructures(cpl);
10785
11363
  phaseGenerateVariables(cpl);
10786
11364
  phaseSaveRestoreView(cpl);
10787
11365
  phaseResolveNames(cpl);
@@ -10796,6 +11374,8 @@ function transformTemplate(cpl) {
10796
11374
  phaseMergeNextContext(cpl);
10797
11375
  phaseNgContainer(cpl);
10798
11376
  phaseEmptyElements(cpl);
11377
+ phasePureFunctionExtraction(cpl);
11378
+ phaseAlignPipeVariadicVarOffset(cpl);
10799
11379
  phaseReify(cpl);
10800
11380
  phaseChaining(cpl);
10801
11381
  }
@@ -10866,8 +11446,9 @@ function maybeGenerateRfBlock(flag, statements) {
10866
11446
  * embedded views or host bindings.
10867
11447
  */
10868
11448
  class ComponentCompilation {
10869
- constructor(componentName) {
11449
+ constructor(componentName, pool) {
10870
11450
  this.componentName = componentName;
11451
+ this.pool = pool;
10871
11452
  /**
10872
11453
  * Tracks the next `ir.XrefId` which can be assigned as template structures are ingested.
10873
11454
  */
@@ -10999,8 +11580,8 @@ const BINARY_OPERATORS = new Map([
10999
11580
  * Process a template AST and convert it into a `ComponentCompilation` in the intermediate
11000
11581
  * representation.
11001
11582
  */
11002
- function ingest(componentName, template) {
11003
- const cpl = new ComponentCompilation(componentName);
11583
+ function ingest(componentName, template, constantPool) {
11584
+ const cpl = new ComponentCompilation(componentName, constantPool);
11004
11585
  ingestNodes(cpl.root, template);
11005
11586
  return cpl;
11006
11587
  }
@@ -11095,6 +11676,12 @@ function convertAst(ast, cpl) {
11095
11676
  return new ReadPropExpr(convertAst(ast.receiver, cpl), ast.name);
11096
11677
  }
11097
11678
  }
11679
+ else if (ast instanceof PropertyWrite) {
11680
+ return new WritePropExpr(convertAst(ast.receiver, cpl), ast.name, convertAst(ast.value, cpl));
11681
+ }
11682
+ else if (ast instanceof KeyedWrite) {
11683
+ return new WriteKeyExpr(convertAst(ast.receiver, cpl), convertAst(ast.key, cpl), convertAst(ast.value, cpl));
11684
+ }
11098
11685
  else if (ast instanceof Call) {
11099
11686
  if (ast.receiver instanceof ImplicitReceiver) {
11100
11687
  throw new Error(`Unexpected ImplicitReceiver`);
@@ -11122,6 +11709,25 @@ function convertAst(ast, cpl) {
11122
11709
  else if (ast instanceof Chain) {
11123
11710
  throw new Error(`AssertionError: Chain in unknown context`);
11124
11711
  }
11712
+ else if (ast instanceof LiteralMap) {
11713
+ const entries = ast.keys.map((key, idx) => {
11714
+ const value = ast.values[idx];
11715
+ return new LiteralMapEntry(key.key, convertAst(value, cpl), key.quoted);
11716
+ });
11717
+ return new LiteralMapExpr(entries);
11718
+ }
11719
+ else if (ast instanceof LiteralArray) {
11720
+ return new LiteralArrayExpr(ast.expressions.map(expr => convertAst(expr, cpl)));
11721
+ }
11722
+ else if (ast instanceof Conditional) {
11723
+ return new ConditionalExpr(convertAst(ast.condition, cpl), convertAst(ast.trueExp, cpl), convertAst(ast.falseExp, cpl));
11724
+ }
11725
+ else if (ast instanceof BindingPipe) {
11726
+ return new PipeBindingExpr(cpl.allocateXrefId(), ast.name, [
11727
+ convertAst(ast.exp, cpl),
11728
+ ...ast.args.map(arg => convertAst(arg, cpl)),
11729
+ ]);
11730
+ }
11125
11731
  else {
11126
11732
  throw new Error(`Unhandled expression type: ${ast.constructor.name}`);
11127
11733
  }
@@ -11154,19 +11760,16 @@ function ingestAttributes(op, element) {
11154
11760
  */
11155
11761
  function ingestBindings(view, op, element) {
11156
11762
  if (element instanceof Template) {
11157
- // TODO: Are ng-template inputs handled differently from element inputs?
11158
- // <ng-template dir [foo]="...">
11159
- // <item-cmp *ngFor="let item of items" [item]="item">
11160
- for (const input of [...element.templateAttrs, ...element.inputs]) {
11161
- if (!(input instanceof BoundAttribute)) {
11763
+ for (const attr of [...element.templateAttrs, ...element.inputs]) {
11764
+ if (!(attr instanceof BoundAttribute)) {
11162
11765
  continue;
11163
11766
  }
11164
- view.update.push(createPropertyOp(op.xref, input.name, convertAst(input.value, view.tpl)));
11767
+ ingestPropertyBinding(view, op.xref, attr.name, attr.value);
11165
11768
  }
11166
11769
  }
11167
11770
  else {
11168
11771
  for (const input of element.inputs) {
11169
- view.update.push(createPropertyOp(op.xref, input.name, convertAst(input.value, view.tpl)));
11772
+ ingestPropertyBinding(view, op.xref, input.name, input.value);
11170
11773
  }
11171
11774
  for (const output of element.outputs) {
11172
11775
  const listenerOp = createListenerOp(op.xref, output.name, op.tag);
@@ -11197,6 +11800,17 @@ function ingestBindings(view, op, element) {
11197
11800
  }
11198
11801
  }
11199
11802
  }
11803
+ function ingestPropertyBinding(view, xref, name, value) {
11804
+ if (value instanceof ASTWithSource) {
11805
+ value = value.ast;
11806
+ }
11807
+ if (value instanceof Interpolation) {
11808
+ view.update.push(createInterpolatePropertyOp(xref, name, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl))));
11809
+ }
11810
+ else {
11811
+ view.update.push(createPropertyOp(xref, name, convertAst(value, view.tpl)));
11812
+ }
11813
+ }
11200
11814
  /**
11201
11815
  * Process all of the local references on an element-like structure in the template AST and convert
11202
11816
  * them to their IR representation.
@@ -21579,7 +22193,7 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
21579
22193
  else {
21580
22194
  // This path compiles the template using the prototype template pipeline. First the template is
21581
22195
  // ingested into IR:
21582
- const tpl = ingest(meta.name, meta.template.nodes);
22196
+ const tpl = ingest(meta.name, meta.template.nodes, constantPool);
21583
22197
  // Then the IR is transformed to prepare it for cod egeneration.
21584
22198
  transformTemplate(tpl);
21585
22199
  // Finally we emit the template function:
@@ -22670,7 +23284,7 @@ function inputsMappingToInputMetadata(inputs) {
22670
23284
  result[key] = {
22671
23285
  bindingPropertyName: value[0],
22672
23286
  classPropertyName: value[1],
22673
- transformFunction: value[2] || null,
23287
+ transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null,
22674
23288
  required: false,
22675
23289
  };
22676
23290
  }
@@ -22745,7 +23359,7 @@ function publishFacade(global) {
22745
23359
  * @description
22746
23360
  * Entry point for all public APIs of the compiler package.
22747
23361
  */
22748
- const VERSION = new Version('16.1.0-next.3');
23362
+ const VERSION = new Version('16.1.0');
22749
23363
 
22750
23364
  class CompilerConfig {
22751
23365
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -24673,7 +25287,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
24673
25287
  function compileDeclareClassMetadata(metadata) {
24674
25288
  const definitionMap = new DefinitionMap();
24675
25289
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
24676
- definitionMap.set('version', literal('16.1.0-next.3'));
25290
+ definitionMap.set('version', literal('16.1.0'));
24677
25291
  definitionMap.set('ngImport', importExpr(Identifiers.core));
24678
25292
  definitionMap.set('type', metadata.type);
24679
25293
  definitionMap.set('decorators', metadata.decorators);
@@ -24776,7 +25390,7 @@ function compileDeclareDirectiveFromMetadata(meta) {
24776
25390
  function createDirectiveDefinitionMap(meta) {
24777
25391
  const definitionMap = new DefinitionMap();
24778
25392
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
24779
- definitionMap.set('version', literal('16.1.0-next.3'));
25393
+ definitionMap.set('version', literal('16.1.0'));
24780
25394
  // e.g. `type: MyDirective`
24781
25395
  definitionMap.set('type', meta.type.value);
24782
25396
  if (meta.isStandalone) {
@@ -25004,7 +25618,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
25004
25618
  function compileDeclareFactoryFunction(meta) {
25005
25619
  const definitionMap = new DefinitionMap();
25006
25620
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
25007
- definitionMap.set('version', literal('16.1.0-next.3'));
25621
+ definitionMap.set('version', literal('16.1.0'));
25008
25622
  definitionMap.set('ngImport', importExpr(Identifiers.core));
25009
25623
  definitionMap.set('type', meta.type.value);
25010
25624
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -25039,7 +25653,7 @@ function compileDeclareInjectableFromMetadata(meta) {
25039
25653
  function createInjectableDefinitionMap(meta) {
25040
25654
  const definitionMap = new DefinitionMap();
25041
25655
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
25042
- definitionMap.set('version', literal('16.1.0-next.3'));
25656
+ definitionMap.set('version', literal('16.1.0'));
25043
25657
  definitionMap.set('ngImport', importExpr(Identifiers.core));
25044
25658
  definitionMap.set('type', meta.type.value);
25045
25659
  // Only generate providedIn property if it has a non-null value
@@ -25090,7 +25704,7 @@ function compileDeclareInjectorFromMetadata(meta) {
25090
25704
  function createInjectorDefinitionMap(meta) {
25091
25705
  const definitionMap = new DefinitionMap();
25092
25706
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
25093
- definitionMap.set('version', literal('16.1.0-next.3'));
25707
+ definitionMap.set('version', literal('16.1.0'));
25094
25708
  definitionMap.set('ngImport', importExpr(Identifiers.core));
25095
25709
  definitionMap.set('type', meta.type.value);
25096
25710
  definitionMap.set('providers', meta.providers);
@@ -25120,7 +25734,7 @@ function compileDeclareNgModuleFromMetadata(meta) {
25120
25734
  function createNgModuleDefinitionMap(meta) {
25121
25735
  const definitionMap = new DefinitionMap();
25122
25736
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
25123
- definitionMap.set('version', literal('16.1.0-next.3'));
25737
+ definitionMap.set('version', literal('16.1.0'));
25124
25738
  definitionMap.set('ngImport', importExpr(Identifiers.core));
25125
25739
  definitionMap.set('type', meta.type.value);
25126
25740
  // We only generate the keys in the metadata if the arrays contain values.
@@ -25171,7 +25785,7 @@ function compileDeclarePipeFromMetadata(meta) {
25171
25785
  function createPipeDefinitionMap(meta) {
25172
25786
  const definitionMap = new DefinitionMap();
25173
25787
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
25174
- definitionMap.set('version', literal('16.1.0-next.3'));
25788
+ definitionMap.set('version', literal('16.1.0'));
25175
25789
  definitionMap.set('ngImport', importExpr(Identifiers.core));
25176
25790
  // e.g. `type: MyPipe`
25177
25791
  definitionMap.set('type', meta.type.value);