@angular/compiler 17.0.0-rc.0 → 17.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/ml_parser/ast.mjs +3 -2
- package/esm2022/src/ml_parser/html_whitespaces.mjs +2 -2
- package/esm2022/src/ml_parser/icu_ast_expander.mjs +2 -2
- package/esm2022/src/ml_parser/lexer.mjs +2 -13
- package/esm2022/src/ml_parser/parser.mjs +3 -3
- package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
- package/esm2022/src/render3/partial/directive.mjs +1 -1
- package/esm2022/src/render3/partial/factory.mjs +1 -1
- package/esm2022/src/render3/partial/injectable.mjs +1 -1
- package/esm2022/src/render3/partial/injector.mjs +1 -1
- package/esm2022/src/render3/partial/ng_module.mjs +1 -1
- package/esm2022/src/render3/partial/pipe.mjs +1 -1
- package/esm2022/src/render3/r3_ast.mjs +3 -2
- package/esm2022/src/render3/r3_control_flow.mjs +2 -2
- package/esm2022/src/render3/r3_template_transform.mjs +2 -2
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +59 -28
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +63 -3
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +27 -6
- package/esm2022/src/template/pipeline/ir/src/ops/shared.mjs +3 -2
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +12 -2
- package/esm2022/src/template/pipeline/ir/src/variable.mjs +1 -1
- package/esm2022/src/template/pipeline/src/compilation.mjs +6 -1
- package/esm2022/src/template/pipeline/src/emit.mjs +13 -4
- package/esm2022/src/template/pipeline/src/ingest.mjs +52 -4
- package/esm2022/src/template/pipeline/src/instruction.mjs +27 -6
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +1 -1
- package/esm2022/src/template/pipeline/src/phases/collapse_singleton_interpolations.mjs +31 -0
- package/esm2022/src/template/pipeline/src/phases/conditionals.mjs +4 -1
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +11 -5
- package/esm2022/src/template/pipeline/src/phases/naming.mjs +21 -3
- package/esm2022/src/template/pipeline/src/phases/ordering.mjs +40 -31
- package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +8 -8
- package/esm2022/src/template/pipeline/src/phases/pipe_creation.mjs +15 -3
- package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +2 -1
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +33 -2
- package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +40 -0
- package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +5 -1
- package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +2 -1
- package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +4 -4
- package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +49 -0
- package/esm2022/src/template/pipeline/src/phases/track_fn_optimization.mjs +75 -0
- package/esm2022/src/template/pipeline/src/phases/track_variables.mjs +31 -0
- package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +36 -3
- package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +43 -3
- package/esm2022/src/template/pipeline/src/util/elements.mjs +6 -6
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +779 -273
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +5 -3
- package/package.json +2 -2
- package/esm2022/src/template/pipeline/src/phases/align_pipe_variadic_var_offset.mjs +0 -42
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v17.0.0-rc.
|
|
2
|
+
* @license Angular v17.0.0-rc.1
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -3983,9 +3983,10 @@ class IfBlockBranch {
|
|
|
3983
3983
|
}
|
|
3984
3984
|
}
|
|
3985
3985
|
class UnknownBlock {
|
|
3986
|
-
constructor(name, sourceSpan) {
|
|
3986
|
+
constructor(name, sourceSpan, nameSpan) {
|
|
3987
3987
|
this.name = name;
|
|
3988
3988
|
this.sourceSpan = sourceSpan;
|
|
3989
|
+
this.nameSpan = nameSpan;
|
|
3989
3990
|
}
|
|
3990
3991
|
visit(visitor) {
|
|
3991
3992
|
return visitor.visitUnknownBlock(this);
|
|
@@ -8826,34 +8827,42 @@ var OpKind;
|
|
|
8826
8827
|
* Create a content projection slot.
|
|
8827
8828
|
*/
|
|
8828
8829
|
OpKind[OpKind["Projection"] = 33] = "Projection";
|
|
8830
|
+
/**
|
|
8831
|
+
* Create a repeater creation instruction op.
|
|
8832
|
+
*/
|
|
8833
|
+
OpKind[OpKind["RepeaterCreate"] = 34] = "RepeaterCreate";
|
|
8834
|
+
/**
|
|
8835
|
+
* An update up for a repeater.
|
|
8836
|
+
*/
|
|
8837
|
+
OpKind[OpKind["Repeater"] = 35] = "Repeater";
|
|
8829
8838
|
/**
|
|
8830
8839
|
* The start of an i18n block.
|
|
8831
8840
|
*/
|
|
8832
|
-
OpKind[OpKind["I18nStart"] =
|
|
8841
|
+
OpKind[OpKind["I18nStart"] = 36] = "I18nStart";
|
|
8833
8842
|
/**
|
|
8834
8843
|
* A self-closing i18n on a single element.
|
|
8835
8844
|
*/
|
|
8836
|
-
OpKind[OpKind["I18n"] =
|
|
8845
|
+
OpKind[OpKind["I18n"] = 37] = "I18n";
|
|
8837
8846
|
/**
|
|
8838
8847
|
* The end of an i18n block.
|
|
8839
8848
|
*/
|
|
8840
|
-
OpKind[OpKind["I18nEnd"] =
|
|
8849
|
+
OpKind[OpKind["I18nEnd"] = 38] = "I18nEnd";
|
|
8841
8850
|
/**
|
|
8842
8851
|
* An expression in an i18n message.
|
|
8843
8852
|
*/
|
|
8844
|
-
OpKind[OpKind["I18nExpression"] =
|
|
8853
|
+
OpKind[OpKind["I18nExpression"] = 39] = "I18nExpression";
|
|
8845
8854
|
/**
|
|
8846
8855
|
* An instruction that applies a set of i18n expressions.
|
|
8847
8856
|
*/
|
|
8848
|
-
OpKind[OpKind["I18nApply"] =
|
|
8857
|
+
OpKind[OpKind["I18nApply"] = 40] = "I18nApply";
|
|
8849
8858
|
/**
|
|
8850
8859
|
* An instruction to create an ICU expression.
|
|
8851
8860
|
*/
|
|
8852
|
-
OpKind[OpKind["Icu"] =
|
|
8861
|
+
OpKind[OpKind["Icu"] = 41] = "Icu";
|
|
8853
8862
|
/**
|
|
8854
8863
|
* An instruction to update an ICU expression.
|
|
8855
8864
|
*/
|
|
8856
|
-
OpKind[OpKind["IcuUpdate"] =
|
|
8865
|
+
OpKind[OpKind["IcuUpdate"] = 42] = "IcuUpdate";
|
|
8857
8866
|
})(OpKind || (OpKind = {}));
|
|
8858
8867
|
/**
|
|
8859
8868
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -8868,87 +8877,106 @@ var ExpressionKind;
|
|
|
8868
8877
|
* A reference to the current view context.
|
|
8869
8878
|
*/
|
|
8870
8879
|
ExpressionKind[ExpressionKind["Context"] = 1] = "Context";
|
|
8880
|
+
/**
|
|
8881
|
+
* A reference to the view context, for use inside a track function.
|
|
8882
|
+
*/
|
|
8883
|
+
ExpressionKind[ExpressionKind["TrackContext"] = 2] = "TrackContext";
|
|
8871
8884
|
/**
|
|
8872
8885
|
* Read of a variable declared in a `VariableOp`.
|
|
8873
8886
|
*/
|
|
8874
|
-
ExpressionKind[ExpressionKind["ReadVariable"] =
|
|
8887
|
+
ExpressionKind[ExpressionKind["ReadVariable"] = 3] = "ReadVariable";
|
|
8875
8888
|
/**
|
|
8876
8889
|
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
8877
8890
|
*/
|
|
8878
|
-
ExpressionKind[ExpressionKind["NextContext"] =
|
|
8891
|
+
ExpressionKind[ExpressionKind["NextContext"] = 4] = "NextContext";
|
|
8879
8892
|
/**
|
|
8880
8893
|
* Runtime operation to retrieve the value of a local reference.
|
|
8881
8894
|
*/
|
|
8882
|
-
ExpressionKind[ExpressionKind["Reference"] =
|
|
8895
|
+
ExpressionKind[ExpressionKind["Reference"] = 5] = "Reference";
|
|
8883
8896
|
/**
|
|
8884
8897
|
* Runtime operation to snapshot the current view context.
|
|
8885
8898
|
*/
|
|
8886
|
-
ExpressionKind[ExpressionKind["GetCurrentView"] =
|
|
8899
|
+
ExpressionKind[ExpressionKind["GetCurrentView"] = 6] = "GetCurrentView";
|
|
8887
8900
|
/**
|
|
8888
8901
|
* Runtime operation to restore a snapshotted view.
|
|
8889
8902
|
*/
|
|
8890
|
-
ExpressionKind[ExpressionKind["RestoreView"] =
|
|
8903
|
+
ExpressionKind[ExpressionKind["RestoreView"] = 7] = "RestoreView";
|
|
8891
8904
|
/**
|
|
8892
8905
|
* Runtime operation to reset the current view context after `RestoreView`.
|
|
8893
8906
|
*/
|
|
8894
|
-
ExpressionKind[ExpressionKind["ResetView"] =
|
|
8907
|
+
ExpressionKind[ExpressionKind["ResetView"] = 8] = "ResetView";
|
|
8895
8908
|
/**
|
|
8896
8909
|
* Defines and calls a function with change-detected arguments.
|
|
8897
8910
|
*/
|
|
8898
|
-
ExpressionKind[ExpressionKind["PureFunctionExpr"] =
|
|
8911
|
+
ExpressionKind[ExpressionKind["PureFunctionExpr"] = 9] = "PureFunctionExpr";
|
|
8899
8912
|
/**
|
|
8900
8913
|
* Indicates a positional parameter to a pure function definition.
|
|
8901
8914
|
*/
|
|
8902
|
-
ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] =
|
|
8915
|
+
ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] = 10] = "PureFunctionParameterExpr";
|
|
8903
8916
|
/**
|
|
8904
8917
|
* Binding to a pipe transformation.
|
|
8905
8918
|
*/
|
|
8906
|
-
ExpressionKind[ExpressionKind["PipeBinding"] =
|
|
8919
|
+
ExpressionKind[ExpressionKind["PipeBinding"] = 11] = "PipeBinding";
|
|
8907
8920
|
/**
|
|
8908
8921
|
* Binding to a pipe transformation with a variable number of arguments.
|
|
8909
8922
|
*/
|
|
8910
|
-
ExpressionKind[ExpressionKind["PipeBindingVariadic"] =
|
|
8923
|
+
ExpressionKind[ExpressionKind["PipeBindingVariadic"] = 12] = "PipeBindingVariadic";
|
|
8911
8924
|
/*
|
|
8912
8925
|
* A safe property read requiring expansion into a null check.
|
|
8913
8926
|
*/
|
|
8914
|
-
ExpressionKind[ExpressionKind["SafePropertyRead"] =
|
|
8927
|
+
ExpressionKind[ExpressionKind["SafePropertyRead"] = 13] = "SafePropertyRead";
|
|
8915
8928
|
/**
|
|
8916
8929
|
* A safe keyed read requiring expansion into a null check.
|
|
8917
8930
|
*/
|
|
8918
|
-
ExpressionKind[ExpressionKind["SafeKeyedRead"] =
|
|
8931
|
+
ExpressionKind[ExpressionKind["SafeKeyedRead"] = 14] = "SafeKeyedRead";
|
|
8919
8932
|
/**
|
|
8920
8933
|
* A safe function call requiring expansion into a null check.
|
|
8921
8934
|
*/
|
|
8922
|
-
ExpressionKind[ExpressionKind["SafeInvokeFunction"] =
|
|
8935
|
+
ExpressionKind[ExpressionKind["SafeInvokeFunction"] = 15] = "SafeInvokeFunction";
|
|
8923
8936
|
/**
|
|
8924
8937
|
* An intermediate expression that will be expanded from a safe read into an explicit ternary.
|
|
8925
8938
|
*/
|
|
8926
|
-
ExpressionKind[ExpressionKind["SafeTernaryExpr"] =
|
|
8939
|
+
ExpressionKind[ExpressionKind["SafeTernaryExpr"] = 16] = "SafeTernaryExpr";
|
|
8927
8940
|
/**
|
|
8928
8941
|
* An empty expression that will be stipped before generating the final output.
|
|
8929
8942
|
*/
|
|
8930
|
-
ExpressionKind[ExpressionKind["EmptyExpr"] =
|
|
8943
|
+
ExpressionKind[ExpressionKind["EmptyExpr"] = 17] = "EmptyExpr";
|
|
8931
8944
|
/*
|
|
8932
8945
|
* An assignment to a temporary variable.
|
|
8933
8946
|
*/
|
|
8934
|
-
ExpressionKind[ExpressionKind["AssignTemporaryExpr"] =
|
|
8947
|
+
ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 18] = "AssignTemporaryExpr";
|
|
8935
8948
|
/**
|
|
8936
8949
|
* A reference to a temporary variable.
|
|
8937
8950
|
*/
|
|
8938
|
-
ExpressionKind[ExpressionKind["ReadTemporaryExpr"] =
|
|
8951
|
+
ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 19] = "ReadTemporaryExpr";
|
|
8939
8952
|
/**
|
|
8940
8953
|
* An expression representing a sanitizer function.
|
|
8941
8954
|
*/
|
|
8942
|
-
ExpressionKind[ExpressionKind["SanitizerExpr"] =
|
|
8955
|
+
ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
|
|
8943
8956
|
/**
|
|
8944
8957
|
* An expression that will cause a literal slot index to be emitted.
|
|
8945
8958
|
*/
|
|
8946
|
-
ExpressionKind[ExpressionKind["SlotLiteralExpr"] =
|
|
8959
|
+
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
|
|
8947
8960
|
/**
|
|
8948
8961
|
* A test expression for a conditional op.
|
|
8949
8962
|
*/
|
|
8950
|
-
ExpressionKind[ExpressionKind["ConditionalCase"] =
|
|
8963
|
+
ExpressionKind[ExpressionKind["ConditionalCase"] = 22] = "ConditionalCase";
|
|
8964
|
+
/**
|
|
8965
|
+
* A variable for use inside a repeater, providing one of the ambiently-available context
|
|
8966
|
+
* properties ($even, $first, etc.).
|
|
8967
|
+
*/
|
|
8968
|
+
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
|
|
8951
8969
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
8970
|
+
var VariableFlags;
|
|
8971
|
+
(function (VariableFlags) {
|
|
8972
|
+
VariableFlags[VariableFlags["None"] = 0] = "None";
|
|
8973
|
+
/**
|
|
8974
|
+
* Always inline this variable, regardless of the number of times it's used.
|
|
8975
|
+
* An `AlwaysInline` variable may not depend on context, because doing so may cause side effects
|
|
8976
|
+
* that are illegal when multi-inlined. (The optimizer will enforce this constraint.)
|
|
8977
|
+
*/
|
|
8978
|
+
VariableFlags[VariableFlags["AlwaysInline"] = 1] = "AlwaysInline";
|
|
8979
|
+
})(VariableFlags || (VariableFlags = {}));
|
|
8952
8980
|
/**
|
|
8953
8981
|
* Distinguishes between different kinds of `SemanticVariable`s.
|
|
8954
8982
|
*/
|
|
@@ -8966,6 +8994,10 @@ var SemanticVariableKind;
|
|
|
8966
8994
|
* Represents a saved state that can be used to restore a view in a listener handler function.
|
|
8967
8995
|
*/
|
|
8968
8996
|
SemanticVariableKind[SemanticVariableKind["SavedView"] = 2] = "SavedView";
|
|
8997
|
+
/**
|
|
8998
|
+
* An alias generated by a special embedded view type (e.g. a `@for` block).
|
|
8999
|
+
*/
|
|
9000
|
+
SemanticVariableKind[SemanticVariableKind["Alias"] = 3] = "Alias";
|
|
8969
9001
|
})(SemanticVariableKind || (SemanticVariableKind = {}));
|
|
8970
9002
|
/**
|
|
8971
9003
|
* Whether to compile in compatibilty mode. In compatibility mode, the template pipeline will
|
|
@@ -9161,12 +9193,13 @@ function createStatementOp(statement) {
|
|
|
9161
9193
|
/**
|
|
9162
9194
|
* Create a `VariableOp`.
|
|
9163
9195
|
*/
|
|
9164
|
-
function createVariableOp(xref, variable, initializer) {
|
|
9196
|
+
function createVariableOp(xref, variable, initializer, flags) {
|
|
9165
9197
|
return {
|
|
9166
9198
|
kind: OpKind.Variable,
|
|
9167
9199
|
xref,
|
|
9168
9200
|
variable,
|
|
9169
9201
|
initializer,
|
|
9202
|
+
flags,
|
|
9170
9203
|
...NEW_OP,
|
|
9171
9204
|
};
|
|
9172
9205
|
}
|
|
@@ -9343,6 +9376,16 @@ function createConditionalOp(target, test, conditions, sourceSpan) {
|
|
|
9343
9376
|
...TRAIT_CONSUMES_VARS,
|
|
9344
9377
|
};
|
|
9345
9378
|
}
|
|
9379
|
+
function createRepeaterOp(repeaterCreate, collection, sourceSpan) {
|
|
9380
|
+
return {
|
|
9381
|
+
kind: OpKind.Repeater,
|
|
9382
|
+
target: repeaterCreate,
|
|
9383
|
+
collection,
|
|
9384
|
+
sourceSpan,
|
|
9385
|
+
...NEW_OP,
|
|
9386
|
+
...TRAIT_USES_SLOT_INDEX,
|
|
9387
|
+
};
|
|
9388
|
+
}
|
|
9346
9389
|
/**
|
|
9347
9390
|
* Create an i18n expression op.
|
|
9348
9391
|
*/
|
|
@@ -9361,7 +9404,7 @@ function createI18nExpressionOp(owner, expression, i18nPlaceholder, resolutionTi
|
|
|
9361
9404
|
};
|
|
9362
9405
|
}
|
|
9363
9406
|
/**
|
|
9364
|
-
*
|
|
9407
|
+
*Creates an op to apply i18n expression ops
|
|
9365
9408
|
*/
|
|
9366
9409
|
function createI18nApplyOp(target, sourceSpan) {
|
|
9367
9410
|
return {
|
|
@@ -9409,8 +9452,11 @@ class LexicalReadExpr extends ExpressionBase {
|
|
|
9409
9452
|
this.kind = ExpressionKind.LexicalRead;
|
|
9410
9453
|
}
|
|
9411
9454
|
visitExpression(visitor, context) { }
|
|
9412
|
-
isEquivalent() {
|
|
9413
|
-
|
|
9455
|
+
isEquivalent(other) {
|
|
9456
|
+
// We assume that the lexical reads are in the same context, which must be true for parent
|
|
9457
|
+
// expressions to be equivalent.
|
|
9458
|
+
// TODO: is this generally safe?
|
|
9459
|
+
return this.name === other.name;
|
|
9414
9460
|
}
|
|
9415
9461
|
isConstant() {
|
|
9416
9462
|
return false;
|
|
@@ -9468,6 +9514,27 @@ class ContextExpr extends ExpressionBase {
|
|
|
9468
9514
|
return new ContextExpr(this.view);
|
|
9469
9515
|
}
|
|
9470
9516
|
}
|
|
9517
|
+
/**
|
|
9518
|
+
* A reference to the current view context inside a track function.
|
|
9519
|
+
*/
|
|
9520
|
+
class TrackContextExpr extends ExpressionBase {
|
|
9521
|
+
constructor(view) {
|
|
9522
|
+
super();
|
|
9523
|
+
this.view = view;
|
|
9524
|
+
this.kind = ExpressionKind.TrackContext;
|
|
9525
|
+
}
|
|
9526
|
+
visitExpression() { }
|
|
9527
|
+
isEquivalent(e) {
|
|
9528
|
+
return e instanceof TrackContextExpr && e.view === this.view;
|
|
9529
|
+
}
|
|
9530
|
+
isConstant() {
|
|
9531
|
+
return false;
|
|
9532
|
+
}
|
|
9533
|
+
transformInternalExpressions() { }
|
|
9534
|
+
clone() {
|
|
9535
|
+
return new TrackContextExpr(this.view);
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9471
9538
|
/**
|
|
9472
9539
|
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
9473
9540
|
*/
|
|
@@ -9985,6 +10052,33 @@ class ConditionalCaseExpr extends ExpressionBase {
|
|
|
9985
10052
|
}
|
|
9986
10053
|
}
|
|
9987
10054
|
}
|
|
10055
|
+
var DerivedRepeaterVarIdentity;
|
|
10056
|
+
(function (DerivedRepeaterVarIdentity) {
|
|
10057
|
+
DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["First"] = 0] = "First";
|
|
10058
|
+
DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Last"] = 1] = "Last";
|
|
10059
|
+
DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
|
|
10060
|
+
DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
|
|
10061
|
+
})(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
|
|
10062
|
+
class DerivedRepeaterVarExpr extends ExpressionBase {
|
|
10063
|
+
constructor(xref, identity) {
|
|
10064
|
+
super();
|
|
10065
|
+
this.xref = xref;
|
|
10066
|
+
this.identity = identity;
|
|
10067
|
+
this.kind = ExpressionKind.DerivedRepeaterVar;
|
|
10068
|
+
}
|
|
10069
|
+
transformInternalExpressions(transform, flags) { }
|
|
10070
|
+
visitExpression(visitor, context) { }
|
|
10071
|
+
isEquivalent(e) {
|
|
10072
|
+
return e instanceof DerivedRepeaterVarExpr && e.identity === this.identity &&
|
|
10073
|
+
e.xref === this.xref;
|
|
10074
|
+
}
|
|
10075
|
+
isConstant() {
|
|
10076
|
+
return false;
|
|
10077
|
+
}
|
|
10078
|
+
clone() {
|
|
10079
|
+
return new DerivedRepeaterVarExpr(this.xref, this.identity);
|
|
10080
|
+
}
|
|
10081
|
+
}
|
|
9988
10082
|
/**
|
|
9989
10083
|
* Visits all `Expression`s in the AST of `op` with the `visitor` function.
|
|
9990
10084
|
*/
|
|
@@ -10079,6 +10173,15 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10079
10173
|
transformExpressionsInStatement(statement, transform, flags);
|
|
10080
10174
|
}
|
|
10081
10175
|
break;
|
|
10176
|
+
case OpKind.RepeaterCreate:
|
|
10177
|
+
op.track = transformExpressionsInExpression(op.track, transform, flags);
|
|
10178
|
+
if (op.trackByFn !== null) {
|
|
10179
|
+
op.trackByFn = transformExpressionsInExpression(op.trackByFn, transform, flags);
|
|
10180
|
+
}
|
|
10181
|
+
break;
|
|
10182
|
+
case OpKind.Repeater:
|
|
10183
|
+
op.collection = transformExpressionsInExpression(op.collection, transform, flags);
|
|
10184
|
+
break;
|
|
10082
10185
|
case OpKind.I18n:
|
|
10083
10186
|
case OpKind.I18nStart:
|
|
10084
10187
|
for (const [placeholder, expression] of op.params) {
|
|
@@ -10483,7 +10586,7 @@ class OpList {
|
|
|
10483
10586
|
*/
|
|
10484
10587
|
const elementContainerOpKinds = new Set([
|
|
10485
10588
|
OpKind.Element, OpKind.ElementStart, OpKind.Container, OpKind.ContainerStart, OpKind.Template,
|
|
10486
|
-
OpKind.
|
|
10589
|
+
OpKind.RepeaterCreate
|
|
10487
10590
|
]);
|
|
10488
10591
|
/**
|
|
10489
10592
|
* Checks whether the given operation represents the creation of an element or container.
|
|
@@ -10530,6 +10633,28 @@ function createTemplateOp(xref, tag, namespace, generatedInBlock, i18nPlaceholde
|
|
|
10530
10633
|
...NEW_OP,
|
|
10531
10634
|
};
|
|
10532
10635
|
}
|
|
10636
|
+
function createRepeaterCreateOp(primaryView, emptyView, track, varNames, sourceSpan) {
|
|
10637
|
+
return {
|
|
10638
|
+
kind: OpKind.RepeaterCreate,
|
|
10639
|
+
attributes: null,
|
|
10640
|
+
xref: primaryView,
|
|
10641
|
+
emptyView,
|
|
10642
|
+
track,
|
|
10643
|
+
trackByFn: null,
|
|
10644
|
+
tag: 'For',
|
|
10645
|
+
namespace: Namespace.HTML,
|
|
10646
|
+
nonBindable: false,
|
|
10647
|
+
localRefs: [],
|
|
10648
|
+
decls: null,
|
|
10649
|
+
vars: null,
|
|
10650
|
+
varNames,
|
|
10651
|
+
usesComponentInstance: false,
|
|
10652
|
+
sourceSpan,
|
|
10653
|
+
...TRAIT_CONSUMES_SLOT,
|
|
10654
|
+
...NEW_OP,
|
|
10655
|
+
numSlotsUsed: emptyView === null ? 2 : 3,
|
|
10656
|
+
};
|
|
10657
|
+
}
|
|
10533
10658
|
/**
|
|
10534
10659
|
* Create an `ElementEndOp`.
|
|
10535
10660
|
*/
|
|
@@ -10620,16 +10745,15 @@ function createProjectionDefOp(def) {
|
|
|
10620
10745
|
...NEW_OP,
|
|
10621
10746
|
};
|
|
10622
10747
|
}
|
|
10623
|
-
function createProjectionOp(xref, selector) {
|
|
10748
|
+
function createProjectionOp(xref, selector, sourceSpan) {
|
|
10624
10749
|
return {
|
|
10625
10750
|
kind: OpKind.Projection,
|
|
10626
10751
|
xref,
|
|
10627
10752
|
selector,
|
|
10628
10753
|
projectionSlotIndex: 0,
|
|
10629
|
-
attributes:
|
|
10754
|
+
attributes: [],
|
|
10630
10755
|
localRefs: [],
|
|
10631
|
-
|
|
10632
|
-
sourceSpan: null,
|
|
10756
|
+
sourceSpan,
|
|
10633
10757
|
...NEW_OP,
|
|
10634
10758
|
...TRAIT_CONSUMES_SLOT,
|
|
10635
10759
|
};
|
|
@@ -10907,6 +11031,11 @@ class ViewCompilationUnit extends CompilationUnit {
|
|
|
10907
11031
|
* which they alias.
|
|
10908
11032
|
*/
|
|
10909
11033
|
this.contextVariables = new Map();
|
|
11034
|
+
/**
|
|
11035
|
+
* Set of aliases available within this view. An alias is a variable whose provided expression is
|
|
11036
|
+
* inlined at every location it is used. It may also depend on context variables, by name.
|
|
11037
|
+
*/
|
|
11038
|
+
this.aliases = new Set();
|
|
10910
11039
|
/**
|
|
10911
11040
|
* Number of declaration slots used within this view, or `null` if slots have not yet been
|
|
10912
11041
|
* allocated.
|
|
@@ -10939,137 +11068,6 @@ class HostBindingCompilationUnit extends CompilationUnit {
|
|
|
10939
11068
|
}
|
|
10940
11069
|
}
|
|
10941
11070
|
|
|
10942
|
-
/**
|
|
10943
|
-
* Counts the number of variable slots used within each view, and stores that on the view itself, as
|
|
10944
|
-
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
10945
|
-
*/
|
|
10946
|
-
function phaseVarCounting(job) {
|
|
10947
|
-
// First, count the vars used in each view, and update the view-level counter.
|
|
10948
|
-
for (const unit of job.units) {
|
|
10949
|
-
let varCount = 0;
|
|
10950
|
-
// Count variables on top-level ops first. Don't explore nested expressions just yet.
|
|
10951
|
-
for (const op of unit.ops()) {
|
|
10952
|
-
if (hasConsumesVarsTrait(op)) {
|
|
10953
|
-
varCount += varsUsedByOp(op);
|
|
10954
|
-
}
|
|
10955
|
-
}
|
|
10956
|
-
// Count variables on expressions inside ops. We do this later because some of these expressions
|
|
10957
|
-
// might be conditional (e.g. `pipeBinding` inside of a ternary), and we don't want to interfere
|
|
10958
|
-
// with indices for top-level binding slots (e.g. `property`).
|
|
10959
|
-
for (const op of unit.ops()) {
|
|
10960
|
-
visitExpressionsInOp(op, expr => {
|
|
10961
|
-
if (!isIrExpression(expr)) {
|
|
10962
|
-
return;
|
|
10963
|
-
}
|
|
10964
|
-
// Some expressions require knowledge of the number of variable slots consumed.
|
|
10965
|
-
if (hasUsesVarOffsetTrait(expr)) {
|
|
10966
|
-
expr.varOffset = varCount;
|
|
10967
|
-
}
|
|
10968
|
-
if (hasConsumesVarsTrait(expr)) {
|
|
10969
|
-
varCount += varsUsedByIrExpression(expr);
|
|
10970
|
-
}
|
|
10971
|
-
});
|
|
10972
|
-
}
|
|
10973
|
-
unit.vars = varCount;
|
|
10974
|
-
}
|
|
10975
|
-
if (job instanceof ComponentCompilationJob) {
|
|
10976
|
-
// Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is
|
|
10977
|
-
// an embedded view).
|
|
10978
|
-
for (const unit of job.units) {
|
|
10979
|
-
for (const op of unit.create) {
|
|
10980
|
-
if (op.kind !== OpKind.Template) {
|
|
10981
|
-
continue;
|
|
10982
|
-
}
|
|
10983
|
-
const childView = job.views.get(op.xref);
|
|
10984
|
-
op.vars = childView.vars;
|
|
10985
|
-
}
|
|
10986
|
-
}
|
|
10987
|
-
}
|
|
10988
|
-
}
|
|
10989
|
-
/**
|
|
10990
|
-
* Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so
|
|
10991
|
-
* count the variables used by any particular `op`.
|
|
10992
|
-
*/
|
|
10993
|
-
function varsUsedByOp(op) {
|
|
10994
|
-
let slots;
|
|
10995
|
-
switch (op.kind) {
|
|
10996
|
-
case OpKind.Property:
|
|
10997
|
-
case OpKind.HostProperty:
|
|
10998
|
-
case OpKind.Attribute:
|
|
10999
|
-
// All of these bindings use 1 variable slot, plus 1 slot for every interpolated expression,
|
|
11000
|
-
// if any.
|
|
11001
|
-
slots = 1;
|
|
11002
|
-
if (op.expression instanceof Interpolation) {
|
|
11003
|
-
slots += op.expression.expressions.length;
|
|
11004
|
-
}
|
|
11005
|
-
return slots;
|
|
11006
|
-
case OpKind.StyleProp:
|
|
11007
|
-
case OpKind.ClassProp:
|
|
11008
|
-
case OpKind.StyleMap:
|
|
11009
|
-
case OpKind.ClassMap:
|
|
11010
|
-
// Style & class bindings use 2 variable slots, plus 1 slot for every interpolated expression,
|
|
11011
|
-
// if any.
|
|
11012
|
-
slots = 2;
|
|
11013
|
-
if (op.expression instanceof Interpolation) {
|
|
11014
|
-
slots += op.expression.expressions.length;
|
|
11015
|
-
}
|
|
11016
|
-
return slots;
|
|
11017
|
-
case OpKind.InterpolateText:
|
|
11018
|
-
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
11019
|
-
return op.interpolation.expressions.length;
|
|
11020
|
-
case OpKind.I18nExpression:
|
|
11021
|
-
case OpKind.Conditional:
|
|
11022
|
-
return 1;
|
|
11023
|
-
default:
|
|
11024
|
-
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
11025
|
-
}
|
|
11026
|
-
}
|
|
11027
|
-
function varsUsedByIrExpression(expr) {
|
|
11028
|
-
switch (expr.kind) {
|
|
11029
|
-
case ExpressionKind.PureFunctionExpr:
|
|
11030
|
-
return 1 + expr.args.length;
|
|
11031
|
-
case ExpressionKind.PipeBinding:
|
|
11032
|
-
return 1 + expr.args.length;
|
|
11033
|
-
case ExpressionKind.PipeBindingVariadic:
|
|
11034
|
-
return 1 + expr.numArgs;
|
|
11035
|
-
default:
|
|
11036
|
-
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
11037
|
-
}
|
|
11038
|
-
}
|
|
11039
|
-
|
|
11040
|
-
function phaseAlignPipeVariadicVarOffset(job) {
|
|
11041
|
-
for (const unit of job.units) {
|
|
11042
|
-
for (const op of unit.update) {
|
|
11043
|
-
visitExpressionsInOp(op, expr => {
|
|
11044
|
-
if (!(expr instanceof PipeBindingVariadicExpr)) {
|
|
11045
|
-
return expr;
|
|
11046
|
-
}
|
|
11047
|
-
if (!(expr.args instanceof PureFunctionExpr)) {
|
|
11048
|
-
return expr;
|
|
11049
|
-
}
|
|
11050
|
-
if (expr.varOffset === null || expr.args.varOffset === null) {
|
|
11051
|
-
throw new Error(`Must run after variable counting`);
|
|
11052
|
-
}
|
|
11053
|
-
// The structure of this variadic pipe expression is:
|
|
11054
|
-
// PipeBindingVariadic(#, Y, PureFunction(X, ...ARGS))
|
|
11055
|
-
// Where X and Y are the slot offsets for the variables used by these operations, and Y > X.
|
|
11056
|
-
// In `TemplateDefinitionBuilder` the PipeBindingVariadic variable slots are allocated
|
|
11057
|
-
// before the PureFunction slots, which is unusually out-of-order.
|
|
11058
|
-
//
|
|
11059
|
-
// To maintain identical output for the tests in question, we adjust the variable offsets of
|
|
11060
|
-
// these two calls to emulate TDB's behavior. This is not perfect, because the ARGS of the
|
|
11061
|
-
// PureFunction call may also allocate slots which by TDB's ordering would come after X, and
|
|
11062
|
-
// we don't account for that. Still, this should be enough to pass the existing pipe tests.
|
|
11063
|
-
// Put the PipeBindingVariadic vars where the PureFunction vars were previously allocated.
|
|
11064
|
-
expr.varOffset = expr.args.varOffset;
|
|
11065
|
-
// Put the PureFunction vars following the PipeBindingVariadic vars.
|
|
11066
|
-
expr.args.varOffset = expr.varOffset + varsUsedByIrExpression(expr);
|
|
11067
|
-
return undefined;
|
|
11068
|
-
});
|
|
11069
|
-
}
|
|
11070
|
-
}
|
|
11071
|
-
}
|
|
11072
|
-
|
|
11073
11071
|
/**
|
|
11074
11072
|
* Find any function calls to `$any`, excluding `this.$any`, and delete them.
|
|
11075
11073
|
*/
|
|
@@ -11145,18 +11143,41 @@ function phaseAssignI18nSlotDependencies(job) {
|
|
|
11145
11143
|
}
|
|
11146
11144
|
}
|
|
11147
11145
|
|
|
11146
|
+
/**
|
|
11147
|
+
* Attribute interpolations of the form `[attr.foo]="{{foo}}""` should be "collapsed" into a plain
|
|
11148
|
+
* attribute instruction, instead of an `attributeInterpolate` instruction.
|
|
11149
|
+
*
|
|
11150
|
+
* (We cannot do this for singleton property interpolations, because `propertyInterpolate`
|
|
11151
|
+
* stringifies its expression.)
|
|
11152
|
+
*
|
|
11153
|
+
* The reification step is also capable of performing this transformation, but doing it early in the
|
|
11154
|
+
* pipeline allows other phases to accurately know what instruction will be emitted.
|
|
11155
|
+
*/
|
|
11156
|
+
function phaseCollapseSingletonInterpolations(job) {
|
|
11157
|
+
for (const unit of job.units) {
|
|
11158
|
+
for (const op of unit.update) {
|
|
11159
|
+
const eligibleOpKind = op.kind === OpKind.Attribute;
|
|
11160
|
+
if (eligibleOpKind && op.expression instanceof Interpolation &&
|
|
11161
|
+
op.expression.strings.length === 2 &&
|
|
11162
|
+
op.expression.strings.every((s) => s === '')) {
|
|
11163
|
+
op.expression = op.expression.expressions[0];
|
|
11164
|
+
}
|
|
11165
|
+
}
|
|
11166
|
+
}
|
|
11167
|
+
}
|
|
11168
|
+
|
|
11148
11169
|
/**
|
|
11149
11170
|
* Gets a map of all elements in the given view by their xref id.
|
|
11150
11171
|
*/
|
|
11151
|
-
function
|
|
11152
|
-
const
|
|
11172
|
+
function createOpXrefMap(unit) {
|
|
11173
|
+
const map = new Map();
|
|
11153
11174
|
for (const op of unit.create) {
|
|
11154
|
-
if (!
|
|
11175
|
+
if (!hasConsumesSlotTrait(op)) {
|
|
11155
11176
|
continue;
|
|
11156
11177
|
}
|
|
11157
|
-
|
|
11178
|
+
map.set(op.xref, op);
|
|
11158
11179
|
}
|
|
11159
|
-
return
|
|
11180
|
+
return map;
|
|
11160
11181
|
}
|
|
11161
11182
|
|
|
11162
11183
|
/**
|
|
@@ -11165,7 +11186,7 @@ function getElementsByXrefId(unit) {
|
|
|
11165
11186
|
*/
|
|
11166
11187
|
function phaseAttributeExtraction(job) {
|
|
11167
11188
|
for (const unit of job.units) {
|
|
11168
|
-
const elements =
|
|
11189
|
+
const elements = createOpXrefMap(unit);
|
|
11169
11190
|
for (const op of unit.ops()) {
|
|
11170
11191
|
switch (op.kind) {
|
|
11171
11192
|
case OpKind.Attribute:
|
|
@@ -11173,7 +11194,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11173
11194
|
break;
|
|
11174
11195
|
case OpKind.Property:
|
|
11175
11196
|
if (!op.isAnimationTrigger) {
|
|
11176
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$
|
|
11197
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
|
|
11177
11198
|
}
|
|
11178
11199
|
break;
|
|
11179
11200
|
case OpKind.StyleProp:
|
|
@@ -11183,7 +11204,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11183
11204
|
// mode.
|
|
11184
11205
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11185
11206
|
op.expression instanceof EmptyExpr) {
|
|
11186
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$
|
|
11207
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
|
|
11187
11208
|
}
|
|
11188
11209
|
break;
|
|
11189
11210
|
case OpKind.Listener:
|
|
@@ -11195,7 +11216,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11195
11216
|
unit.create.push(extractedAttributeOp);
|
|
11196
11217
|
}
|
|
11197
11218
|
else {
|
|
11198
|
-
OpList.insertBefore(extractedAttributeOp, lookupElement$
|
|
11219
|
+
OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target));
|
|
11199
11220
|
}
|
|
11200
11221
|
}
|
|
11201
11222
|
break;
|
|
@@ -11206,7 +11227,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11206
11227
|
/**
|
|
11207
11228
|
* Looks up an element in the given map by xref ID.
|
|
11208
11229
|
*/
|
|
11209
|
-
function lookupElement$
|
|
11230
|
+
function lookupElement$2(elements, xref) {
|
|
11210
11231
|
const el = elements.get(xref);
|
|
11211
11232
|
if (el === undefined) {
|
|
11212
11233
|
throw new Error('All attributes should have an element-like target.');
|
|
@@ -11244,7 +11265,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11244
11265
|
unit.create.push(extractedAttributeOp);
|
|
11245
11266
|
}
|
|
11246
11267
|
else {
|
|
11247
|
-
const ownerOp = lookupElement$
|
|
11268
|
+
const ownerOp = lookupElement$2(elements, op.target);
|
|
11248
11269
|
OpList.insertBefore(extractedAttributeOp, ownerOp);
|
|
11249
11270
|
}
|
|
11250
11271
|
OpList.remove(op);
|
|
@@ -11254,7 +11275,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11254
11275
|
/**
|
|
11255
11276
|
* Looks up an element in the given map by xref ID.
|
|
11256
11277
|
*/
|
|
11257
|
-
function lookupElement$
|
|
11278
|
+
function lookupElement$1(elements, xref) {
|
|
11258
11279
|
const el = elements.get(xref);
|
|
11259
11280
|
if (el === undefined) {
|
|
11260
11281
|
throw new Error('All attributes should have an element-like target.');
|
|
@@ -11280,7 +11301,7 @@ function phaseBindingSpecialization(job) {
|
|
|
11280
11301
|
case BindingKind.Attribute:
|
|
11281
11302
|
if (op.name === 'ngNonBindable') {
|
|
11282
11303
|
OpList.remove(op);
|
|
11283
|
-
const target = lookupElement$
|
|
11304
|
+
const target = lookupElement$1(elements, op.target);
|
|
11284
11305
|
target.nonBindable = true;
|
|
11285
11306
|
}
|
|
11286
11307
|
else {
|
|
@@ -11440,6 +11461,9 @@ function phaseConditionals(job) {
|
|
|
11440
11461
|
}
|
|
11441
11462
|
// Save the resulting aggregate Joost-expression.
|
|
11442
11463
|
op.processed = test;
|
|
11464
|
+
// Clear the original conditions array, since we no longer need it, and don't want it to
|
|
11465
|
+
// affect subsequent phases (e.g. pipe creation).
|
|
11466
|
+
op.conditions = [];
|
|
11443
11467
|
}
|
|
11444
11468
|
}
|
|
11445
11469
|
}
|
|
@@ -11891,6 +11915,37 @@ function ternaryTransform(e) {
|
|
|
11891
11915
|
return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr);
|
|
11892
11916
|
}
|
|
11893
11917
|
|
|
11918
|
+
function phaseRepeaterDerivedVars(job) {
|
|
11919
|
+
const repeaters = new Map();
|
|
11920
|
+
for (const unit of job.units) {
|
|
11921
|
+
for (const op of unit.ops()) {
|
|
11922
|
+
if (op.kind === OpKind.RepeaterCreate) {
|
|
11923
|
+
repeaters.set(op.xref, op);
|
|
11924
|
+
}
|
|
11925
|
+
}
|
|
11926
|
+
}
|
|
11927
|
+
for (const unit of job.units) {
|
|
11928
|
+
for (const op of unit.ops()) {
|
|
11929
|
+
transformExpressionsInOp(op, expr => {
|
|
11930
|
+
if (!(expr instanceof DerivedRepeaterVarExpr)) {
|
|
11931
|
+
return expr;
|
|
11932
|
+
}
|
|
11933
|
+
const repeaterOp = repeaters.get(expr.xref);
|
|
11934
|
+
switch (expr.identity) {
|
|
11935
|
+
case DerivedRepeaterVarIdentity.First:
|
|
11936
|
+
return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
|
|
11937
|
+
case DerivedRepeaterVarIdentity.Last:
|
|
11938
|
+
return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
|
|
11939
|
+
case DerivedRepeaterVarIdentity.Even:
|
|
11940
|
+
return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
|
|
11941
|
+
case DerivedRepeaterVarIdentity.Odd:
|
|
11942
|
+
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
|
|
11943
|
+
}
|
|
11944
|
+
}, VisitorContextFlag.None);
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
}
|
|
11948
|
+
|
|
11894
11949
|
/**
|
|
11895
11950
|
* Generate `ir.AdvanceOp`s in between `ir.UpdateOp`s that ensure the runtime's implicit slot
|
|
11896
11951
|
* context will be advanced correctly.
|
|
@@ -12005,6 +12060,7 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
12005
12060
|
for (const op of view.create) {
|
|
12006
12061
|
switch (op.kind) {
|
|
12007
12062
|
case OpKind.Template:
|
|
12063
|
+
case OpKind.RepeaterCreate:
|
|
12008
12064
|
// Descend into child embedded views.
|
|
12009
12065
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12010
12066
|
break;
|
|
@@ -12031,6 +12087,7 @@ function getScopeForView(view, parent) {
|
|
|
12031
12087
|
view: view.xref,
|
|
12032
12088
|
},
|
|
12033
12089
|
contextVariables: new Map(),
|
|
12090
|
+
aliases: view.aliases,
|
|
12034
12091
|
references: [],
|
|
12035
12092
|
parent,
|
|
12036
12093
|
};
|
|
@@ -12078,19 +12135,23 @@ function generateVariablesInScopeForView(view, scope) {
|
|
|
12078
12135
|
// Before generating variables for a parent view, we need to switch to the context of the parent
|
|
12079
12136
|
// view with a `nextContext` expression. This context switching operation itself declares a
|
|
12080
12137
|
// variable, because the context of the view may be referenced directly.
|
|
12081
|
-
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.viewContextVariable, new NextContextExpr()));
|
|
12138
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.viewContextVariable, new NextContextExpr(), VariableFlags.None));
|
|
12082
12139
|
}
|
|
12083
12140
|
// Add variables for all context variables available in this scope's view.
|
|
12084
|
-
|
|
12141
|
+
const scopeView = view.job.views.get(scope.view);
|
|
12142
|
+
for (const [name, value] of scopeView.contextVariables) {
|
|
12085
12143
|
const context = new ContextExpr(scope.view);
|
|
12086
12144
|
// We either read the context, or, if the variable is CTX_REF, use the context directly.
|
|
12087
12145
|
const variable = value === CTX_REF ? context : new ReadPropExpr(context, value);
|
|
12088
12146
|
// Add the variable declaration.
|
|
12089
|
-
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable));
|
|
12147
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable, VariableFlags.None));
|
|
12148
|
+
}
|
|
12149
|
+
for (const alias of scopeView.aliases) {
|
|
12150
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), alias, alias.expression.clone(), VariableFlags.AlwaysInline));
|
|
12090
12151
|
}
|
|
12091
12152
|
// Add variables for all local references declared for elements in this scope.
|
|
12092
12153
|
for (const ref of scope.references) {
|
|
12093
|
-
newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
12154
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset), VariableFlags.None));
|
|
12094
12155
|
}
|
|
12095
12156
|
if (scope.parent !== null) {
|
|
12096
12157
|
// Recursively add variables from the parent scope.
|
|
@@ -13972,11 +14033,12 @@ class Comment {
|
|
|
13972
14033
|
}
|
|
13973
14034
|
}
|
|
13974
14035
|
class Block {
|
|
13975
|
-
constructor(name, parameters, children, sourceSpan, startSourceSpan, endSourceSpan = null) {
|
|
14036
|
+
constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null) {
|
|
13976
14037
|
this.name = name;
|
|
13977
14038
|
this.parameters = parameters;
|
|
13978
14039
|
this.children = children;
|
|
13979
14040
|
this.sourceSpan = sourceSpan;
|
|
14041
|
+
this.nameSpan = nameSpan;
|
|
13980
14042
|
this.startSourceSpan = startSourceSpan;
|
|
13981
14043
|
this.endSourceSpan = endSourceSpan;
|
|
13982
14044
|
}
|
|
@@ -17790,7 +17852,7 @@ class _Tokenizer {
|
|
|
17790
17852
|
}
|
|
17791
17853
|
}
|
|
17792
17854
|
if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansion() &&
|
|
17793
|
-
(this.
|
|
17855
|
+
(this._cursor.peek() === $AT || this._cursor.peek() === $RBRACE)) {
|
|
17794
17856
|
return true;
|
|
17795
17857
|
}
|
|
17796
17858
|
return false;
|
|
@@ -17813,17 +17875,6 @@ class _Tokenizer {
|
|
|
17813
17875
|
}
|
|
17814
17876
|
return false;
|
|
17815
17877
|
}
|
|
17816
|
-
_isBlockStart() {
|
|
17817
|
-
if (this._tokenizeBlocks && this._cursor.peek() === $AT) {
|
|
17818
|
-
const tmp = this._cursor.clone();
|
|
17819
|
-
// If it is, also verify that the next character is a valid block identifier.
|
|
17820
|
-
tmp.advance();
|
|
17821
|
-
if (isBlockNameChar(tmp.peek())) {
|
|
17822
|
-
return true;
|
|
17823
|
-
}
|
|
17824
|
-
}
|
|
17825
|
-
return false;
|
|
17826
|
-
}
|
|
17827
17878
|
_readUntil(char) {
|
|
17828
17879
|
const start = this._cursor.clone();
|
|
17829
17880
|
this._attemptUntilChar(char);
|
|
@@ -18530,7 +18581,7 @@ class _TreeBuilder {
|
|
|
18530
18581
|
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18531
18582
|
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
|
|
18532
18583
|
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18533
|
-
const block = new Block(token.parts[0], parameters, [], span, startSpan);
|
|
18584
|
+
const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
|
|
18534
18585
|
this._pushContainer(block, false);
|
|
18535
18586
|
}
|
|
18536
18587
|
_consumeBlockClose(token) {
|
|
@@ -18550,7 +18601,7 @@ class _TreeBuilder {
|
|
|
18550
18601
|
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18551
18602
|
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
|
|
18552
18603
|
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18553
|
-
const block = new Block(token.parts[0], parameters, [], span, startSpan);
|
|
18604
|
+
const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
|
|
18554
18605
|
this._pushContainer(block, false);
|
|
18555
18606
|
// Incomplete blocks don't have children so we close them immediately and report an error.
|
|
18556
18607
|
this._popContainer(null, Block, null);
|
|
@@ -19476,6 +19527,22 @@ function addNamesToView(unit, baseName, state, compatibility) {
|
|
|
19476
19527
|
case OpKind.Variable:
|
|
19477
19528
|
varNames.set(op.xref, getVariableName(op.variable, state));
|
|
19478
19529
|
break;
|
|
19530
|
+
case OpKind.RepeaterCreate:
|
|
19531
|
+
if (!(unit instanceof ViewCompilationUnit)) {
|
|
19532
|
+
throw new Error(`AssertionError: must be compiling a component`);
|
|
19533
|
+
}
|
|
19534
|
+
if (op.slot === null) {
|
|
19535
|
+
throw new Error(`Expected slot to be assigned`);
|
|
19536
|
+
}
|
|
19537
|
+
if (op.emptyView !== null) {
|
|
19538
|
+
const emptyView = unit.job.views.get(op.emptyView);
|
|
19539
|
+
// Repeater empty view function is at slot +2 (metadata is in the first slot).
|
|
19540
|
+
addNamesToView(emptyView, `${baseName}_${prefixWithNamespace(`${op.tag}Empty`, op.namespace)}_${op.slot + 2}`, state, compatibility);
|
|
19541
|
+
}
|
|
19542
|
+
const repeaterToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
|
|
19543
|
+
// Repeater primary view function is at slot +1 (metadata is in the first slot).
|
|
19544
|
+
addNamesToView(unit.job.views.get(op.xref), `${baseName}${repeaterToken}_${op.slot + 1}`, state, compatibility);
|
|
19545
|
+
break;
|
|
19479
19546
|
case OpKind.Template:
|
|
19480
19547
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
19481
19548
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
@@ -19521,10 +19588,12 @@ function getVariableName(variable, state) {
|
|
|
19521
19588
|
variable.name = `ctx_r${state.index++}`;
|
|
19522
19589
|
break;
|
|
19523
19590
|
case SemanticVariableKind.Identifier:
|
|
19524
|
-
|
|
19591
|
+
// TODO: Prefix increment and `_r` for compatiblity only.
|
|
19592
|
+
variable.name = `${variable.identifier}_r${++state.index}`;
|
|
19525
19593
|
break;
|
|
19526
19594
|
default:
|
|
19527
|
-
|
|
19595
|
+
// TODO: Prefix increment for compatibility only.
|
|
19596
|
+
variable.name = `_r${++state.index}`;
|
|
19528
19597
|
break;
|
|
19529
19598
|
}
|
|
19530
19599
|
}
|
|
@@ -19635,7 +19704,7 @@ function phaseNgContainer(job) {
|
|
|
19635
19704
|
/**
|
|
19636
19705
|
* Looks up an element in the given map by xref ID.
|
|
19637
19706
|
*/
|
|
19638
|
-
function lookupElement
|
|
19707
|
+
function lookupElement(elements, xref) {
|
|
19639
19708
|
const el = elements.get(xref);
|
|
19640
19709
|
if (el === undefined) {
|
|
19641
19710
|
throw new Error('All attributes should have an element-like target.');
|
|
@@ -19664,7 +19733,7 @@ function phaseNonbindable(job) {
|
|
|
19664
19733
|
OpList.insertAfter(createDisableBindingsOp(op.xref), op);
|
|
19665
19734
|
}
|
|
19666
19735
|
if ((op.kind === OpKind.ElementEnd || op.kind === OpKind.ContainerEnd) &&
|
|
19667
|
-
lookupElement
|
|
19736
|
+
lookupElement(elements, op.xref).nonBindable) {
|
|
19668
19737
|
OpList.insertBefore(createEnableBindingsOp(op.xref), op);
|
|
19669
19738
|
}
|
|
19670
19739
|
}
|
|
@@ -19692,9 +19761,14 @@ function phaseNullishCoalescing(job) {
|
|
|
19692
19761
|
function kindTest(kind) {
|
|
19693
19762
|
return (op) => op.kind === kind;
|
|
19694
19763
|
}
|
|
19764
|
+
function kindWithInterpolationTest(kind, interpolation) {
|
|
19765
|
+
return (op) => {
|
|
19766
|
+
return op.kind === kind && interpolation === op.expression instanceof Interpolation;
|
|
19767
|
+
};
|
|
19768
|
+
}
|
|
19695
19769
|
/**
|
|
19696
19770
|
* Defines the groups based on `OpKind` that ops will be divided into, for the various create
|
|
19697
|
-
*
|
|
19771
|
+
* op kinds. Ops will be collected into groups, then optionally transformed, before recombining
|
|
19698
19772
|
* the groups in the order defined here.
|
|
19699
19773
|
*/
|
|
19700
19774
|
const CREATE_ORDERING = [
|
|
@@ -19702,18 +19776,20 @@ const CREATE_ORDERING = [
|
|
|
19702
19776
|
{ test: op => op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) },
|
|
19703
19777
|
];
|
|
19704
19778
|
/**
|
|
19705
|
-
*
|
|
19779
|
+
* Defines the groups based on `OpKind` that ops will be divided into, for the various update
|
|
19780
|
+
* op kinds.
|
|
19706
19781
|
*/
|
|
19707
19782
|
const UPDATE_ORDERING = [
|
|
19708
|
-
{ test:
|
|
19709
|
-
{ test:
|
|
19783
|
+
{ test: kindWithInterpolationTest(OpKind.HostProperty, true) },
|
|
19784
|
+
{ test: kindWithInterpolationTest(OpKind.HostProperty, false) },
|
|
19710
19785
|
{ test: kindTest(OpKind.StyleMap), transform: keepLast },
|
|
19711
19786
|
{ test: kindTest(OpKind.ClassMap), transform: keepLast },
|
|
19712
19787
|
{ test: kindTest(OpKind.StyleProp) },
|
|
19713
19788
|
{ test: kindTest(OpKind.ClassProp) },
|
|
19714
|
-
{ test:
|
|
19715
|
-
{ test:
|
|
19716
|
-
{ test:
|
|
19789
|
+
{ test: kindWithInterpolationTest(OpKind.Property, true) },
|
|
19790
|
+
{ test: kindWithInterpolationTest(OpKind.Attribute, true) },
|
|
19791
|
+
{ test: kindWithInterpolationTest(OpKind.Property, false) },
|
|
19792
|
+
{ test: kindWithInterpolationTest(OpKind.Attribute, false) },
|
|
19717
19793
|
];
|
|
19718
19794
|
/**
|
|
19719
19795
|
* The set of all op kinds we handle in the reordering phase.
|
|
@@ -19728,32 +19804,34 @@ function phaseOrdering(job) {
|
|
|
19728
19804
|
// be reordered, put the ones we've pulled so far back in the correct order. Finally, if we
|
|
19729
19805
|
// still have ops pulled at the end, put them back in the correct order.
|
|
19730
19806
|
// Create mode:
|
|
19731
|
-
|
|
19732
|
-
for (const op of unit.create) {
|
|
19733
|
-
if (handledOpKinds.has(op.kind)) {
|
|
19734
|
-
opsToOrder.push(op);
|
|
19735
|
-
OpList.remove(op);
|
|
19736
|
-
}
|
|
19737
|
-
else {
|
|
19738
|
-
OpList.insertBefore(reorder(opsToOrder, CREATE_ORDERING), op);
|
|
19739
|
-
opsToOrder = [];
|
|
19740
|
-
}
|
|
19741
|
-
}
|
|
19742
|
-
unit.create.push(reorder(opsToOrder, CREATE_ORDERING));
|
|
19807
|
+
orderWithin(unit.create, CREATE_ORDERING);
|
|
19743
19808
|
// Update mode:
|
|
19744
|
-
|
|
19745
|
-
|
|
19746
|
-
|
|
19747
|
-
|
|
19748
|
-
|
|
19749
|
-
|
|
19750
|
-
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19809
|
+
orderWithin(unit.update, UPDATE_ORDERING);
|
|
19810
|
+
}
|
|
19811
|
+
}
|
|
19812
|
+
/**
|
|
19813
|
+
* Order all the ops within the specified group.
|
|
19814
|
+
*/
|
|
19815
|
+
function orderWithin(opList, ordering) {
|
|
19816
|
+
let opsToOrder = [];
|
|
19817
|
+
// Only reorder ops that target the same xref; do not mix ops that target different xrefs.
|
|
19818
|
+
let firstTargetInGroup = null;
|
|
19819
|
+
for (const op of opList) {
|
|
19820
|
+
const currentTarget = hasDependsOnSlotContextTrait(op) ? op.target : null;
|
|
19821
|
+
if (!handledOpKinds.has(op.kind) ||
|
|
19822
|
+
(currentTarget !== firstTargetInGroup &&
|
|
19823
|
+
(firstTargetInGroup !== null && currentTarget !== null))) {
|
|
19824
|
+
OpList.insertBefore(reorder(opsToOrder, ordering), op);
|
|
19825
|
+
opsToOrder = [];
|
|
19826
|
+
firstTargetInGroup = null;
|
|
19827
|
+
}
|
|
19828
|
+
if (handledOpKinds.has(op.kind)) {
|
|
19829
|
+
opsToOrder.push(op);
|
|
19830
|
+
OpList.remove(op);
|
|
19831
|
+
firstTargetInGroup = currentTarget ?? firstTargetInGroup;
|
|
19754
19832
|
}
|
|
19755
|
-
unit.update.push(reorder(opsToOrder, UPDATE_ORDERING));
|
|
19756
19833
|
}
|
|
19834
|
+
opList.push(reorder(opsToOrder, ordering));
|
|
19757
19835
|
}
|
|
19758
19836
|
/**
|
|
19759
19837
|
* Reorders the given list of ops according to the ordering defined by `ORDERING`.
|
|
@@ -19812,15 +19890,15 @@ function phaseParseExtractedStyles(cpl) {
|
|
|
19812
19890
|
*/
|
|
19813
19891
|
function phaseRemoveContentSelectors(job) {
|
|
19814
19892
|
for (const unit of job.units) {
|
|
19815
|
-
const elements =
|
|
19893
|
+
const elements = createOpXrefMap(unit);
|
|
19816
19894
|
for (const op of unit.update) {
|
|
19817
19895
|
switch (op.kind) {
|
|
19818
19896
|
case OpKind.Binding:
|
|
19819
|
-
const target =
|
|
19897
|
+
const target = lookupInXrefMap(elements, op.target);
|
|
19820
19898
|
if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
|
|
19821
19899
|
OpList.remove(op);
|
|
19822
19900
|
}
|
|
19823
|
-
|
|
19901
|
+
break;
|
|
19824
19902
|
}
|
|
19825
19903
|
}
|
|
19826
19904
|
}
|
|
@@ -19828,10 +19906,10 @@ function phaseRemoveContentSelectors(job) {
|
|
|
19828
19906
|
/**
|
|
19829
19907
|
* Looks up an element in the given map by xref ID.
|
|
19830
19908
|
*/
|
|
19831
|
-
function
|
|
19832
|
-
const el =
|
|
19909
|
+
function lookupInXrefMap(map, xref) {
|
|
19910
|
+
const el = map.get(xref);
|
|
19833
19911
|
if (el === undefined) {
|
|
19834
|
-
throw new Error('All attributes should have an
|
|
19912
|
+
throw new Error('All attributes should have an slottable target.');
|
|
19835
19913
|
}
|
|
19836
19914
|
return el;
|
|
19837
19915
|
}
|
|
@@ -19853,10 +19931,22 @@ function processPipeBindingsInView(unit) {
|
|
|
19853
19931
|
if (flags & VisitorContextFlag.InChildOperation) {
|
|
19854
19932
|
throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
|
|
19855
19933
|
}
|
|
19856
|
-
|
|
19934
|
+
// This update op must be associated with a create op that consumes a slot (either by
|
|
19935
|
+
// depending on the ambient context of `target`, or merely referencing that create op via
|
|
19936
|
+
// `target`).
|
|
19937
|
+
if (!hasDependsOnSlotContextTrait(updateOp) &&
|
|
19938
|
+
!hasUsesSlotIndexTrait(updateOp)) {
|
|
19857
19939
|
throw new Error(`AssertionError: pipe binding associated with non-slot operation ${OpKind[updateOp.kind]}`);
|
|
19858
19940
|
}
|
|
19859
|
-
|
|
19941
|
+
if (unit.job.compatibility) {
|
|
19942
|
+
addPipeToCreationBlock(unit, updateOp.target, expr);
|
|
19943
|
+
}
|
|
19944
|
+
else {
|
|
19945
|
+
// When not in compatibility mode, we just add the pipe to the end of the create block. This
|
|
19946
|
+
// is not only simpler and faster, but allows more chaining opportunities for other
|
|
19947
|
+
// instructions.
|
|
19948
|
+
unit.create.push(createPipeOp(expr.target, expr.name));
|
|
19949
|
+
}
|
|
19860
19950
|
});
|
|
19861
19951
|
}
|
|
19862
19952
|
}
|
|
@@ -19978,6 +20068,7 @@ class PureFunctionConstant extends GenericKeyFn {
|
|
|
19978
20068
|
return super.keyOf(expr);
|
|
19979
20069
|
}
|
|
19980
20070
|
}
|
|
20071
|
+
// TODO: Use the new pool method `getSharedFunctionReference`
|
|
19981
20072
|
toSharedConstantDeclaration(declName, keyExpr) {
|
|
19982
20073
|
const fnParams = [];
|
|
19983
20074
|
for (let idx = 0; idx < this.numArgs; idx++) {
|
|
@@ -20177,15 +20268,15 @@ function deferOn(sourceSpan) {
|
|
|
20177
20268
|
function projectionDef(def) {
|
|
20178
20269
|
return call(Identifiers.projectionDef, def ? [def] : [], null);
|
|
20179
20270
|
}
|
|
20180
|
-
function projection(slot, projectionSlotIndex, attributes) {
|
|
20271
|
+
function projection(slot, projectionSlotIndex, attributes, sourceSpan) {
|
|
20181
20272
|
const args = [literal(slot)];
|
|
20182
|
-
if (projectionSlotIndex !== 0 || attributes
|
|
20273
|
+
if (projectionSlotIndex !== 0 || attributes.length > 0) {
|
|
20183
20274
|
args.push(literal(projectionSlotIndex));
|
|
20184
|
-
if (attributes
|
|
20185
|
-
args.push(literal(
|
|
20275
|
+
if (attributes.length > 0) {
|
|
20276
|
+
args.push(literalArr(attributes.map(attr => literal(attr))));
|
|
20186
20277
|
}
|
|
20187
20278
|
}
|
|
20188
|
-
return call(Identifiers.projection, args,
|
|
20279
|
+
return call(Identifiers.projection, args, sourceSpan);
|
|
20189
20280
|
}
|
|
20190
20281
|
function i18nStart(slot, constIndex, subTemplateIndex) {
|
|
20191
20282
|
const args = [literal(slot), literal(constIndex)];
|
|
@@ -20194,6 +20285,27 @@ function i18nStart(slot, constIndex, subTemplateIndex) {
|
|
|
20194
20285
|
}
|
|
20195
20286
|
return call(Identifiers.i18nStart, args, null);
|
|
20196
20287
|
}
|
|
20288
|
+
function repeaterCreate(slot, viewFnName, decls, vars, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
|
|
20289
|
+
let args = [
|
|
20290
|
+
literal(slot),
|
|
20291
|
+
variable(viewFnName),
|
|
20292
|
+
literal(decls),
|
|
20293
|
+
literal(vars),
|
|
20294
|
+
trackByFn,
|
|
20295
|
+
];
|
|
20296
|
+
if (trackByUsesComponentInstance || emptyViewFnName !== null) {
|
|
20297
|
+
args.push(literal(trackByUsesComponentInstance));
|
|
20298
|
+
if (emptyViewFnName !== null) {
|
|
20299
|
+
args.push(variable(emptyViewFnName));
|
|
20300
|
+
args.push(literal(emptyDecls));
|
|
20301
|
+
args.push(literal(emptyVars));
|
|
20302
|
+
}
|
|
20303
|
+
}
|
|
20304
|
+
return call(Identifiers.repeaterCreate, args, sourceSpan);
|
|
20305
|
+
}
|
|
20306
|
+
function repeater(metadataSlot, collection, sourceSpan) {
|
|
20307
|
+
return call(Identifiers.repeater, [literal(metadataSlot), collection], sourceSpan);
|
|
20308
|
+
}
|
|
20197
20309
|
function i18n(slot, constIndex, subTemplateIndex) {
|
|
20198
20310
|
const args = [literal(slot), literal(constIndex)];
|
|
20199
20311
|
if (subTemplateIndex) {
|
|
@@ -20648,7 +20760,35 @@ function reifyCreateOperations(unit, ops) {
|
|
|
20648
20760
|
if (op.slot === null) {
|
|
20649
20761
|
throw new Error('No slot was assigned for project instruction');
|
|
20650
20762
|
}
|
|
20651
|
-
OpList.replace(op, projection(op.slot, op.projectionSlotIndex, op.attributes));
|
|
20763
|
+
OpList.replace(op, projection(op.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan));
|
|
20764
|
+
break;
|
|
20765
|
+
case OpKind.RepeaterCreate:
|
|
20766
|
+
if (op.slot === null) {
|
|
20767
|
+
throw new Error('No slot was assigned for repeater instruction');
|
|
20768
|
+
}
|
|
20769
|
+
if (!(unit instanceof ViewCompilationUnit)) {
|
|
20770
|
+
throw new Error(`AssertionError: must be compiling a component`);
|
|
20771
|
+
}
|
|
20772
|
+
const repeaterView = unit.job.views.get(op.xref);
|
|
20773
|
+
if (repeaterView.fnName === null) {
|
|
20774
|
+
throw new Error(`AssertionError: expected repeater primary view to have been named`);
|
|
20775
|
+
}
|
|
20776
|
+
let emptyViewFnName = null;
|
|
20777
|
+
let emptyDecls = null;
|
|
20778
|
+
let emptyVars = null;
|
|
20779
|
+
if (op.emptyView !== null) {
|
|
20780
|
+
const emptyView = unit.job.views.get(op.emptyView);
|
|
20781
|
+
if (emptyView === undefined) {
|
|
20782
|
+
throw new Error('AssertionError: repeater had empty view xref, but empty view was not found');
|
|
20783
|
+
}
|
|
20784
|
+
if (emptyView.fnName === null || emptyView.decls === null || emptyView.vars === null) {
|
|
20785
|
+
throw new Error(`AssertionError: expected repeater empty view to have been named and counted`);
|
|
20786
|
+
}
|
|
20787
|
+
emptyViewFnName = emptyView.fnName;
|
|
20788
|
+
emptyDecls = emptyView.decls;
|
|
20789
|
+
emptyVars = emptyView.vars;
|
|
20790
|
+
}
|
|
20791
|
+
OpList.replace(op, repeaterCreate(op.slot, repeaterView.fnName, op.decls, op.vars, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
|
|
20652
20792
|
break;
|
|
20653
20793
|
case OpKind.Statement:
|
|
20654
20794
|
// Pass statement operations directly through.
|
|
@@ -20745,6 +20885,9 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
20745
20885
|
}
|
|
20746
20886
|
OpList.replace(op, conditional(op.targetSlot, op.processed, op.contextValue, op.sourceSpan));
|
|
20747
20887
|
break;
|
|
20888
|
+
case OpKind.Repeater:
|
|
20889
|
+
OpList.replace(op, repeater(op.targetSlot, op.collection, op.sourceSpan));
|
|
20890
|
+
break;
|
|
20748
20891
|
case OpKind.Statement:
|
|
20749
20892
|
// Pass statement operations directly through.
|
|
20750
20893
|
break;
|
|
@@ -20883,6 +21026,10 @@ function processLexicalScope$1(view, ops) {
|
|
|
20883
21026
|
break;
|
|
20884
21027
|
}
|
|
20885
21028
|
}
|
|
21029
|
+
if (view === view.job.root) {
|
|
21030
|
+
// Prefer `ctx` of the root view to any variables which happen to contain the root context.
|
|
21031
|
+
scope.set(view.xref, variable('ctx'));
|
|
21032
|
+
}
|
|
20886
21033
|
for (const op of ops) {
|
|
20887
21034
|
transformExpressionsInOp(op, expr => {
|
|
20888
21035
|
if (expr instanceof ContextExpr) {
|
|
@@ -21229,6 +21376,7 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
21229
21376
|
case OpKind.Variable:
|
|
21230
21377
|
switch (op.variable.kind) {
|
|
21231
21378
|
case SemanticVariableKind.Identifier:
|
|
21379
|
+
case SemanticVariableKind.Alias:
|
|
21232
21380
|
// This variable represents some kind of identifier which can be used in the template.
|
|
21233
21381
|
if (scope.has(op.variable.identifier)) {
|
|
21234
21382
|
continue;
|
|
@@ -21311,7 +21459,7 @@ const sanitizers = new Map([
|
|
|
21311
21459
|
*/
|
|
21312
21460
|
function phaseResolveSanitizers(job) {
|
|
21313
21461
|
for (const unit of job.units) {
|
|
21314
|
-
const elements =
|
|
21462
|
+
const elements = createOpXrefMap(unit);
|
|
21315
21463
|
let sanitizerFn;
|
|
21316
21464
|
for (const op of unit.update) {
|
|
21317
21465
|
switch (op.kind) {
|
|
@@ -21325,7 +21473,7 @@ function phaseResolveSanitizers(job) {
|
|
|
21325
21473
|
// <iframe>).
|
|
21326
21474
|
if (op.sanitizer === null) {
|
|
21327
21475
|
const ownerOp = elements.get(op.target);
|
|
21328
|
-
if (ownerOp === undefined) {
|
|
21476
|
+
if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
|
|
21329
21477
|
throw Error('Property should have an element-like owner');
|
|
21330
21478
|
}
|
|
21331
21479
|
if (isIframeElement$1(ownerOp) && isIframeSecuritySensitiveAttr(op.name)) {
|
|
@@ -21351,7 +21499,7 @@ function phaseSaveRestoreView(job) {
|
|
|
21351
21499
|
kind: SemanticVariableKind.SavedView,
|
|
21352
21500
|
name: null,
|
|
21353
21501
|
view: view.xref,
|
|
21354
|
-
}, new GetCurrentViewExpr()),
|
|
21502
|
+
}, new GetCurrentViewExpr(), VariableFlags.None),
|
|
21355
21503
|
]);
|
|
21356
21504
|
for (const op of view.create) {
|
|
21357
21505
|
if (op.kind !== OpKind.Listener) {
|
|
@@ -21381,7 +21529,7 @@ function addSaveRestoreViewOperationToListener(unit, op) {
|
|
|
21381
21529
|
kind: SemanticVariableKind.Context,
|
|
21382
21530
|
name: null,
|
|
21383
21531
|
view: unit.xref,
|
|
21384
|
-
}, new RestoreViewExpr(unit.xref)),
|
|
21532
|
+
}, new RestoreViewExpr(unit.xref), VariableFlags.None),
|
|
21385
21533
|
]);
|
|
21386
21534
|
// The "restore view" operation in listeners requires a call to `resetView` to reset the
|
|
21387
21535
|
// context prior to returning from the listener operation. Find any `return` statements in
|
|
@@ -21436,13 +21584,13 @@ function phaseSlotAllocation(job) {
|
|
|
21436
21584
|
// propagate the number of slots used for each view into the operation which declares it.
|
|
21437
21585
|
for (const unit of job.units) {
|
|
21438
21586
|
for (const op of unit.ops()) {
|
|
21439
|
-
if (op.kind === OpKind.Template) {
|
|
21587
|
+
if (op.kind === OpKind.Template || op.kind === OpKind.RepeaterCreate) {
|
|
21440
21588
|
// Record the number of slots used by the view this `ir.TemplateOp` declares in the
|
|
21441
21589
|
// operation itself, so it can be emitted later.
|
|
21442
21590
|
const childView = job.views.get(op.xref);
|
|
21443
21591
|
op.decls = childView.decls;
|
|
21444
21592
|
}
|
|
21445
|
-
if (hasUsesSlotIndexTrait(op) && op.targetSlot === null) {
|
|
21593
|
+
if (hasUsesSlotIndexTrait(op) && op.target !== null && op.targetSlot === null) {
|
|
21446
21594
|
if (!slotMap.has(op.target)) {
|
|
21447
21595
|
// We do expect to find a slot allocated for everything which might be referenced.
|
|
21448
21596
|
throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
|
|
@@ -21584,6 +21732,137 @@ function assignName(names, expr) {
|
|
|
21584
21732
|
expr.name = name;
|
|
21585
21733
|
}
|
|
21586
21734
|
|
|
21735
|
+
/**
|
|
21736
|
+
* Counts the number of variable slots used within each view, and stores that on the view itself, as
|
|
21737
|
+
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
21738
|
+
*/
|
|
21739
|
+
function phaseVarCounting(job) {
|
|
21740
|
+
// First, count the vars used in each view, and update the view-level counter.
|
|
21741
|
+
for (const unit of job.units) {
|
|
21742
|
+
let varCount = 0;
|
|
21743
|
+
// Count variables on top-level ops first. Don't explore nested expressions just yet.
|
|
21744
|
+
for (const op of unit.ops()) {
|
|
21745
|
+
if (hasConsumesVarsTrait(op)) {
|
|
21746
|
+
varCount += varsUsedByOp(op);
|
|
21747
|
+
}
|
|
21748
|
+
}
|
|
21749
|
+
// Count variables on expressions inside ops. We do this later because some of these expressions
|
|
21750
|
+
// might be conditional (e.g. `pipeBinding` inside of a ternary), and we don't want to interfere
|
|
21751
|
+
// with indices for top-level binding slots (e.g. `property`).
|
|
21752
|
+
for (const op of unit.ops()) {
|
|
21753
|
+
visitExpressionsInOp(op, expr => {
|
|
21754
|
+
if (!isIrExpression(expr)) {
|
|
21755
|
+
return;
|
|
21756
|
+
}
|
|
21757
|
+
// TemplateDefinitionBuilder assigns variable offsets for everything but pure functions
|
|
21758
|
+
// first, and then assigns offsets to pure functions lazily. We emulate that behavior by
|
|
21759
|
+
// assigning offsets in two passes instead of one, only in compatibility mode.
|
|
21760
|
+
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
21761
|
+
expr instanceof PureFunctionExpr) {
|
|
21762
|
+
return;
|
|
21763
|
+
}
|
|
21764
|
+
// Some expressions require knowledge of the number of variable slots consumed.
|
|
21765
|
+
if (hasUsesVarOffsetTrait(expr)) {
|
|
21766
|
+
expr.varOffset = varCount;
|
|
21767
|
+
}
|
|
21768
|
+
if (hasConsumesVarsTrait(expr)) {
|
|
21769
|
+
varCount += varsUsedByIrExpression(expr);
|
|
21770
|
+
}
|
|
21771
|
+
});
|
|
21772
|
+
}
|
|
21773
|
+
// Compatiblity mode pass for pure function offsets (as explained above).
|
|
21774
|
+
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
21775
|
+
for (const op of unit.ops()) {
|
|
21776
|
+
visitExpressionsInOp(op, expr => {
|
|
21777
|
+
if (!isIrExpression(expr) || !(expr instanceof PureFunctionExpr)) {
|
|
21778
|
+
return;
|
|
21779
|
+
}
|
|
21780
|
+
// Some expressions require knowledge of the number of variable slots consumed.
|
|
21781
|
+
if (hasUsesVarOffsetTrait(expr)) {
|
|
21782
|
+
expr.varOffset = varCount;
|
|
21783
|
+
}
|
|
21784
|
+
if (hasConsumesVarsTrait(expr)) {
|
|
21785
|
+
varCount += varsUsedByIrExpression(expr);
|
|
21786
|
+
}
|
|
21787
|
+
});
|
|
21788
|
+
}
|
|
21789
|
+
}
|
|
21790
|
+
unit.vars = varCount;
|
|
21791
|
+
}
|
|
21792
|
+
if (job instanceof ComponentCompilationJob) {
|
|
21793
|
+
// Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is
|
|
21794
|
+
// an embedded view).
|
|
21795
|
+
for (const unit of job.units) {
|
|
21796
|
+
for (const op of unit.create) {
|
|
21797
|
+
if (op.kind !== OpKind.Template && op.kind !== OpKind.RepeaterCreate) {
|
|
21798
|
+
continue;
|
|
21799
|
+
}
|
|
21800
|
+
const childView = job.views.get(op.xref);
|
|
21801
|
+
op.vars = childView.vars;
|
|
21802
|
+
}
|
|
21803
|
+
}
|
|
21804
|
+
}
|
|
21805
|
+
}
|
|
21806
|
+
/**
|
|
21807
|
+
* Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so
|
|
21808
|
+
* count the variables used by any particular `op`.
|
|
21809
|
+
*/
|
|
21810
|
+
function varsUsedByOp(op) {
|
|
21811
|
+
let slots;
|
|
21812
|
+
switch (op.kind) {
|
|
21813
|
+
case OpKind.Property:
|
|
21814
|
+
case OpKind.HostProperty:
|
|
21815
|
+
case OpKind.Attribute:
|
|
21816
|
+
// All of these bindings use 1 variable slot, plus 1 slot for every interpolated expression,
|
|
21817
|
+
// if any.
|
|
21818
|
+
slots = 1;
|
|
21819
|
+
if (op.expression instanceof Interpolation && !isSingletonInterpolation(op.expression)) {
|
|
21820
|
+
slots += op.expression.expressions.length;
|
|
21821
|
+
}
|
|
21822
|
+
return slots;
|
|
21823
|
+
case OpKind.StyleProp:
|
|
21824
|
+
case OpKind.ClassProp:
|
|
21825
|
+
case OpKind.StyleMap:
|
|
21826
|
+
case OpKind.ClassMap:
|
|
21827
|
+
// Style & class bindings use 2 variable slots, plus 1 slot for every interpolated expression,
|
|
21828
|
+
// if any.
|
|
21829
|
+
slots = 2;
|
|
21830
|
+
if (op.expression instanceof Interpolation) {
|
|
21831
|
+
slots += op.expression.expressions.length;
|
|
21832
|
+
}
|
|
21833
|
+
return slots;
|
|
21834
|
+
case OpKind.InterpolateText:
|
|
21835
|
+
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
21836
|
+
return op.interpolation.expressions.length;
|
|
21837
|
+
case OpKind.I18nExpression:
|
|
21838
|
+
case OpKind.Conditional:
|
|
21839
|
+
return 1;
|
|
21840
|
+
default:
|
|
21841
|
+
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
21842
|
+
}
|
|
21843
|
+
}
|
|
21844
|
+
function varsUsedByIrExpression(expr) {
|
|
21845
|
+
switch (expr.kind) {
|
|
21846
|
+
case ExpressionKind.PureFunctionExpr:
|
|
21847
|
+
return 1 + expr.args.length;
|
|
21848
|
+
case ExpressionKind.PipeBinding:
|
|
21849
|
+
return 1 + expr.args.length;
|
|
21850
|
+
case ExpressionKind.PipeBindingVariadic:
|
|
21851
|
+
return 1 + expr.numArgs;
|
|
21852
|
+
default:
|
|
21853
|
+
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
21854
|
+
}
|
|
21855
|
+
}
|
|
21856
|
+
function isSingletonInterpolation(expr) {
|
|
21857
|
+
if (expr.expressions.length !== 1 || expr.strings.length !== 2) {
|
|
21858
|
+
return false;
|
|
21859
|
+
}
|
|
21860
|
+
if (expr.strings[0] !== '' || expr.strings[1] !== '') {
|
|
21861
|
+
return false;
|
|
21862
|
+
}
|
|
21863
|
+
return true;
|
|
21864
|
+
}
|
|
21865
|
+
|
|
21587
21866
|
/**
|
|
21588
21867
|
* Optimize variables declared and used in the IR.
|
|
21589
21868
|
*
|
|
@@ -21604,6 +21883,13 @@ function assignName(names, expr) {
|
|
|
21604
21883
|
*/
|
|
21605
21884
|
function phaseVariableOptimization(job) {
|
|
21606
21885
|
for (const unit of job.units) {
|
|
21886
|
+
inlineAlwaysInlineVariables(unit.create);
|
|
21887
|
+
inlineAlwaysInlineVariables(unit.update);
|
|
21888
|
+
for (const op of unit.create) {
|
|
21889
|
+
if (op.kind === OpKind.Listener) {
|
|
21890
|
+
inlineAlwaysInlineVariables(op.handlerOps);
|
|
21891
|
+
}
|
|
21892
|
+
}
|
|
21607
21893
|
optimizeVariablesInOpList(unit.create, job.compatibility);
|
|
21608
21894
|
optimizeVariablesInOpList(unit.update, job.compatibility);
|
|
21609
21895
|
for (const op of unit.create) {
|
|
@@ -21646,6 +21932,30 @@ var Fence;
|
|
|
21646
21932
|
*/
|
|
21647
21933
|
Fence[Fence["SideEffectful"] = 4] = "SideEffectful";
|
|
21648
21934
|
})(Fence || (Fence = {}));
|
|
21935
|
+
function inlineAlwaysInlineVariables(ops) {
|
|
21936
|
+
const vars = new Map();
|
|
21937
|
+
for (const op of ops) {
|
|
21938
|
+
if (op.kind === OpKind.Variable && op.flags & VariableFlags.AlwaysInline) {
|
|
21939
|
+
visitExpressionsInOp(op, expr => {
|
|
21940
|
+
if (isIrExpression(expr) && fencesForIrExpression(expr) !== Fence.None) {
|
|
21941
|
+
throw new Error(`AssertionError: A context-sensitive variable was marked AlwaysInline`);
|
|
21942
|
+
}
|
|
21943
|
+
});
|
|
21944
|
+
vars.set(op.xref, op);
|
|
21945
|
+
}
|
|
21946
|
+
transformExpressionsInOp(op, expr => {
|
|
21947
|
+
if (expr instanceof ReadVariableExpr && vars.has(expr.xref)) {
|
|
21948
|
+
const varOp = vars.get(expr.xref);
|
|
21949
|
+
// Inline by cloning, because we might inline into multiple places.
|
|
21950
|
+
return varOp.initializer.clone();
|
|
21951
|
+
}
|
|
21952
|
+
return expr;
|
|
21953
|
+
}, VisitorContextFlag.None);
|
|
21954
|
+
}
|
|
21955
|
+
for (const op of vars.values()) {
|
|
21956
|
+
OpList.remove(op);
|
|
21957
|
+
}
|
|
21958
|
+
}
|
|
21649
21959
|
/**
|
|
21650
21960
|
* Process a list of operations and optimize variables within that list.
|
|
21651
21961
|
*/
|
|
@@ -21715,10 +22025,15 @@ function optimizeVariablesInOpList(ops, compatibility) {
|
|
|
21715
22025
|
// Next, inline any remaining variables with exactly one usage.
|
|
21716
22026
|
const toInline = [];
|
|
21717
22027
|
for (const [id, count] of varUsages) {
|
|
22028
|
+
const decl = varDecls.get(id);
|
|
22029
|
+
const varInfo = opMap.get(decl);
|
|
21718
22030
|
// We can inline variables that:
|
|
21719
|
-
// - are used once
|
|
22031
|
+
// - are used exactly once, and
|
|
21720
22032
|
// - are not used remotely
|
|
21721
|
-
|
|
22033
|
+
// OR
|
|
22034
|
+
// - are marked for always inlining
|
|
22035
|
+
const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline);
|
|
22036
|
+
if (count !== 1 || isAlwaysInline) {
|
|
21722
22037
|
// We can't inline this variable as it's used more than once.
|
|
21723
22038
|
continue;
|
|
21724
22039
|
}
|
|
@@ -21734,6 +22049,10 @@ function optimizeVariablesInOpList(ops, compatibility) {
|
|
|
21734
22049
|
// no future operation will make inlining legal.
|
|
21735
22050
|
const decl = varDecls.get(candidate);
|
|
21736
22051
|
const varInfo = opMap.get(decl);
|
|
22052
|
+
const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline);
|
|
22053
|
+
if (isAlwaysInline) {
|
|
22054
|
+
throw new Error(`AssertionError: Found an 'AlwaysInline' variable after the always inlining pass.`);
|
|
22055
|
+
}
|
|
21737
22056
|
// Scan operations following the variable declaration and look for the point where that variable
|
|
21738
22057
|
// is used. There should only be one usage given the precondition above.
|
|
21739
22058
|
for (let targetOp = decl.next; targetOp.kind !== OpKind.ListEnd; targetOp = targetOp.next) {
|
|
@@ -21981,6 +22300,141 @@ function phaseWrapIcus(job) {
|
|
|
21981
22300
|
}
|
|
21982
22301
|
}
|
|
21983
22302
|
|
|
22303
|
+
function phaseTrackVariables(job) {
|
|
22304
|
+
for (const unit of job.units) {
|
|
22305
|
+
for (const op of unit.create) {
|
|
22306
|
+
if (op.kind !== OpKind.RepeaterCreate) {
|
|
22307
|
+
continue;
|
|
22308
|
+
}
|
|
22309
|
+
op.track = transformExpressionsInExpression(op.track, expr => {
|
|
22310
|
+
if (expr instanceof LexicalReadExpr) {
|
|
22311
|
+
if (expr.name === op.varNames.$index) {
|
|
22312
|
+
return variable('$index');
|
|
22313
|
+
}
|
|
22314
|
+
else if (expr.name === op.varNames.$implicit) {
|
|
22315
|
+
return variable('$item');
|
|
22316
|
+
}
|
|
22317
|
+
// TODO: handle prohibited context variables (emit as globals?)
|
|
22318
|
+
}
|
|
22319
|
+
return expr;
|
|
22320
|
+
}, VisitorContextFlag.None);
|
|
22321
|
+
}
|
|
22322
|
+
}
|
|
22323
|
+
}
|
|
22324
|
+
|
|
22325
|
+
/**
|
|
22326
|
+
* Generate track functions that need to be extracted to the constant pool. This entails wrapping
|
|
22327
|
+
* them in an arrow (or traditional) function, replacing context reads with `this.`, and storing
|
|
22328
|
+
* them in the constant pool.
|
|
22329
|
+
*
|
|
22330
|
+
* Note that, if a track function was previously optimized, it will not need to be extracted, and
|
|
22331
|
+
* this phase is a no-op.
|
|
22332
|
+
*/
|
|
22333
|
+
function phaseTrackFnGeneration(job) {
|
|
22334
|
+
for (const unit of job.units) {
|
|
22335
|
+
for (const op of unit.create) {
|
|
22336
|
+
if (op.kind !== OpKind.RepeaterCreate) {
|
|
22337
|
+
continue;
|
|
22338
|
+
}
|
|
22339
|
+
if (op.trackByFn !== null) {
|
|
22340
|
+
// The final track function was already set, probably because it was optimized.
|
|
22341
|
+
continue;
|
|
22342
|
+
}
|
|
22343
|
+
// Find all component context reads.
|
|
22344
|
+
let usesComponentContext = false;
|
|
22345
|
+
op.track = transformExpressionsInExpression(op.track, expr => {
|
|
22346
|
+
if (expr instanceof TrackContextExpr) {
|
|
22347
|
+
usesComponentContext = true;
|
|
22348
|
+
return variable('this');
|
|
22349
|
+
}
|
|
22350
|
+
return expr;
|
|
22351
|
+
}, VisitorContextFlag.None);
|
|
22352
|
+
let fn;
|
|
22353
|
+
const fnParams = [new FnParam('$index'), new FnParam('$item')];
|
|
22354
|
+
if (usesComponentContext) {
|
|
22355
|
+
fn = new FunctionExpr(fnParams, [new ReturnStatement(op.track)]);
|
|
22356
|
+
}
|
|
22357
|
+
else {
|
|
22358
|
+
fn = arrowFn(fnParams, op.track);
|
|
22359
|
+
}
|
|
22360
|
+
op.trackByFn = job.pool.getSharedFunctionReference(fn, '_forTrack');
|
|
22361
|
+
}
|
|
22362
|
+
}
|
|
22363
|
+
}
|
|
22364
|
+
|
|
22365
|
+
function phaseTrackFnOptimization(job) {
|
|
22366
|
+
for (const unit of job.units) {
|
|
22367
|
+
for (const op of unit.create) {
|
|
22368
|
+
if (op.kind !== OpKind.RepeaterCreate) {
|
|
22369
|
+
continue;
|
|
22370
|
+
}
|
|
22371
|
+
if (op.track instanceof ReadVarExpr && op.track.name === '$index') {
|
|
22372
|
+
// Top-level access of `$index` uses the built in `repeaterTrackByIndex`.
|
|
22373
|
+
op.trackByFn = importExpr(Identifiers.repeaterTrackByIndex);
|
|
22374
|
+
}
|
|
22375
|
+
else if (op.track instanceof ReadVarExpr && op.track.name === '$item') {
|
|
22376
|
+
// Top-level access of the item uses the built in `repeaterTrackByIdentity`.
|
|
22377
|
+
op.trackByFn = importExpr(Identifiers.repeaterTrackByIdentity);
|
|
22378
|
+
}
|
|
22379
|
+
else if (isTrackByFunctionCall(job.root.xref, op.track)) {
|
|
22380
|
+
// Top-level method calls in the form of `fn($index, item)` can be passed in directly.
|
|
22381
|
+
if (op.track.receiver.receiver.view === unit.xref) {
|
|
22382
|
+
// TODO: this may be wrong
|
|
22383
|
+
op.trackByFn = op.track.receiver;
|
|
22384
|
+
}
|
|
22385
|
+
else {
|
|
22386
|
+
// This is a plain method call, but not in the component's root view.
|
|
22387
|
+
// We need to get the component instance, and then call the method on it.
|
|
22388
|
+
op.trackByFn =
|
|
22389
|
+
importExpr(Identifiers.componentInstance).callFn([]).prop(op.track.receiver.name);
|
|
22390
|
+
// Because the context is not avaiable (without a special function), we don't want to
|
|
22391
|
+
// try to resolve it later. Let's get rid of it by overwriting the original track
|
|
22392
|
+
// expression (which won't be used anyway).
|
|
22393
|
+
op.track = op.trackByFn;
|
|
22394
|
+
}
|
|
22395
|
+
}
|
|
22396
|
+
else {
|
|
22397
|
+
// The track function could not be optimized.
|
|
22398
|
+
// Replace context reads with a special IR expression, since context reads in a track
|
|
22399
|
+
// function are emitted specially.
|
|
22400
|
+
op.track = transformExpressionsInExpression(op.track, expr => {
|
|
22401
|
+
if (expr instanceof ContextExpr) {
|
|
22402
|
+
op.usesComponentInstance = true;
|
|
22403
|
+
return new TrackContextExpr(expr.view);
|
|
22404
|
+
}
|
|
22405
|
+
return expr;
|
|
22406
|
+
}, VisitorContextFlag.None);
|
|
22407
|
+
}
|
|
22408
|
+
}
|
|
22409
|
+
}
|
|
22410
|
+
}
|
|
22411
|
+
function isTrackByFunctionCall(rootView, expr) {
|
|
22412
|
+
if (!(expr instanceof InvokeFunctionExpr) || expr.args.length !== 2) {
|
|
22413
|
+
return false;
|
|
22414
|
+
}
|
|
22415
|
+
if (!(expr.receiver instanceof ReadPropExpr &&
|
|
22416
|
+
expr.receiver.receiver instanceof ContextExpr) ||
|
|
22417
|
+
expr.receiver.receiver.view !== rootView) {
|
|
22418
|
+
return false;
|
|
22419
|
+
}
|
|
22420
|
+
const [arg0, arg1] = expr.args;
|
|
22421
|
+
if (!(arg0 instanceof ReadVarExpr) || arg0.name !== '$index') {
|
|
22422
|
+
return false;
|
|
22423
|
+
}
|
|
22424
|
+
if (!(arg1 instanceof ReadVarExpr) || arg1.name !== '$item') {
|
|
22425
|
+
return false;
|
|
22426
|
+
}
|
|
22427
|
+
return true;
|
|
22428
|
+
}
|
|
22429
|
+
|
|
22430
|
+
/**
|
|
22431
|
+
*
|
|
22432
|
+
* @license
|
|
22433
|
+
* Copyright Google LLC All Rights Reserved.
|
|
22434
|
+
*
|
|
22435
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
22436
|
+
* found in the LICENSE file at https://angular.io/license
|
|
22437
|
+
*/
|
|
21984
22438
|
const phases = [
|
|
21985
22439
|
{ kind: CompilationJobKind.Tmpl, fn: phaseRemoveContentSelectors },
|
|
21986
22440
|
{ kind: CompilationJobKind.Host, fn: phaseHostStylePropertyParsing },
|
|
@@ -21992,6 +22446,8 @@ const phases = [
|
|
|
21992
22446
|
{ kind: CompilationJobKind.Both, fn: phaseAttributeExtraction },
|
|
21993
22447
|
{ kind: CompilationJobKind.Both, fn: phaseParseExtractedStyles },
|
|
21994
22448
|
{ kind: CompilationJobKind.Tmpl, fn: phaseRemoveEmptyBindings },
|
|
22449
|
+
{ kind: CompilationJobKind.Both, fn: phaseCollapseSingletonInterpolations },
|
|
22450
|
+
{ kind: CompilationJobKind.Both, fn: phaseOrdering },
|
|
21995
22451
|
{ kind: CompilationJobKind.Tmpl, fn: phaseConditionals },
|
|
21996
22452
|
{ kind: CompilationJobKind.Tmpl, fn: phasePipeCreation },
|
|
21997
22453
|
{ kind: CompilationJobKind.Tmpl, fn: phaseI18nTextExtraction },
|
|
@@ -22004,7 +22460,10 @@ const phases = [
|
|
|
22004
22460
|
{ kind: CompilationJobKind.Tmpl, fn: phaseSaveRestoreView },
|
|
22005
22461
|
{ kind: CompilationJobKind.Tmpl, fn: phaseFindAnyCasts },
|
|
22006
22462
|
{ kind: CompilationJobKind.Both, fn: phaseResolveDollarEvent },
|
|
22463
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseRepeaterDerivedVars },
|
|
22464
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseTrackVariables },
|
|
22007
22465
|
{ kind: CompilationJobKind.Both, fn: phaseResolveNames },
|
|
22466
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseTrackFnOptimization },
|
|
22008
22467
|
{ kind: CompilationJobKind.Both, fn: phaseResolveContexts },
|
|
22009
22468
|
{ kind: CompilationJobKind.Tmpl, fn: phaseResolveSanitizers },
|
|
22010
22469
|
{ kind: CompilationJobKind.Tmpl, fn: phaseLocalRefs },
|
|
@@ -22013,6 +22472,7 @@ const phases = [
|
|
|
22013
22472
|
{ kind: CompilationJobKind.Both, fn: phaseTemporaryVariables },
|
|
22014
22473
|
{ kind: CompilationJobKind.Tmpl, fn: phaseSlotAllocation },
|
|
22015
22474
|
{ kind: CompilationJobKind.Tmpl, fn: phaseResolveI18nPlaceholders },
|
|
22475
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseTrackFnGeneration },
|
|
22016
22476
|
{ kind: CompilationJobKind.Tmpl, fn: phaseI18nMessageExtraction },
|
|
22017
22477
|
{ kind: CompilationJobKind.Tmpl, fn: phaseI18nConstCollection },
|
|
22018
22478
|
{ kind: CompilationJobKind.Tmpl, fn: phaseConstTraitCollection },
|
|
@@ -22027,8 +22487,6 @@ const phases = [
|
|
|
22027
22487
|
{ kind: CompilationJobKind.Tmpl, fn: phaseEmptyElements },
|
|
22028
22488
|
{ kind: CompilationJobKind.Tmpl, fn: phaseNonbindable },
|
|
22029
22489
|
{ kind: CompilationJobKind.Both, fn: phasePureFunctionExtraction },
|
|
22030
|
-
{ kind: CompilationJobKind.Tmpl, fn: phaseAlignPipeVariadicVarOffset },
|
|
22031
|
-
{ kind: CompilationJobKind.Both, fn: phaseOrdering },
|
|
22032
22490
|
{ kind: CompilationJobKind.Both, fn: phaseReify },
|
|
22033
22491
|
{ kind: CompilationJobKind.Both, fn: phaseChaining },
|
|
22034
22492
|
];
|
|
@@ -22233,6 +22691,9 @@ function ingestNodes(unit, template) {
|
|
|
22233
22691
|
else if (node instanceof Icu$1) {
|
|
22234
22692
|
ingestIcu(unit, node);
|
|
22235
22693
|
}
|
|
22694
|
+
else if (node instanceof ForLoopBlock) {
|
|
22695
|
+
ingestForBlock(unit, node);
|
|
22696
|
+
}
|
|
22236
22697
|
else {
|
|
22237
22698
|
throw new Error(`Unsupported template node: ${node.constructor.name}`);
|
|
22238
22699
|
}
|
|
@@ -22287,7 +22748,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
22287
22748
|
ingestReferences(tplOp, tmpl);
|
|
22288
22749
|
ingestNodes(childView, tmpl.children);
|
|
22289
22750
|
for (const { name, value } of tmpl.variables) {
|
|
22290
|
-
childView.contextVariables.set(name, value);
|
|
22751
|
+
childView.contextVariables.set(name, value !== '' ? value : '$implicit');
|
|
22291
22752
|
}
|
|
22292
22753
|
// If this is a plain template and there is an i18n message associated with it, insert i18n start
|
|
22293
22754
|
// and end ops. For structural directive templates, the i18n ops will be added when ingesting the
|
|
@@ -22302,7 +22763,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
22302
22763
|
* Ingest a literal text node from the AST into the given `ViewCompilation`.
|
|
22303
22764
|
*/
|
|
22304
22765
|
function ingestContent(unit, content) {
|
|
22305
|
-
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector);
|
|
22766
|
+
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.sourceSpan);
|
|
22306
22767
|
for (const attr of content.attributes) {
|
|
22307
22768
|
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
|
|
22308
22769
|
}
|
|
@@ -22438,6 +22899,50 @@ function ingestIcu(unit, icu) {
|
|
|
22438
22899
|
throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`);
|
|
22439
22900
|
}
|
|
22440
22901
|
}
|
|
22902
|
+
/**
|
|
22903
|
+
* Ingest an `@for` block into the given `ViewCompilation`.
|
|
22904
|
+
*/
|
|
22905
|
+
function ingestForBlock(unit, forBlock) {
|
|
22906
|
+
const repeaterView = unit.job.allocateView(unit.xref);
|
|
22907
|
+
const createRepeaterAlias = (ident, repeaterVar) => {
|
|
22908
|
+
repeaterView.aliases.add({
|
|
22909
|
+
kind: SemanticVariableKind.Alias,
|
|
22910
|
+
name: null,
|
|
22911
|
+
identifier: ident,
|
|
22912
|
+
expression: new DerivedRepeaterVarExpr(repeaterView.xref, repeaterVar),
|
|
22913
|
+
});
|
|
22914
|
+
};
|
|
22915
|
+
// Set all the context variables and aliases available in the repeater.
|
|
22916
|
+
repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
|
|
22917
|
+
repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value);
|
|
22918
|
+
repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value);
|
|
22919
|
+
createRepeaterAlias(forBlock.contextVariables.$first.name, DerivedRepeaterVarIdentity.First);
|
|
22920
|
+
createRepeaterAlias(forBlock.contextVariables.$last.name, DerivedRepeaterVarIdentity.Last);
|
|
22921
|
+
createRepeaterAlias(forBlock.contextVariables.$even.name, DerivedRepeaterVarIdentity.Even);
|
|
22922
|
+
createRepeaterAlias(forBlock.contextVariables.$odd.name, DerivedRepeaterVarIdentity.Odd);
|
|
22923
|
+
const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
|
|
22924
|
+
const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
|
|
22925
|
+
ingestNodes(repeaterView, forBlock.children);
|
|
22926
|
+
let emptyView = null;
|
|
22927
|
+
if (forBlock.empty !== null) {
|
|
22928
|
+
emptyView = unit.job.allocateView(unit.xref);
|
|
22929
|
+
ingestNodes(emptyView, forBlock.empty.children);
|
|
22930
|
+
}
|
|
22931
|
+
const varNames = {
|
|
22932
|
+
$index: forBlock.contextVariables.$index.name,
|
|
22933
|
+
$count: forBlock.contextVariables.$count.name,
|
|
22934
|
+
$first: forBlock.contextVariables.$first.name,
|
|
22935
|
+
$last: forBlock.contextVariables.$last.name,
|
|
22936
|
+
$even: forBlock.contextVariables.$even.name,
|
|
22937
|
+
$odd: forBlock.contextVariables.$odd.name,
|
|
22938
|
+
$implicit: forBlock.item.name,
|
|
22939
|
+
};
|
|
22940
|
+
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, track, varNames, forBlock.sourceSpan);
|
|
22941
|
+
unit.create.push(repeaterCreate);
|
|
22942
|
+
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
22943
|
+
const repeater = createRepeaterOp(repeaterCreate.xref, expression, forBlock.sourceSpan);
|
|
22944
|
+
unit.update.push(repeater);
|
|
22945
|
+
}
|
|
22441
22946
|
/**
|
|
22442
22947
|
* Convert a template AST expression into an output AST expression.
|
|
22443
22948
|
*/
|
|
@@ -22490,7 +22995,8 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
22490
22995
|
else if (ast instanceof LiteralMap) {
|
|
22491
22996
|
const entries = ast.keys.map((key, idx) => {
|
|
22492
22997
|
const value = ast.values[idx];
|
|
22493
|
-
// TODO: should literals have source maps, or do we just map the whole surrounding
|
|
22998
|
+
// TODO: should literals have source maps, or do we just map the whole surrounding
|
|
22999
|
+
// expression?
|
|
22494
23000
|
return new LiteralMapEntry(key.key, convertAst(value, job, baseSourceSpan), key.quoted);
|
|
22495
23001
|
});
|
|
22496
23002
|
return new LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
@@ -23323,7 +23829,7 @@ class WhitespaceVisitor {
|
|
|
23323
23829
|
return expansionCase;
|
|
23324
23830
|
}
|
|
23325
23831
|
visitBlock(block, context) {
|
|
23326
|
-
return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
|
|
23832
|
+
return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.nameSpan, block.startSourceSpan, block.endSourceSpan);
|
|
23327
23833
|
}
|
|
23328
23834
|
visitBlockParameter(parameter, context) {
|
|
23329
23835
|
return parameter;
|
|
@@ -23962,7 +24468,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
|
|
|
23962
24468
|
continue;
|
|
23963
24469
|
}
|
|
23964
24470
|
if ((node.name !== 'case' || node.parameters.length === 0) && node.name !== 'default') {
|
|
23965
|
-
unknownBlocks.push(new UnknownBlock(node.name, node.sourceSpan));
|
|
24471
|
+
unknownBlocks.push(new UnknownBlock(node.name, node.sourceSpan, node.nameSpan));
|
|
23966
24472
|
continue;
|
|
23967
24473
|
}
|
|
23968
24474
|
const expression = node.name === 'case' ?
|
|
@@ -24940,7 +25446,7 @@ class HtmlAstToIvyAst {
|
|
|
24940
25446
|
errorMessage = `Unrecognized block @${block.name}.`;
|
|
24941
25447
|
}
|
|
24942
25448
|
result = {
|
|
24943
|
-
node: new UnknownBlock(block.name, block.sourceSpan),
|
|
25449
|
+
node: new UnknownBlock(block.name, block.sourceSpan, block.nameSpan),
|
|
24944
25450
|
errors: [new ParseError(block.sourceSpan, errorMessage)],
|
|
24945
25451
|
};
|
|
24946
25452
|
break;
|
|
@@ -29756,7 +30262,7 @@ function publishFacade(global) {
|
|
|
29756
30262
|
* @description
|
|
29757
30263
|
* Entry point for all public APIs of the compiler package.
|
|
29758
30264
|
*/
|
|
29759
|
-
const VERSION = new Version('17.0.0-rc.
|
|
30265
|
+
const VERSION = new Version('17.0.0-rc.1');
|
|
29760
30266
|
|
|
29761
30267
|
class CompilerConfig {
|
|
29762
30268
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -31286,7 +31792,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
31286
31792
|
function compileDeclareClassMetadata(metadata) {
|
|
31287
31793
|
const definitionMap = new DefinitionMap();
|
|
31288
31794
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
31289
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
31795
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31290
31796
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31291
31797
|
definitionMap.set('type', metadata.type);
|
|
31292
31798
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -31394,7 +31900,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
31394
31900
|
// in 16.1 is actually used.
|
|
31395
31901
|
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
|
|
31396
31902
|
definitionMap.set('minVersion', literal(minVersion));
|
|
31397
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
31903
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31398
31904
|
// e.g. `type: MyDirective`
|
|
31399
31905
|
definitionMap.set('type', meta.type.value);
|
|
31400
31906
|
if (meta.isStandalone) {
|
|
@@ -31671,7 +32177,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
31671
32177
|
function compileDeclareFactoryFunction(meta) {
|
|
31672
32178
|
const definitionMap = new DefinitionMap();
|
|
31673
32179
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
31674
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
32180
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31675
32181
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31676
32182
|
definitionMap.set('type', meta.type.value);
|
|
31677
32183
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -31706,7 +32212,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
31706
32212
|
function createInjectableDefinitionMap(meta) {
|
|
31707
32213
|
const definitionMap = new DefinitionMap();
|
|
31708
32214
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
31709
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
32215
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31710
32216
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31711
32217
|
definitionMap.set('type', meta.type.value);
|
|
31712
32218
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -31757,7 +32263,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
31757
32263
|
function createInjectorDefinitionMap(meta) {
|
|
31758
32264
|
const definitionMap = new DefinitionMap();
|
|
31759
32265
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
31760
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
32266
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31761
32267
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31762
32268
|
definitionMap.set('type', meta.type.value);
|
|
31763
32269
|
definitionMap.set('providers', meta.providers);
|
|
@@ -31790,7 +32296,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
31790
32296
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
31791
32297
|
}
|
|
31792
32298
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
31793
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
32299
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31794
32300
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31795
32301
|
definitionMap.set('type', meta.type.value);
|
|
31796
32302
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -31841,7 +32347,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
31841
32347
|
function createPipeDefinitionMap(meta) {
|
|
31842
32348
|
const definitionMap = new DefinitionMap();
|
|
31843
32349
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
31844
|
-
definitionMap.set('version', literal('17.0.0-rc.
|
|
32350
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31845
32351
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31846
32352
|
// e.g. `type: MyPipe`
|
|
31847
32353
|
definitionMap.set('type', meta.type.value);
|