@angular/compiler 16.0.0-next.7 → 16.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/render3/partial/class_metadata.mjs +1 -1
- package/esm2022/src/render3/partial/directive.mjs +1 -1
- package/esm2022/src/render3/partial/factory.mjs +1 -1
- package/esm2022/src/render3/partial/injectable.mjs +1 -1
- package/esm2022/src/render3/partial/injector.mjs +1 -1
- package/esm2022/src/render3/partial/ng_module.mjs +1 -1
- package/esm2022/src/render3/partial/pipe.mjs +1 -1
- package/esm2022/src/render3/view/compiler.mjs +52 -27
- package/esm2022/src/render3/view/util.mjs +4 -2
- package/esm2022/src/template/pipeline/ir/index.mjs +17 -0
- package/esm2022/src/template/pipeline/ir/src/element.mjs +101 -0
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +121 -0
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +293 -0
- package/esm2022/src/template/pipeline/ir/src/operations.mjs +223 -0
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +78 -0
- package/esm2022/src/template/pipeline/ir/src/ops/shared.mjs +43 -0
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +49 -0
- package/esm2022/src/template/pipeline/ir/src/traits.mjs +68 -0
- package/esm2022/src/template/pipeline/ir/src/variable.mjs +9 -0
- package/esm2022/src/template/pipeline/src/compilation.mjs +122 -0
- package/esm2022/src/template/pipeline/src/emit.mjs +83 -0
- package/esm2022/src/template/pipeline/src/ingest.mjs +194 -0
- package/esm2022/src/template/pipeline/src/instruction.mjs +139 -0
- package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +57 -0
- package/esm2022/src/template/pipeline/src/phases/empty_elements.mjs +27 -0
- package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +56 -0
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +146 -0
- package/esm2022/src/template/pipeline/src/phases/local_refs.mjs +44 -0
- package/esm2022/src/template/pipeline/src/phases/naming.mjs +61 -0
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +157 -0
- package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +55 -0
- package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +95 -0
- package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +75 -0
- package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +60 -0
- package/esm2022/src/template/pipeline/switch/index.mjs +2 -0
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +2386 -231
- package/fesm2022/compiler.mjs.map +1 -1
- package/fesm2022/testing.mjs +1 -1
- package/index.d.ts +1 -1
- package/package.json +3 -3
- package/testing/index.d.ts +1 -1
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v16.0.0-
|
|
2
|
+
* @license Angular v16.0.0-rc.1
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -4815,7 +4815,9 @@ function getAttrsForDirectiveMatching(elOrTpl) {
|
|
|
4815
4815
|
}
|
|
4816
4816
|
});
|
|
4817
4817
|
elOrTpl.inputs.forEach(i => {
|
|
4818
|
-
|
|
4818
|
+
if (i.type === 0 /* BindingType.Property */) {
|
|
4819
|
+
attributesMap[i.name] = '';
|
|
4820
|
+
}
|
|
4819
4821
|
});
|
|
4820
4822
|
elOrTpl.outputs.forEach(o => {
|
|
4821
4823
|
attributesMap[o.name] = '';
|
|
@@ -8216,192 +8218,2358 @@ function escapeBlocks(input, charPairs, placeholder) {
|
|
|
8216
8218
|
escapedBlocks.push(input.substring(blockStartIndex));
|
|
8217
8219
|
resultParts.push(placeholder);
|
|
8218
8220
|
}
|
|
8219
|
-
else {
|
|
8220
|
-
resultParts.push(input.substring(nonBlockStartIndex));
|
|
8221
|
+
else {
|
|
8222
|
+
resultParts.push(input.substring(nonBlockStartIndex));
|
|
8223
|
+
}
|
|
8224
|
+
return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
|
|
8225
|
+
}
|
|
8226
|
+
/**
|
|
8227
|
+
* Object containing as keys characters that should be substituted by placeholders
|
|
8228
|
+
* when found in strings during the css text parsing, and as values the respective
|
|
8229
|
+
* placeholders
|
|
8230
|
+
*/
|
|
8231
|
+
const ESCAPE_IN_STRING_MAP = {
|
|
8232
|
+
';': SEMI_IN_PLACEHOLDER,
|
|
8233
|
+
',': COMMA_IN_PLACEHOLDER,
|
|
8234
|
+
':': COLON_IN_PLACEHOLDER
|
|
8235
|
+
};
|
|
8236
|
+
/**
|
|
8237
|
+
* Parse the provided css text and inside strings (meaning, inside pairs of unescaped single or
|
|
8238
|
+
* double quotes) replace specific characters with their respective placeholders as indicated
|
|
8239
|
+
* by the `ESCAPE_IN_STRING_MAP` map.
|
|
8240
|
+
*
|
|
8241
|
+
* For example convert the text
|
|
8242
|
+
* `animation: "my-anim:at\"ion" 1s;`
|
|
8243
|
+
* to
|
|
8244
|
+
* `animation: "my-anim%COLON_IN_PLACEHOLDER%at\"ion" 1s;`
|
|
8245
|
+
*
|
|
8246
|
+
* This is necessary in order to remove the meaning of some characters when found inside strings
|
|
8247
|
+
* (for example `;` indicates the end of a css declaration, `,` the sequence of values and `:` the
|
|
8248
|
+
* division between property and value during a declaration, none of these meanings apply when such
|
|
8249
|
+
* characters are within strings and so in order to prevent parsing issues they need to be replaced
|
|
8250
|
+
* with placeholder text for the duration of the css manipulation process).
|
|
8251
|
+
*
|
|
8252
|
+
* @param input the original css text.
|
|
8253
|
+
*
|
|
8254
|
+
* @returns the css text with specific characters in strings replaced by placeholders.
|
|
8255
|
+
**/
|
|
8256
|
+
function escapeInStrings(input) {
|
|
8257
|
+
let result = input;
|
|
8258
|
+
let currentQuoteChar = null;
|
|
8259
|
+
for (let i = 0; i < result.length; i++) {
|
|
8260
|
+
const char = result[i];
|
|
8261
|
+
if (char === '\\') {
|
|
8262
|
+
i++;
|
|
8263
|
+
}
|
|
8264
|
+
else {
|
|
8265
|
+
if (currentQuoteChar !== null) {
|
|
8266
|
+
// index i is inside a quoted sub-string
|
|
8267
|
+
if (char === currentQuoteChar) {
|
|
8268
|
+
currentQuoteChar = null;
|
|
8269
|
+
}
|
|
8270
|
+
else {
|
|
8271
|
+
const placeholder = ESCAPE_IN_STRING_MAP[char];
|
|
8272
|
+
if (placeholder) {
|
|
8273
|
+
result = `${result.substr(0, i)}${placeholder}${result.substr(i + 1)}`;
|
|
8274
|
+
i += placeholder.length - 1;
|
|
8275
|
+
}
|
|
8276
|
+
}
|
|
8277
|
+
}
|
|
8278
|
+
else if (char === '\'' || char === '"') {
|
|
8279
|
+
currentQuoteChar = char;
|
|
8280
|
+
}
|
|
8281
|
+
}
|
|
8282
|
+
}
|
|
8283
|
+
return result;
|
|
8284
|
+
}
|
|
8285
|
+
/**
|
|
8286
|
+
* Replace in a string all occurrences of keys in the `ESCAPE_IN_STRING_MAP` map with their
|
|
8287
|
+
* original representation, this is simply used to revert the changes applied by the
|
|
8288
|
+
* escapeInStrings function.
|
|
8289
|
+
*
|
|
8290
|
+
* For example it reverts the text:
|
|
8291
|
+
* `animation: "my-anim%COLON_IN_PLACEHOLDER%at\"ion" 1s;`
|
|
8292
|
+
* to it's original form of:
|
|
8293
|
+
* `animation: "my-anim:at\"ion" 1s;`
|
|
8294
|
+
*
|
|
8295
|
+
* Note: For the sake of simplicity this function does not check that the placeholders are
|
|
8296
|
+
* actually inside strings as it would anyway be extremely unlikely to find them outside of strings.
|
|
8297
|
+
*
|
|
8298
|
+
* @param input the css text containing the placeholders.
|
|
8299
|
+
*
|
|
8300
|
+
* @returns the css text without the placeholders.
|
|
8301
|
+
*/
|
|
8302
|
+
function unescapeInStrings(input) {
|
|
8303
|
+
let result = input.replace(_cssCommaInPlaceholderReGlobal, ',');
|
|
8304
|
+
result = result.replace(_cssSemiInPlaceholderReGlobal, ';');
|
|
8305
|
+
result = result.replace(_cssColonInPlaceholderReGlobal, ':');
|
|
8306
|
+
return result;
|
|
8307
|
+
}
|
|
8308
|
+
/**
|
|
8309
|
+
* Unescape all quotes present in a string, but only if the string was actually already
|
|
8310
|
+
* quoted.
|
|
8311
|
+
*
|
|
8312
|
+
* This generates a "canonical" representation of strings which can be used to match strings
|
|
8313
|
+
* which would otherwise only differ because of differently escaped quotes.
|
|
8314
|
+
*
|
|
8315
|
+
* For example it converts the string (assumed to be quoted):
|
|
8316
|
+
* `this \\"is\\" a \\'\\\\'test`
|
|
8317
|
+
* to:
|
|
8318
|
+
* `this "is" a '\\\\'test`
|
|
8319
|
+
* (note that the latter backslashes are not removed as they are not actually escaping the single
|
|
8320
|
+
* quote)
|
|
8321
|
+
*
|
|
8322
|
+
*
|
|
8323
|
+
* @param input the string possibly containing escaped quotes.
|
|
8324
|
+
* @param isQuoted boolean indicating whether the string was quoted inside a bigger string (if not
|
|
8325
|
+
* then it means that it doesn't represent an inner string and thus no unescaping is required)
|
|
8326
|
+
*
|
|
8327
|
+
* @returns the string in the "canonical" representation without escaped quotes.
|
|
8328
|
+
*/
|
|
8329
|
+
function unescapeQuotes(str, isQuoted) {
|
|
8330
|
+
return !isQuoted ? str : str.replace(/((?:^|[^\\])(?:\\\\)*)\\(?=['"])/g, '$1');
|
|
8331
|
+
}
|
|
8332
|
+
/**
|
|
8333
|
+
* Combine the `contextSelectors` with the `hostMarker` and the `otherSelectors`
|
|
8334
|
+
* to create a selector that matches the same as `:host-context()`.
|
|
8335
|
+
*
|
|
8336
|
+
* Given a single context selector `A` we need to output selectors that match on the host and as an
|
|
8337
|
+
* ancestor of the host:
|
|
8338
|
+
*
|
|
8339
|
+
* ```
|
|
8340
|
+
* A <hostMarker>, A<hostMarker> {}
|
|
8341
|
+
* ```
|
|
8342
|
+
*
|
|
8343
|
+
* When there is more than one context selector we also have to create combinations of those
|
|
8344
|
+
* selectors with each other. For example if there are `A` and `B` selectors the output is:
|
|
8345
|
+
*
|
|
8346
|
+
* ```
|
|
8347
|
+
* AB<hostMarker>, AB <hostMarker>, A B<hostMarker>,
|
|
8348
|
+
* B A<hostMarker>, A B <hostMarker>, B A <hostMarker> {}
|
|
8349
|
+
* ```
|
|
8350
|
+
*
|
|
8351
|
+
* And so on...
|
|
8352
|
+
*
|
|
8353
|
+
* @param hostMarker the string that selects the host element.
|
|
8354
|
+
* @param contextSelectors an array of context selectors that will be combined.
|
|
8355
|
+
* @param otherSelectors the rest of the selectors that are not context selectors.
|
|
8356
|
+
*/
|
|
8357
|
+
function combineHostContextSelectors(contextSelectors, otherSelectors) {
|
|
8358
|
+
const hostMarker = _polyfillHostNoCombinator;
|
|
8359
|
+
_polyfillHostRe.lastIndex = 0; // reset the regex to ensure we get an accurate test
|
|
8360
|
+
const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
|
|
8361
|
+
// If there are no context selectors then just output a host marker
|
|
8362
|
+
if (contextSelectors.length === 0) {
|
|
8363
|
+
return hostMarker + otherSelectors;
|
|
8364
|
+
}
|
|
8365
|
+
const combined = [contextSelectors.pop() || ''];
|
|
8366
|
+
while (contextSelectors.length > 0) {
|
|
8367
|
+
const length = combined.length;
|
|
8368
|
+
const contextSelector = contextSelectors.pop();
|
|
8369
|
+
for (let i = 0; i < length; i++) {
|
|
8370
|
+
const previousSelectors = combined[i];
|
|
8371
|
+
// Add the new selector as a descendant of the previous selectors
|
|
8372
|
+
combined[length * 2 + i] = previousSelectors + ' ' + contextSelector;
|
|
8373
|
+
// Add the new selector as an ancestor of the previous selectors
|
|
8374
|
+
combined[length + i] = contextSelector + ' ' + previousSelectors;
|
|
8375
|
+
// Add the new selector to act on the same element as the previous selectors
|
|
8376
|
+
combined[i] = contextSelector + previousSelectors;
|
|
8377
|
+
}
|
|
8378
|
+
}
|
|
8379
|
+
// Finally connect the selector to the `hostMarker`s: either acting directly on the host
|
|
8380
|
+
// (A<hostMarker>) or as an ancestor (A <hostMarker>).
|
|
8381
|
+
return combined
|
|
8382
|
+
.map(s => otherSelectorsHasHost ?
|
|
8383
|
+
`${s}${otherSelectors}` :
|
|
8384
|
+
`${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`)
|
|
8385
|
+
.join(',');
|
|
8386
|
+
}
|
|
8387
|
+
/**
|
|
8388
|
+
* Mutate the given `groups` array so that there are `multiples` clones of the original array
|
|
8389
|
+
* stored.
|
|
8390
|
+
*
|
|
8391
|
+
* For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the
|
|
8392
|
+
* newly added groups will be clones of the original.
|
|
8393
|
+
*
|
|
8394
|
+
* @param groups An array of groups of strings that will be repeated. This array is mutated
|
|
8395
|
+
* in-place.
|
|
8396
|
+
* @param multiples The number of times the current groups should appear.
|
|
8397
|
+
*/
|
|
8398
|
+
function repeatGroups(groups, multiples) {
|
|
8399
|
+
const length = groups.length;
|
|
8400
|
+
for (let i = 1; i < multiples; i++) {
|
|
8401
|
+
for (let j = 0; j < length; j++) {
|
|
8402
|
+
groups[j + (i * length)] = groups[j].slice(0);
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
8405
|
+
}
|
|
8406
|
+
|
|
8407
|
+
var TagContentType;
|
|
8408
|
+
(function (TagContentType) {
|
|
8409
|
+
TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
|
8410
|
+
TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
|
8411
|
+
TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
|
8412
|
+
})(TagContentType || (TagContentType = {}));
|
|
8413
|
+
function splitNsName(elementName) {
|
|
8414
|
+
if (elementName[0] != ':') {
|
|
8415
|
+
return [null, elementName];
|
|
8416
|
+
}
|
|
8417
|
+
const colonIndex = elementName.indexOf(':', 1);
|
|
8418
|
+
if (colonIndex === -1) {
|
|
8419
|
+
throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
|
8420
|
+
}
|
|
8421
|
+
return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
|
8422
|
+
}
|
|
8423
|
+
// `<ng-container>` tags work the same regardless the namespace
|
|
8424
|
+
function isNgContainer(tagName) {
|
|
8425
|
+
return splitNsName(tagName)[1] === 'ng-container';
|
|
8426
|
+
}
|
|
8427
|
+
// `<ng-content>` tags work the same regardless the namespace
|
|
8428
|
+
function isNgContent(tagName) {
|
|
8429
|
+
return splitNsName(tagName)[1] === 'ng-content';
|
|
8430
|
+
}
|
|
8431
|
+
// `<ng-template>` tags work the same regardless the namespace
|
|
8432
|
+
function isNgTemplate(tagName) {
|
|
8433
|
+
return splitNsName(tagName)[1] === 'ng-template';
|
|
8434
|
+
}
|
|
8435
|
+
function getNsPrefix(fullName) {
|
|
8436
|
+
return fullName === null ? null : splitNsName(fullName)[0];
|
|
8437
|
+
}
|
|
8438
|
+
function mergeNsAndName(prefix, localName) {
|
|
8439
|
+
return prefix ? `:${prefix}:${localName}` : localName;
|
|
8440
|
+
}
|
|
8441
|
+
|
|
8442
|
+
/**
|
|
8443
|
+
* Enumeration of the types of attributes which can be applied to an element.
|
|
8444
|
+
*/
|
|
8445
|
+
var ElementAttributeKind;
|
|
8446
|
+
(function (ElementAttributeKind) {
|
|
8447
|
+
/**
|
|
8448
|
+
* Static attributes.
|
|
8449
|
+
*/
|
|
8450
|
+
ElementAttributeKind[ElementAttributeKind["Attribute"] = 0] = "Attribute";
|
|
8451
|
+
/**
|
|
8452
|
+
* Class bindings.
|
|
8453
|
+
*/
|
|
8454
|
+
ElementAttributeKind[ElementAttributeKind["Class"] = 1] = "Class";
|
|
8455
|
+
/**
|
|
8456
|
+
* Style bindings.
|
|
8457
|
+
*/
|
|
8458
|
+
ElementAttributeKind[ElementAttributeKind["Style"] = 2] = "Style";
|
|
8459
|
+
/**
|
|
8460
|
+
* Dynamic property or attribute bindings.
|
|
8461
|
+
*/
|
|
8462
|
+
ElementAttributeKind[ElementAttributeKind["Binding"] = 3] = "Binding";
|
|
8463
|
+
/**
|
|
8464
|
+
* Attributes on a template node.
|
|
8465
|
+
*/
|
|
8466
|
+
ElementAttributeKind[ElementAttributeKind["Template"] = 4] = "Template";
|
|
8467
|
+
/**
|
|
8468
|
+
* Internationalized attributes.
|
|
8469
|
+
*/
|
|
8470
|
+
ElementAttributeKind[ElementAttributeKind["I18n"] = 5] = "I18n";
|
|
8471
|
+
})(ElementAttributeKind || (ElementAttributeKind = {}));
|
|
8472
|
+
const FLYWEIGHT_ARRAY = Object.freeze([]);
|
|
8473
|
+
/**
|
|
8474
|
+
* Container for all of the various kinds of attributes which are applied on an element.
|
|
8475
|
+
*/
|
|
8476
|
+
class ElementAttributes {
|
|
8477
|
+
constructor() {
|
|
8478
|
+
this.known = new Set();
|
|
8479
|
+
this.byKind = new Map;
|
|
8480
|
+
this.projectAs = null;
|
|
8481
|
+
}
|
|
8482
|
+
get attributes() {
|
|
8483
|
+
return this.byKind.get(ElementAttributeKind.Attribute) ?? FLYWEIGHT_ARRAY;
|
|
8484
|
+
}
|
|
8485
|
+
get classes() {
|
|
8486
|
+
return this.byKind.get(ElementAttributeKind.Class) ?? FLYWEIGHT_ARRAY;
|
|
8487
|
+
}
|
|
8488
|
+
get styles() {
|
|
8489
|
+
return this.byKind.get(ElementAttributeKind.Style) ?? FLYWEIGHT_ARRAY;
|
|
8490
|
+
}
|
|
8491
|
+
get bindings() {
|
|
8492
|
+
return this.byKind.get(ElementAttributeKind.Binding) ?? FLYWEIGHT_ARRAY;
|
|
8493
|
+
}
|
|
8494
|
+
get template() {
|
|
8495
|
+
return this.byKind.get(ElementAttributeKind.Template) ?? FLYWEIGHT_ARRAY;
|
|
8496
|
+
}
|
|
8497
|
+
get i18n() {
|
|
8498
|
+
return this.byKind.get(ElementAttributeKind.I18n) ?? FLYWEIGHT_ARRAY;
|
|
8499
|
+
}
|
|
8500
|
+
add(kind, name, value) {
|
|
8501
|
+
if (this.known.has(name)) {
|
|
8502
|
+
return;
|
|
8503
|
+
}
|
|
8504
|
+
this.known.add(name);
|
|
8505
|
+
const array = this.arrayFor(kind);
|
|
8506
|
+
array.push(...getAttributeNameLiterals$1(name));
|
|
8507
|
+
if (value !== null) {
|
|
8508
|
+
array.push(value);
|
|
8509
|
+
}
|
|
8510
|
+
}
|
|
8511
|
+
arrayFor(kind) {
|
|
8512
|
+
if (!this.byKind.has(kind)) {
|
|
8513
|
+
this.byKind.set(kind, []);
|
|
8514
|
+
}
|
|
8515
|
+
return this.byKind.get(kind);
|
|
8516
|
+
}
|
|
8517
|
+
}
|
|
8518
|
+
function getAttributeNameLiterals$1(name) {
|
|
8519
|
+
const [attributeNamespace, attributeName] = splitNsName(name);
|
|
8520
|
+
const nameLiteral = literal(attributeName);
|
|
8521
|
+
if (attributeNamespace) {
|
|
8522
|
+
return [
|
|
8523
|
+
literal(0 /* core.AttributeMarker.NamespaceURI */), literal(attributeNamespace), nameLiteral
|
|
8524
|
+
];
|
|
8525
|
+
}
|
|
8526
|
+
return [nameLiteral];
|
|
8527
|
+
}
|
|
8528
|
+
function assertIsElementAttributes(attrs) {
|
|
8529
|
+
if (!(attrs instanceof ElementAttributes)) {
|
|
8530
|
+
throw new Error(`AssertionError: ElementAttributes has already been coalesced into the view constants`);
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
|
|
8534
|
+
/**
|
|
8535
|
+
* Distinguishes different kinds of IR operations.
|
|
8536
|
+
*
|
|
8537
|
+
* Includes both creation and update operations.
|
|
8538
|
+
*/
|
|
8539
|
+
var OpKind;
|
|
8540
|
+
(function (OpKind) {
|
|
8541
|
+
/**
|
|
8542
|
+
* A special operation type which is used to represent the beginning and end nodes of a linked
|
|
8543
|
+
* list of operations.
|
|
8544
|
+
*/
|
|
8545
|
+
OpKind[OpKind["ListEnd"] = 0] = "ListEnd";
|
|
8546
|
+
/**
|
|
8547
|
+
* An operation which wraps an output AST statement.
|
|
8548
|
+
*/
|
|
8549
|
+
OpKind[OpKind["Statement"] = 1] = "Statement";
|
|
8550
|
+
/**
|
|
8551
|
+
* An operation which declares and initializes a `SemanticVariable`.
|
|
8552
|
+
*/
|
|
8553
|
+
OpKind[OpKind["Variable"] = 2] = "Variable";
|
|
8554
|
+
/**
|
|
8555
|
+
* An operation to begin rendering of an element.
|
|
8556
|
+
*/
|
|
8557
|
+
OpKind[OpKind["ElementStart"] = 3] = "ElementStart";
|
|
8558
|
+
/**
|
|
8559
|
+
* An operation to render an element with no children.
|
|
8560
|
+
*/
|
|
8561
|
+
OpKind[OpKind["Element"] = 4] = "Element";
|
|
8562
|
+
/**
|
|
8563
|
+
* An operation which declares an embedded view.
|
|
8564
|
+
*/
|
|
8565
|
+
OpKind[OpKind["Template"] = 5] = "Template";
|
|
8566
|
+
/**
|
|
8567
|
+
* An operation to end rendering of an element previously started with `ElementStart`.
|
|
8568
|
+
*/
|
|
8569
|
+
OpKind[OpKind["ElementEnd"] = 6] = "ElementEnd";
|
|
8570
|
+
/**
|
|
8571
|
+
* An operation to render a text node.
|
|
8572
|
+
*/
|
|
8573
|
+
OpKind[OpKind["Text"] = 7] = "Text";
|
|
8574
|
+
/**
|
|
8575
|
+
* An operation declaring an event listener for an element.
|
|
8576
|
+
*/
|
|
8577
|
+
OpKind[OpKind["Listener"] = 8] = "Listener";
|
|
8578
|
+
/**
|
|
8579
|
+
* An operation to interpolate text into a text node.
|
|
8580
|
+
*/
|
|
8581
|
+
OpKind[OpKind["InterpolateText"] = 9] = "InterpolateText";
|
|
8582
|
+
/**
|
|
8583
|
+
* An operation to bind an expression to a property of an element.
|
|
8584
|
+
*/
|
|
8585
|
+
OpKind[OpKind["Property"] = 10] = "Property";
|
|
8586
|
+
/**
|
|
8587
|
+
* An operation to advance the runtime's implicit slot context during the update phase of a view.
|
|
8588
|
+
*/
|
|
8589
|
+
OpKind[OpKind["Advance"] = 11] = "Advance";
|
|
8590
|
+
})(OpKind || (OpKind = {}));
|
|
8591
|
+
/**
|
|
8592
|
+
* Distinguishes different kinds of IR expressions.
|
|
8593
|
+
*/
|
|
8594
|
+
var ExpressionKind;
|
|
8595
|
+
(function (ExpressionKind) {
|
|
8596
|
+
/**
|
|
8597
|
+
* Read of a variable in a lexical scope.
|
|
8598
|
+
*/
|
|
8599
|
+
ExpressionKind[ExpressionKind["LexicalRead"] = 0] = "LexicalRead";
|
|
8600
|
+
/**
|
|
8601
|
+
* A reference to the current view context.
|
|
8602
|
+
*/
|
|
8603
|
+
ExpressionKind[ExpressionKind["Context"] = 1] = "Context";
|
|
8604
|
+
/**
|
|
8605
|
+
* Read of a variable declared in a `VariableOp`.
|
|
8606
|
+
*/
|
|
8607
|
+
ExpressionKind[ExpressionKind["ReadVariable"] = 2] = "ReadVariable";
|
|
8608
|
+
/**
|
|
8609
|
+
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
8610
|
+
*/
|
|
8611
|
+
ExpressionKind[ExpressionKind["NextContext"] = 3] = "NextContext";
|
|
8612
|
+
/**
|
|
8613
|
+
* Runtime operation to retrieve the value of a local reference.
|
|
8614
|
+
*/
|
|
8615
|
+
ExpressionKind[ExpressionKind["Reference"] = 4] = "Reference";
|
|
8616
|
+
/**
|
|
8617
|
+
* Runtime operation to snapshot the current view context.
|
|
8618
|
+
*/
|
|
8619
|
+
ExpressionKind[ExpressionKind["GetCurrentView"] = 5] = "GetCurrentView";
|
|
8620
|
+
/**
|
|
8621
|
+
* Runtime operation to restore a snapshotted view.
|
|
8622
|
+
*/
|
|
8623
|
+
ExpressionKind[ExpressionKind["RestoreView"] = 6] = "RestoreView";
|
|
8624
|
+
/**
|
|
8625
|
+
* Runtime operation to reset the current view context after `RestoreView`.
|
|
8626
|
+
*/
|
|
8627
|
+
ExpressionKind[ExpressionKind["ResetView"] = 7] = "ResetView";
|
|
8628
|
+
})(ExpressionKind || (ExpressionKind = {}));
|
|
8629
|
+
/**
|
|
8630
|
+
* Distinguishes between different kinds of `SemanticVariable`s.
|
|
8631
|
+
*/
|
|
8632
|
+
var SemanticVariableKind;
|
|
8633
|
+
(function (SemanticVariableKind) {
|
|
8634
|
+
/**
|
|
8635
|
+
* Represents the context of a particular view.
|
|
8636
|
+
*/
|
|
8637
|
+
SemanticVariableKind[SemanticVariableKind["Context"] = 0] = "Context";
|
|
8638
|
+
/**
|
|
8639
|
+
* Represents an identifier declared in the lexical scope of a view.
|
|
8640
|
+
*/
|
|
8641
|
+
SemanticVariableKind[SemanticVariableKind["Identifier"] = 1] = "Identifier";
|
|
8642
|
+
/**
|
|
8643
|
+
* Represents a saved state that can be used to restore a view in a listener handler function.
|
|
8644
|
+
*/
|
|
8645
|
+
SemanticVariableKind[SemanticVariableKind["SavedView"] = 2] = "SavedView";
|
|
8646
|
+
})(SemanticVariableKind || (SemanticVariableKind = {}));
|
|
8647
|
+
|
|
8648
|
+
/**
|
|
8649
|
+
* Marker symbol for `ConsumesSlotOpTrait`.
|
|
8650
|
+
*/
|
|
8651
|
+
const ConsumesSlot = Symbol('ConsumesSlot');
|
|
8652
|
+
/**
|
|
8653
|
+
* Marker symbol for `DependsOnSlotContextOpTrait`.
|
|
8654
|
+
*/
|
|
8655
|
+
const DependsOnSlotContext = Symbol('DependsOnSlotContext');
|
|
8656
|
+
/**
|
|
8657
|
+
* Marker symbol for `UsesSlotIndex` trait.
|
|
8658
|
+
*/
|
|
8659
|
+
const UsesSlotIndex = Symbol('UsesSlotIndex');
|
|
8660
|
+
/**
|
|
8661
|
+
* Marker symbol for `ConsumesVars` trait.
|
|
8662
|
+
*/
|
|
8663
|
+
const ConsumesVarsTrait = Symbol('UsesVars');
|
|
8664
|
+
/**
|
|
8665
|
+
* Default values for most `ConsumesSlotOpTrait` fields (used with the spread operator to initialize
|
|
8666
|
+
* implementors of the trait).
|
|
8667
|
+
*/
|
|
8668
|
+
const TRAIT_CONSUMES_SLOT = {
|
|
8669
|
+
[ConsumesSlot]: true,
|
|
8670
|
+
slot: null,
|
|
8671
|
+
numSlotsUsed: 1,
|
|
8672
|
+
};
|
|
8673
|
+
/**
|
|
8674
|
+
* Default values for most `DependsOnSlotContextOpTrait` fields (used with the spread operator to
|
|
8675
|
+
* initialize implementors of the trait).
|
|
8676
|
+
*/
|
|
8677
|
+
const TRAIT_DEPENDS_ON_SLOT_CONTEXT = {
|
|
8678
|
+
[DependsOnSlotContext]: true,
|
|
8679
|
+
};
|
|
8680
|
+
/**
|
|
8681
|
+
* Default values for `UsesVars` fields (used with the spread operator to initialize
|
|
8682
|
+
* implementors of the trait).
|
|
8683
|
+
*/
|
|
8684
|
+
const TRAIT_CONSUMES_VARS = {
|
|
8685
|
+
[ConsumesVarsTrait]: true,
|
|
8686
|
+
};
|
|
8687
|
+
/**
|
|
8688
|
+
* Test whether an operation implements `ConsumesSlotOpTrait`.
|
|
8689
|
+
*/
|
|
8690
|
+
function hasConsumesSlotTrait(op) {
|
|
8691
|
+
return op[ConsumesSlot] === true;
|
|
8692
|
+
}
|
|
8693
|
+
/**
|
|
8694
|
+
* Test whether an operation implements `DependsOnSlotContextOpTrait`.
|
|
8695
|
+
*/
|
|
8696
|
+
function hasDependsOnSlotContextTrait(op) {
|
|
8697
|
+
return op[DependsOnSlotContext] === true;
|
|
8698
|
+
}
|
|
8699
|
+
function hasConsumesVarsTrait(value) {
|
|
8700
|
+
return value[ConsumesVarsTrait] === true;
|
|
8701
|
+
}
|
|
8702
|
+
/**
|
|
8703
|
+
* Test whether an expression implements `UsesSlotIndexExprTrait`.
|
|
8704
|
+
*/
|
|
8705
|
+
function hasUsesSlotIndexTrait(expr) {
|
|
8706
|
+
return expr[UsesSlotIndex] === true;
|
|
8707
|
+
}
|
|
8708
|
+
|
|
8709
|
+
var _a;
|
|
8710
|
+
/**
|
|
8711
|
+
* Check whether a given `o.Expression` is a logical IR expression type.
|
|
8712
|
+
*/
|
|
8713
|
+
function isIrExpression(expr) {
|
|
8714
|
+
return expr instanceof ExpressionBase;
|
|
8715
|
+
}
|
|
8716
|
+
/**
|
|
8717
|
+
* Base type used for all logical IR expressions.
|
|
8718
|
+
*/
|
|
8719
|
+
class ExpressionBase extends Expression {
|
|
8720
|
+
constructor(sourceSpan = null) {
|
|
8721
|
+
super(null, sourceSpan);
|
|
8722
|
+
}
|
|
8723
|
+
}
|
|
8724
|
+
/**
|
|
8725
|
+
* Logical expression representing a lexical read of a variable name.
|
|
8726
|
+
*/
|
|
8727
|
+
class LexicalReadExpr extends ExpressionBase {
|
|
8728
|
+
constructor(name) {
|
|
8729
|
+
super();
|
|
8730
|
+
this.name = name;
|
|
8731
|
+
this.kind = ExpressionKind.LexicalRead;
|
|
8732
|
+
}
|
|
8733
|
+
visitExpression(visitor, context) { }
|
|
8734
|
+
isEquivalent() {
|
|
8735
|
+
return false;
|
|
8736
|
+
}
|
|
8737
|
+
isConstant() {
|
|
8738
|
+
return false;
|
|
8739
|
+
}
|
|
8740
|
+
transformInternalExpressions() { }
|
|
8741
|
+
}
|
|
8742
|
+
/**
|
|
8743
|
+
* Runtime operation to retrieve the value of a local reference.
|
|
8744
|
+
*/
|
|
8745
|
+
class ReferenceExpr extends ExpressionBase {
|
|
8746
|
+
static { _a = UsesSlotIndex; }
|
|
8747
|
+
constructor(target, offset) {
|
|
8748
|
+
super();
|
|
8749
|
+
this.target = target;
|
|
8750
|
+
this.offset = offset;
|
|
8751
|
+
this.kind = ExpressionKind.Reference;
|
|
8752
|
+
this[_a] = true;
|
|
8753
|
+
this.slot = null;
|
|
8754
|
+
}
|
|
8755
|
+
visitExpression() { }
|
|
8756
|
+
isEquivalent(e) {
|
|
8757
|
+
return e instanceof ReferenceExpr && e.target === this.target;
|
|
8758
|
+
}
|
|
8759
|
+
isConstant() {
|
|
8760
|
+
return false;
|
|
8761
|
+
}
|
|
8762
|
+
transformInternalExpressions() { }
|
|
8763
|
+
}
|
|
8764
|
+
/**
|
|
8765
|
+
* A reference to the current view context (usually the `ctx` variable in a template function).
|
|
8766
|
+
*/
|
|
8767
|
+
class ContextExpr extends ExpressionBase {
|
|
8768
|
+
constructor(view) {
|
|
8769
|
+
super();
|
|
8770
|
+
this.view = view;
|
|
8771
|
+
this.kind = ExpressionKind.Context;
|
|
8772
|
+
}
|
|
8773
|
+
visitExpression() { }
|
|
8774
|
+
isEquivalent(e) {
|
|
8775
|
+
return e instanceof ContextExpr && e.view === this.view;
|
|
8776
|
+
}
|
|
8777
|
+
isConstant() {
|
|
8778
|
+
return false;
|
|
8779
|
+
}
|
|
8780
|
+
transformInternalExpressions() { }
|
|
8781
|
+
}
|
|
8782
|
+
/**
|
|
8783
|
+
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
8784
|
+
*/
|
|
8785
|
+
class NextContextExpr extends ExpressionBase {
|
|
8786
|
+
constructor() {
|
|
8787
|
+
super();
|
|
8788
|
+
this.kind = ExpressionKind.NextContext;
|
|
8789
|
+
}
|
|
8790
|
+
visitExpression() { }
|
|
8791
|
+
isEquivalent(e) {
|
|
8792
|
+
return e instanceof NextContextExpr;
|
|
8793
|
+
}
|
|
8794
|
+
isConstant() {
|
|
8795
|
+
return false;
|
|
8796
|
+
}
|
|
8797
|
+
transformInternalExpressions() { }
|
|
8798
|
+
}
|
|
8799
|
+
/**
|
|
8800
|
+
* Runtime operation to snapshot the current view context.
|
|
8801
|
+
*
|
|
8802
|
+
* The result of this operation can be stored in a variable and later used with the `RestoreView`
|
|
8803
|
+
* operation.
|
|
8804
|
+
*/
|
|
8805
|
+
class GetCurrentViewExpr extends ExpressionBase {
|
|
8806
|
+
constructor() {
|
|
8807
|
+
super();
|
|
8808
|
+
this.kind = ExpressionKind.GetCurrentView;
|
|
8809
|
+
}
|
|
8810
|
+
visitExpression() { }
|
|
8811
|
+
isEquivalent(e) {
|
|
8812
|
+
return e instanceof GetCurrentViewExpr;
|
|
8813
|
+
}
|
|
8814
|
+
isConstant() {
|
|
8815
|
+
return false;
|
|
8816
|
+
}
|
|
8817
|
+
transformInternalExpressions() { }
|
|
8818
|
+
}
|
|
8819
|
+
/**
|
|
8820
|
+
* Runtime operation to restore a snapshotted view.
|
|
8821
|
+
*/
|
|
8822
|
+
class RestoreViewExpr extends ExpressionBase {
|
|
8823
|
+
constructor(view) {
|
|
8824
|
+
super();
|
|
8825
|
+
this.view = view;
|
|
8826
|
+
this.kind = ExpressionKind.RestoreView;
|
|
8827
|
+
}
|
|
8828
|
+
visitExpression(visitor, context) {
|
|
8829
|
+
if (typeof this.view !== 'number') {
|
|
8830
|
+
this.view.visitExpression(visitor, context);
|
|
8831
|
+
}
|
|
8832
|
+
}
|
|
8833
|
+
isEquivalent(e) {
|
|
8834
|
+
if (!(e instanceof RestoreViewExpr) || typeof e.view !== typeof this.view) {
|
|
8835
|
+
return false;
|
|
8836
|
+
}
|
|
8837
|
+
if (typeof this.view === 'number') {
|
|
8838
|
+
return this.view === e.view;
|
|
8839
|
+
}
|
|
8840
|
+
else {
|
|
8841
|
+
return this.view.isEquivalent(e.view);
|
|
8842
|
+
}
|
|
8843
|
+
}
|
|
8844
|
+
isConstant() {
|
|
8845
|
+
return false;
|
|
8846
|
+
}
|
|
8847
|
+
transformInternalExpressions(transform) {
|
|
8848
|
+
if (typeof this.view !== 'number') {
|
|
8849
|
+
this.view = transformExpressionsInExpression(this.view, transform);
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8852
|
+
}
|
|
8853
|
+
/**
|
|
8854
|
+
* Runtime operation to reset the current view context after `RestoreView`.
|
|
8855
|
+
*/
|
|
8856
|
+
class ResetViewExpr extends ExpressionBase {
|
|
8857
|
+
constructor(expr) {
|
|
8858
|
+
super();
|
|
8859
|
+
this.expr = expr;
|
|
8860
|
+
this.kind = ExpressionKind.ResetView;
|
|
8861
|
+
}
|
|
8862
|
+
visitExpression(visitor, context) {
|
|
8863
|
+
this.expr.visitExpression(visitor, context);
|
|
8864
|
+
}
|
|
8865
|
+
isEquivalent(e) {
|
|
8866
|
+
return e instanceof ResetViewExpr && this.expr.isEquivalent(e.expr);
|
|
8867
|
+
}
|
|
8868
|
+
isConstant() {
|
|
8869
|
+
return false;
|
|
8870
|
+
}
|
|
8871
|
+
transformInternalExpressions(transform) {
|
|
8872
|
+
this.expr = transformExpressionsInExpression(this.expr, transform);
|
|
8873
|
+
}
|
|
8874
|
+
}
|
|
8875
|
+
/**
|
|
8876
|
+
* Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
|
|
8877
|
+
*/
|
|
8878
|
+
class ReadVariableExpr extends ExpressionBase {
|
|
8879
|
+
constructor(xref) {
|
|
8880
|
+
super();
|
|
8881
|
+
this.xref = xref;
|
|
8882
|
+
this.kind = ExpressionKind.ReadVariable;
|
|
8883
|
+
this.name = null;
|
|
8884
|
+
}
|
|
8885
|
+
visitExpression() { }
|
|
8886
|
+
isEquivalent(other) {
|
|
8887
|
+
return other instanceof ReadVariableExpr && other.xref === this.xref;
|
|
8888
|
+
}
|
|
8889
|
+
isConstant() {
|
|
8890
|
+
return false;
|
|
8891
|
+
}
|
|
8892
|
+
transformInternalExpressions() { }
|
|
8893
|
+
}
|
|
8894
|
+
/**
|
|
8895
|
+
* Visits all `Expression`s in the AST of `op` with the `visitor` function.
|
|
8896
|
+
*/
|
|
8897
|
+
function visitExpressionsInOp(op, visitor) {
|
|
8898
|
+
transformExpressionsInOp(op, (expr) => {
|
|
8899
|
+
visitor(expr);
|
|
8900
|
+
return expr;
|
|
8901
|
+
});
|
|
8902
|
+
}
|
|
8903
|
+
/**
|
|
8904
|
+
* Transform all `Expression`s in the AST of `op` with the `transform` function.
|
|
8905
|
+
*
|
|
8906
|
+
* All such operations will be replaced with the result of applying `transform`, which may be an
|
|
8907
|
+
* identity transformation.
|
|
8908
|
+
*/
|
|
8909
|
+
function transformExpressionsInOp(op, transform) {
|
|
8910
|
+
switch (op.kind) {
|
|
8911
|
+
case OpKind.Property:
|
|
8912
|
+
op.expression = transformExpressionsInExpression(op.expression, transform);
|
|
8913
|
+
break;
|
|
8914
|
+
case OpKind.Statement:
|
|
8915
|
+
transformExpressionsInStatement(op.statement, transform);
|
|
8916
|
+
break;
|
|
8917
|
+
case OpKind.Variable:
|
|
8918
|
+
op.initializer = transformExpressionsInExpression(op.initializer, transform);
|
|
8919
|
+
break;
|
|
8920
|
+
case OpKind.InterpolateText:
|
|
8921
|
+
for (let i = 0; i < op.expressions.length; i++) {
|
|
8922
|
+
op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform);
|
|
8923
|
+
}
|
|
8924
|
+
break;
|
|
8925
|
+
case OpKind.Listener:
|
|
8926
|
+
for (const innerOp of op.handlerOps) {
|
|
8927
|
+
transformExpressionsInOp(innerOp, transform);
|
|
8928
|
+
}
|
|
8929
|
+
break;
|
|
8930
|
+
case OpKind.Element:
|
|
8931
|
+
case OpKind.ElementStart:
|
|
8932
|
+
case OpKind.ElementEnd:
|
|
8933
|
+
case OpKind.Template:
|
|
8934
|
+
case OpKind.Text:
|
|
8935
|
+
// These operations contain no expressions.
|
|
8936
|
+
break;
|
|
8937
|
+
default:
|
|
8938
|
+
throw new Error(`AssertionError: transformExpressionsInOp doesn't handle ${OpKind[op.kind]}`);
|
|
8939
|
+
}
|
|
8940
|
+
}
|
|
8941
|
+
/**
|
|
8942
|
+
* Transform all `Expression`s in the AST of `expr` with the `transform` function.
|
|
8943
|
+
*
|
|
8944
|
+
* All such operations will be replaced with the result of applying `transform`, which may be an
|
|
8945
|
+
* identity transformation.
|
|
8946
|
+
*/
|
|
8947
|
+
function transformExpressionsInExpression(expr, transform) {
|
|
8948
|
+
if (expr instanceof ExpressionBase) {
|
|
8949
|
+
expr.transformInternalExpressions(transform);
|
|
8950
|
+
return transform(expr);
|
|
8951
|
+
}
|
|
8952
|
+
else if (expr instanceof BinaryOperatorExpr) {
|
|
8953
|
+
expr.lhs = transformExpressionsInExpression(expr.lhs, transform);
|
|
8954
|
+
expr.rhs = transformExpressionsInExpression(expr.rhs, transform);
|
|
8955
|
+
}
|
|
8956
|
+
else if (expr instanceof ReadPropExpr) {
|
|
8957
|
+
expr.receiver = transformExpressionsInExpression(expr.receiver, transform);
|
|
8958
|
+
}
|
|
8959
|
+
else if (expr instanceof InvokeFunctionExpr) {
|
|
8960
|
+
expr.fn = transformExpressionsInExpression(expr.fn, transform);
|
|
8961
|
+
for (let i = 0; i < expr.args.length; i++) {
|
|
8962
|
+
expr.args[i] = transformExpressionsInExpression(expr.args[i], transform);
|
|
8963
|
+
}
|
|
8964
|
+
}
|
|
8965
|
+
else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
|
|
8966
|
+
expr instanceof LiteralExpr) {
|
|
8967
|
+
// No action for these types.
|
|
8968
|
+
}
|
|
8969
|
+
else {
|
|
8970
|
+
throw new Error(`Unhandled expression kind: ${expr.constructor.name}`);
|
|
8971
|
+
}
|
|
8972
|
+
return expr;
|
|
8973
|
+
}
|
|
8974
|
+
/**
|
|
8975
|
+
* Transform all `Expression`s in the AST of `stmt` with the `transform` function.
|
|
8976
|
+
*
|
|
8977
|
+
* All such operations will be replaced with the result of applying `transform`, which may be an
|
|
8978
|
+
* identity transformation.
|
|
8979
|
+
*/
|
|
8980
|
+
function transformExpressionsInStatement(stmt, transform) {
|
|
8981
|
+
if (stmt instanceof ExpressionStatement) {
|
|
8982
|
+
stmt.expr = transformExpressionsInExpression(stmt.expr, transform);
|
|
8983
|
+
}
|
|
8984
|
+
else if (stmt instanceof ReturnStatement) {
|
|
8985
|
+
stmt.value = transformExpressionsInExpression(stmt.value, transform);
|
|
8986
|
+
}
|
|
8987
|
+
else {
|
|
8988
|
+
throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`);
|
|
8989
|
+
}
|
|
8990
|
+
}
|
|
8991
|
+
|
|
8992
|
+
/**
|
|
8993
|
+
* A linked list of `Op` nodes of a given subtype.
|
|
8994
|
+
*
|
|
8995
|
+
* @param OpT specific subtype of `Op` nodes which this list contains.
|
|
8996
|
+
*/
|
|
8997
|
+
class OpList {
|
|
8998
|
+
static { this.nextListId = 0; }
|
|
8999
|
+
constructor() {
|
|
9000
|
+
/**
|
|
9001
|
+
* Debug ID of this `OpList` instance.
|
|
9002
|
+
*/
|
|
9003
|
+
this.debugListId = OpList.nextListId++;
|
|
9004
|
+
// OpList uses static head/tail nodes of a special `ListEnd` type.
|
|
9005
|
+
// This avoids the need for special casing of the first and last list
|
|
9006
|
+
// elements in all list operations.
|
|
9007
|
+
this.head = {
|
|
9008
|
+
kind: OpKind.ListEnd,
|
|
9009
|
+
next: null,
|
|
9010
|
+
prev: null,
|
|
9011
|
+
debugListId: this.debugListId,
|
|
9012
|
+
};
|
|
9013
|
+
this.tail = {
|
|
9014
|
+
kind: OpKind.ListEnd,
|
|
9015
|
+
next: null,
|
|
9016
|
+
prev: null,
|
|
9017
|
+
debugListId: this.debugListId,
|
|
9018
|
+
};
|
|
9019
|
+
// Link `head` and `tail` together at the start (list is empty).
|
|
9020
|
+
this.head.next = this.tail;
|
|
9021
|
+
this.tail.prev = this.head;
|
|
9022
|
+
}
|
|
9023
|
+
/**
|
|
9024
|
+
* Push a new operation to the tail of the list.
|
|
9025
|
+
*/
|
|
9026
|
+
push(op) {
|
|
9027
|
+
OpList.assertIsNotEnd(op);
|
|
9028
|
+
OpList.assertIsUnowned(op);
|
|
9029
|
+
op.debugListId = this.debugListId;
|
|
9030
|
+
// The old "previous" node (which might be the head, if the list is empty).
|
|
9031
|
+
const oldLast = this.tail.prev;
|
|
9032
|
+
// Insert `op` following the old last node.
|
|
9033
|
+
op.prev = oldLast;
|
|
9034
|
+
oldLast.next = op;
|
|
9035
|
+
// Connect `op` with the list tail.
|
|
9036
|
+
op.next = this.tail;
|
|
9037
|
+
this.tail.prev = op;
|
|
9038
|
+
}
|
|
9039
|
+
/**
|
|
9040
|
+
* Prepend one or more nodes to the start of the list.
|
|
9041
|
+
*/
|
|
9042
|
+
prepend(ops) {
|
|
9043
|
+
if (ops.length === 0) {
|
|
9044
|
+
return;
|
|
9045
|
+
}
|
|
9046
|
+
for (const op of ops) {
|
|
9047
|
+
OpList.assertIsNotEnd(op);
|
|
9048
|
+
OpList.assertIsUnowned(op);
|
|
9049
|
+
op.debugListId = this.debugListId;
|
|
9050
|
+
}
|
|
9051
|
+
const first = this.head.next;
|
|
9052
|
+
let prev = this.head;
|
|
9053
|
+
for (const op of ops) {
|
|
9054
|
+
prev.next = op;
|
|
9055
|
+
op.prev = prev;
|
|
9056
|
+
prev = op;
|
|
9057
|
+
}
|
|
9058
|
+
prev.next = first;
|
|
9059
|
+
first.prev = prev;
|
|
9060
|
+
}
|
|
9061
|
+
/**
|
|
9062
|
+
* `OpList` is iterable via the iteration protocol.
|
|
9063
|
+
*
|
|
9064
|
+
* It's safe to mutate the part of the list that has already been returned by the iterator, up to
|
|
9065
|
+
* and including the last operation returned. Mutations beyond that point _may_ be safe, but may
|
|
9066
|
+
* also corrupt the iteration position and should be avoided.
|
|
9067
|
+
*/
|
|
9068
|
+
*[Symbol.iterator]() {
|
|
9069
|
+
let current = this.head.next;
|
|
9070
|
+
while (current !== this.tail) {
|
|
9071
|
+
// Guards against corruption of the iterator state by mutations to the tail of the list during
|
|
9072
|
+
// iteration.
|
|
9073
|
+
OpList.assertIsOwned(current);
|
|
9074
|
+
const next = current.next;
|
|
9075
|
+
yield current;
|
|
9076
|
+
current = next;
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
/**
|
|
9080
|
+
* Replace `oldOp` with `newOp` in the list.
|
|
9081
|
+
*/
|
|
9082
|
+
static replace(oldOp, newOp) {
|
|
9083
|
+
OpList.assertIsNotEnd(oldOp);
|
|
9084
|
+
OpList.assertIsNotEnd(newOp);
|
|
9085
|
+
OpList.assertIsOwned(oldOp);
|
|
9086
|
+
OpList.assertIsUnowned(newOp);
|
|
9087
|
+
newOp.debugListId = oldOp.debugListId;
|
|
9088
|
+
if (oldOp.prev !== null) {
|
|
9089
|
+
oldOp.prev.next = newOp;
|
|
9090
|
+
newOp.prev = oldOp.prev;
|
|
9091
|
+
}
|
|
9092
|
+
if (oldOp.next !== null) {
|
|
9093
|
+
oldOp.next.prev = newOp;
|
|
9094
|
+
newOp.next = oldOp.next;
|
|
9095
|
+
}
|
|
9096
|
+
oldOp.debugListId = null;
|
|
9097
|
+
oldOp.prev = null;
|
|
9098
|
+
oldOp.next = null;
|
|
9099
|
+
}
|
|
9100
|
+
/**
|
|
9101
|
+
* Replace `oldOp` with some number of new operations in the list (which may include `oldOp`).
|
|
9102
|
+
*/
|
|
9103
|
+
static replaceWithMany(oldOp, newOps) {
|
|
9104
|
+
if (newOps.length === 0) {
|
|
9105
|
+
// Replacing with an empty list -> pure removal.
|
|
9106
|
+
OpList.remove(oldOp);
|
|
9107
|
+
return;
|
|
9108
|
+
}
|
|
9109
|
+
OpList.assertIsNotEnd(oldOp);
|
|
9110
|
+
OpList.assertIsOwned(oldOp);
|
|
9111
|
+
const listId = oldOp.debugListId;
|
|
9112
|
+
oldOp.debugListId = null;
|
|
9113
|
+
for (const newOp of newOps) {
|
|
9114
|
+
OpList.assertIsNotEnd(newOp);
|
|
9115
|
+
// `newOp` might be `oldOp`, but at this point it's been marked as unowned.
|
|
9116
|
+
OpList.assertIsUnowned(newOp);
|
|
9117
|
+
}
|
|
9118
|
+
// It should be safe to reuse `oldOp` in the `newOps` list - maybe you want to sandwich an
|
|
9119
|
+
// operation between two new ops.
|
|
9120
|
+
const { prev: oldPrev, next: oldNext } = oldOp;
|
|
9121
|
+
oldOp.prev = null;
|
|
9122
|
+
oldOp.next = null;
|
|
9123
|
+
let prev = oldPrev;
|
|
9124
|
+
for (const newOp of newOps) {
|
|
9125
|
+
this.assertIsUnowned(newOp);
|
|
9126
|
+
newOp.debugListId = listId;
|
|
9127
|
+
prev.next = newOp;
|
|
9128
|
+
newOp.prev = prev;
|
|
9129
|
+
// This _should_ be the case, but set it just in case.
|
|
9130
|
+
newOp.next = null;
|
|
9131
|
+
prev = newOp;
|
|
9132
|
+
}
|
|
9133
|
+
// At the end of iteration, `prev` holds the last node in the list.
|
|
9134
|
+
const first = newOps[0];
|
|
9135
|
+
const last = prev;
|
|
9136
|
+
// Replace `oldOp` with the chain `first` -> `last`.
|
|
9137
|
+
if (oldPrev !== null) {
|
|
9138
|
+
oldPrev.next = first;
|
|
9139
|
+
first.prev = oldOp.prev;
|
|
9140
|
+
}
|
|
9141
|
+
if (oldNext !== null) {
|
|
9142
|
+
oldNext.prev = last;
|
|
9143
|
+
last.next = oldNext;
|
|
9144
|
+
}
|
|
9145
|
+
}
|
|
9146
|
+
/**
|
|
9147
|
+
* Remove the given node from the list which contains it.
|
|
9148
|
+
*/
|
|
9149
|
+
static remove(op) {
|
|
9150
|
+
OpList.assertIsNotEnd(op);
|
|
9151
|
+
OpList.assertIsOwned(op);
|
|
9152
|
+
op.prev.next = op.next;
|
|
9153
|
+
op.next.prev = op.prev;
|
|
9154
|
+
// Break any link between the node and this list to safeguard against its usage in future
|
|
9155
|
+
// operations.
|
|
9156
|
+
op.debugListId = null;
|
|
9157
|
+
op.prev = null;
|
|
9158
|
+
op.next = null;
|
|
9159
|
+
}
|
|
9160
|
+
/**
|
|
9161
|
+
* Insert `op` before `before`.
|
|
9162
|
+
*/
|
|
9163
|
+
static insertBefore(op, before) {
|
|
9164
|
+
OpList.assertIsNotEnd(before);
|
|
9165
|
+
OpList.assertIsNotEnd(op);
|
|
9166
|
+
OpList.assertIsUnowned(op);
|
|
9167
|
+
OpList.assertIsOwned(before, op.debugListId);
|
|
9168
|
+
op.debugListId = before.debugListId;
|
|
9169
|
+
// Just in case.
|
|
9170
|
+
op.prev = null;
|
|
9171
|
+
before.prev.next = op;
|
|
9172
|
+
op.prev = before.prev;
|
|
9173
|
+
op.next = before;
|
|
9174
|
+
before.prev = op;
|
|
9175
|
+
}
|
|
9176
|
+
/**
|
|
9177
|
+
* Asserts that `op` does not currently belong to a list.
|
|
9178
|
+
*/
|
|
9179
|
+
static assertIsUnowned(op) {
|
|
9180
|
+
if (op.debugListId !== null) {
|
|
9181
|
+
throw new Error(`AssertionError: illegal operation on owned node: ${OpKind[op.kind]}`);
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9184
|
+
/**
|
|
9185
|
+
* Asserts that `op` currently belongs to a list. If `byList` is passed, `op` is asserted to
|
|
9186
|
+
* specifically belong to that list.
|
|
9187
|
+
*/
|
|
9188
|
+
static assertIsOwned(op, byList) {
|
|
9189
|
+
if (op.debugListId === null) {
|
|
9190
|
+
throw new Error(`AssertionError: illegal operation on unowned node: ${OpKind[op.kind]}`);
|
|
9191
|
+
}
|
|
9192
|
+
else if (byList !== undefined && op.debugListId !== byList) {
|
|
9193
|
+
throw new Error(`AssertionError: node belongs to the wrong list (expected ${byList}, actual ${op.debugListId})`);
|
|
9194
|
+
}
|
|
9195
|
+
}
|
|
9196
|
+
/**
|
|
9197
|
+
* Asserts that `op` is not a special `ListEnd` node.
|
|
9198
|
+
*/
|
|
9199
|
+
static assertIsNotEnd(op) {
|
|
9200
|
+
if (op.kind === OpKind.ListEnd) {
|
|
9201
|
+
throw new Error(`AssertionError: illegal operation on list head or tail`);
|
|
9202
|
+
}
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
|
|
9206
|
+
/**
|
|
9207
|
+
* Create a `StatementOp`.
|
|
9208
|
+
*/
|
|
9209
|
+
function createStatementOp(statement) {
|
|
9210
|
+
return {
|
|
9211
|
+
kind: OpKind.Statement,
|
|
9212
|
+
statement,
|
|
9213
|
+
...NEW_OP,
|
|
9214
|
+
};
|
|
9215
|
+
}
|
|
9216
|
+
/**
|
|
9217
|
+
* Create a `VariableOp`.
|
|
9218
|
+
*/
|
|
9219
|
+
function createVariableOp(xref, variable, initializer) {
|
|
9220
|
+
return {
|
|
9221
|
+
kind: OpKind.Variable,
|
|
9222
|
+
xref,
|
|
9223
|
+
name: null,
|
|
9224
|
+
variable,
|
|
9225
|
+
initializer,
|
|
9226
|
+
...NEW_OP,
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
/**
|
|
9230
|
+
* Static structure shared by all operations.
|
|
9231
|
+
*
|
|
9232
|
+
* Used as a convenience via the spread operator (`...NEW_OP`) when creating new operations, and
|
|
9233
|
+
* ensures the fields are always in the same order.
|
|
9234
|
+
*/
|
|
9235
|
+
const NEW_OP = {
|
|
9236
|
+
debugListId: null,
|
|
9237
|
+
prev: null,
|
|
9238
|
+
next: null,
|
|
9239
|
+
};
|
|
9240
|
+
|
|
9241
|
+
/**
|
|
9242
|
+
* Create an `ElementStartOp`.
|
|
9243
|
+
*/
|
|
9244
|
+
function createElementStartOp(tag, xref) {
|
|
9245
|
+
return {
|
|
9246
|
+
kind: OpKind.ElementStart,
|
|
9247
|
+
xref,
|
|
9248
|
+
tag,
|
|
9249
|
+
attributes: new ElementAttributes(),
|
|
9250
|
+
localRefs: [],
|
|
9251
|
+
...TRAIT_CONSUMES_SLOT,
|
|
9252
|
+
...NEW_OP,
|
|
9253
|
+
};
|
|
9254
|
+
}
|
|
9255
|
+
/**
|
|
9256
|
+
* Create a `TemplateOp`.
|
|
9257
|
+
*/
|
|
9258
|
+
function createTemplateOp(xref, tag) {
|
|
9259
|
+
return {
|
|
9260
|
+
kind: OpKind.Template,
|
|
9261
|
+
xref,
|
|
9262
|
+
attributes: new ElementAttributes(),
|
|
9263
|
+
tag,
|
|
9264
|
+
decls: null,
|
|
9265
|
+
vars: null,
|
|
9266
|
+
localRefs: [],
|
|
9267
|
+
...TRAIT_CONSUMES_SLOT,
|
|
9268
|
+
...NEW_OP,
|
|
9269
|
+
};
|
|
9270
|
+
}
|
|
9271
|
+
/**
|
|
9272
|
+
* Create an `ElementEndOp`.
|
|
9273
|
+
*/
|
|
9274
|
+
function createElementEndOp(xref) {
|
|
9275
|
+
return {
|
|
9276
|
+
kind: OpKind.ElementEnd,
|
|
9277
|
+
xref,
|
|
9278
|
+
...NEW_OP,
|
|
9279
|
+
};
|
|
9280
|
+
}
|
|
9281
|
+
/**
|
|
9282
|
+
* Create a `TextOp`.
|
|
9283
|
+
*/
|
|
9284
|
+
function createTextOp(xref, initialValue) {
|
|
9285
|
+
return {
|
|
9286
|
+
kind: OpKind.Text,
|
|
9287
|
+
xref,
|
|
9288
|
+
initialValue,
|
|
9289
|
+
...TRAIT_CONSUMES_SLOT,
|
|
9290
|
+
...NEW_OP,
|
|
9291
|
+
};
|
|
9292
|
+
}
|
|
9293
|
+
/**
|
|
9294
|
+
* Create a `ListenerOp`.
|
|
9295
|
+
*/
|
|
9296
|
+
function createListenerOp(xref, name) {
|
|
9297
|
+
return {
|
|
9298
|
+
kind: OpKind.Listener,
|
|
9299
|
+
xref,
|
|
9300
|
+
name,
|
|
9301
|
+
handlerOps: new OpList(),
|
|
9302
|
+
handlerFnName: null,
|
|
9303
|
+
...NEW_OP,
|
|
9304
|
+
};
|
|
9305
|
+
}
|
|
9306
|
+
|
|
9307
|
+
/**
|
|
9308
|
+
* Create an `InterpolationTextOp`.
|
|
9309
|
+
*/
|
|
9310
|
+
function createInterpolateTextOp(xref, strings, expressions) {
|
|
9311
|
+
return {
|
|
9312
|
+
kind: OpKind.InterpolateText,
|
|
9313
|
+
target: xref,
|
|
9314
|
+
strings,
|
|
9315
|
+
expressions,
|
|
9316
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9317
|
+
...TRAIT_CONSUMES_VARS,
|
|
9318
|
+
...NEW_OP,
|
|
9319
|
+
};
|
|
9320
|
+
}
|
|
9321
|
+
/**
|
|
9322
|
+
* Create a `PropertyOp`.
|
|
9323
|
+
*/
|
|
9324
|
+
function createPropertyOp(xref, name, expression) {
|
|
9325
|
+
return {
|
|
9326
|
+
kind: OpKind.Property,
|
|
9327
|
+
target: xref,
|
|
9328
|
+
name,
|
|
9329
|
+
expression,
|
|
9330
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9331
|
+
...TRAIT_CONSUMES_VARS,
|
|
9332
|
+
...NEW_OP,
|
|
9333
|
+
};
|
|
9334
|
+
}
|
|
9335
|
+
/**
|
|
9336
|
+
* Create an `AdvanceOp`.
|
|
9337
|
+
*/
|
|
9338
|
+
function createAdvanceOp(delta) {
|
|
9339
|
+
return {
|
|
9340
|
+
kind: OpKind.Advance,
|
|
9341
|
+
delta,
|
|
9342
|
+
...NEW_OP,
|
|
9343
|
+
};
|
|
9344
|
+
}
|
|
9345
|
+
|
|
9346
|
+
/**
|
|
9347
|
+
* Converts the semantic attributes of element-like operations (elements, templates) into constant
|
|
9348
|
+
* array expressions, and lifts them into the overall component `consts`.
|
|
9349
|
+
*/
|
|
9350
|
+
function phaseConstCollection(cpl) {
|
|
9351
|
+
for (const [_, view] of cpl.views) {
|
|
9352
|
+
for (const op of view.create) {
|
|
9353
|
+
if (op.kind !== OpKind.ElementStart && op.kind !== OpKind.Element &&
|
|
9354
|
+
op.kind !== OpKind.Template) {
|
|
9355
|
+
continue;
|
|
9356
|
+
}
|
|
9357
|
+
else if (!(op.attributes instanceof ElementAttributes)) {
|
|
9358
|
+
continue;
|
|
9359
|
+
}
|
|
9360
|
+
const attrArray = serializeAttributes(op.attributes);
|
|
9361
|
+
if (attrArray.entries.length > 0) {
|
|
9362
|
+
op.attributes = cpl.addConst(attrArray);
|
|
9363
|
+
}
|
|
9364
|
+
else {
|
|
9365
|
+
op.attributes = null;
|
|
9366
|
+
}
|
|
9367
|
+
}
|
|
9368
|
+
}
|
|
9369
|
+
}
|
|
9370
|
+
function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, styles, template }) {
|
|
9371
|
+
const attrArray = [...attributes];
|
|
9372
|
+
if (projectAs !== null) {
|
|
9373
|
+
attrArray.push(literal(5 /* core.AttributeMarker.ProjectAs */), literal(projectAs));
|
|
9374
|
+
}
|
|
9375
|
+
if (classes.length > 0) {
|
|
9376
|
+
attrArray.push(literal(1 /* core.AttributeMarker.Classes */), ...classes);
|
|
9377
|
+
}
|
|
9378
|
+
if (styles.length > 0) {
|
|
9379
|
+
attrArray.push(literal(2 /* core.AttributeMarker.Styles */), ...styles);
|
|
9380
|
+
}
|
|
9381
|
+
if (bindings.length > 0) {
|
|
9382
|
+
attrArray.push(literal(3 /* core.AttributeMarker.Bindings */), ...bindings);
|
|
9383
|
+
}
|
|
9384
|
+
if (template.length > 0) {
|
|
9385
|
+
attrArray.push(literal(4 /* core.AttributeMarker.Template */), ...template);
|
|
9386
|
+
}
|
|
9387
|
+
if (i18n.length > 0) {
|
|
9388
|
+
attrArray.push(literal(6 /* core.AttributeMarker.I18n */), ...i18n);
|
|
9389
|
+
}
|
|
9390
|
+
return literalArr(attrArray);
|
|
9391
|
+
}
|
|
9392
|
+
|
|
9393
|
+
/**
|
|
9394
|
+
* Replace sequences of `ElementStart` followed by `ElementEnd` with a condensed `Element`
|
|
9395
|
+
* instruction.
|
|
9396
|
+
*/
|
|
9397
|
+
function phaseEmptyElements(cpl) {
|
|
9398
|
+
for (const [_, view] of cpl.views) {
|
|
9399
|
+
for (const op of view.create) {
|
|
9400
|
+
if (op.kind === OpKind.ElementEnd && op.prev !== null &&
|
|
9401
|
+
op.prev.kind === OpKind.ElementStart) {
|
|
9402
|
+
// Transmute the `ElementStart` instruction to `Element`. This is safe as they're designed
|
|
9403
|
+
// to be identical apart from the `kind`.
|
|
9404
|
+
op.prev.kind = OpKind.Element;
|
|
9405
|
+
// Remove the `ElementEnd` instruction.
|
|
9406
|
+
OpList.remove(op);
|
|
9407
|
+
}
|
|
9408
|
+
}
|
|
9409
|
+
}
|
|
9410
|
+
}
|
|
9411
|
+
|
|
9412
|
+
/**
|
|
9413
|
+
* Generate `ir.AdvanceOp`s in between `ir.UpdateOp`s that ensure the runtime's implicit slot
|
|
9414
|
+
* context will be advanced correctly.
|
|
9415
|
+
*/
|
|
9416
|
+
function phaseGenerateAdvance(cpl) {
|
|
9417
|
+
for (const [_, view] of cpl.views) {
|
|
9418
|
+
// First build a map of all of the declarations in the view that have assigned slots.
|
|
9419
|
+
const slotMap = new Map();
|
|
9420
|
+
for (const op of view.create) {
|
|
9421
|
+
if (!hasConsumesSlotTrait(op)) {
|
|
9422
|
+
continue;
|
|
9423
|
+
}
|
|
9424
|
+
else if (op.slot === null) {
|
|
9425
|
+
throw new Error(`AssertionError: expected slots to have been allocated before generating advance() calls`);
|
|
9426
|
+
}
|
|
9427
|
+
slotMap.set(op.xref, op.slot);
|
|
9428
|
+
}
|
|
9429
|
+
// Next, step through the update operations and generate `ir.AdvanceOp`s as required to ensure
|
|
9430
|
+
// the runtime's implicit slot counter will be set to the correct slot before executing each
|
|
9431
|
+
// update operation which depends on it.
|
|
9432
|
+
//
|
|
9433
|
+
// To do that, we track what the runtime's slot counter will be through the update operations.
|
|
9434
|
+
let slotContext = 0;
|
|
9435
|
+
for (const op of view.update) {
|
|
9436
|
+
if (!hasDependsOnSlotContextTrait(op)) {
|
|
9437
|
+
// `op` doesn't depend on the slot counter, so it can be skipped.
|
|
9438
|
+
continue;
|
|
9439
|
+
}
|
|
9440
|
+
else if (!slotMap.has(op.target)) {
|
|
9441
|
+
// We expect ops that _do_ depend on the slot counter to point at declarations that exist in
|
|
9442
|
+
// the `slotMap`.
|
|
9443
|
+
throw new Error(`AssertionError: reference to unknown slot for var ${op.target}`);
|
|
9444
|
+
}
|
|
9445
|
+
const slot = slotMap.get(op.target);
|
|
9446
|
+
// Does the slot counter need to be adjusted?
|
|
9447
|
+
if (slotContext !== slot) {
|
|
9448
|
+
// If so, generate an `ir.AdvanceOp` to advance the counter.
|
|
9449
|
+
const delta = slot - slotContext;
|
|
9450
|
+
if (delta < 0) {
|
|
9451
|
+
throw new Error(`AssertionError: slot counter should never need to move backwards`);
|
|
9452
|
+
}
|
|
9453
|
+
OpList.insertBefore(op, createAdvanceOp(delta));
|
|
9454
|
+
slotContext = slot;
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
9459
|
+
|
|
9460
|
+
// This file contains helpers for generating calls to Ivy instructions. In particular, each
|
|
9461
|
+
// instruction type is represented as a function, which may select a specific instruction variant
|
|
9462
|
+
// depending on the exact arguments.
|
|
9463
|
+
function element(slot, tag, constIndex, localRefIndex) {
|
|
9464
|
+
return elementStartBase(Identifiers.element, slot, tag, constIndex, localRefIndex);
|
|
9465
|
+
}
|
|
9466
|
+
function elementStart(slot, tag, constIndex, localRefIndex) {
|
|
9467
|
+
return elementStartBase(Identifiers.elementStart, slot, tag, constIndex, localRefIndex);
|
|
9468
|
+
}
|
|
9469
|
+
function elementStartBase(instruction, slot, tag, constIndex, localRefIndex) {
|
|
9470
|
+
const args = [
|
|
9471
|
+
literal(slot),
|
|
9472
|
+
literal(tag),
|
|
9473
|
+
];
|
|
9474
|
+
if (localRefIndex !== null) {
|
|
9475
|
+
args.push(literal(constIndex), // might be null, but that's okay.
|
|
9476
|
+
literal(localRefIndex));
|
|
9477
|
+
}
|
|
9478
|
+
else if (constIndex !== null) {
|
|
9479
|
+
args.push(literal(constIndex));
|
|
9480
|
+
}
|
|
9481
|
+
return call(instruction, args);
|
|
9482
|
+
}
|
|
9483
|
+
function elementEnd() {
|
|
9484
|
+
return call(Identifiers.elementEnd, []);
|
|
9485
|
+
}
|
|
9486
|
+
function template(slot, templateFnRef, decls, vars, tag, constIndex) {
|
|
9487
|
+
return call(Identifiers.templateCreate, [
|
|
9488
|
+
literal(slot),
|
|
9489
|
+
templateFnRef,
|
|
9490
|
+
literal(decls),
|
|
9491
|
+
literal(vars),
|
|
9492
|
+
literal(tag),
|
|
9493
|
+
literal(constIndex),
|
|
9494
|
+
]);
|
|
9495
|
+
}
|
|
9496
|
+
function listener(name, handlerFn) {
|
|
9497
|
+
return call(Identifiers.listener, [
|
|
9498
|
+
literal(name),
|
|
9499
|
+
handlerFn,
|
|
9500
|
+
]);
|
|
9501
|
+
}
|
|
9502
|
+
function advance(delta) {
|
|
9503
|
+
return call(Identifiers.advance, [
|
|
9504
|
+
literal(delta),
|
|
9505
|
+
]);
|
|
9506
|
+
}
|
|
9507
|
+
function reference(slot) {
|
|
9508
|
+
return importExpr(Identifiers.reference).callFn([
|
|
9509
|
+
literal(slot),
|
|
9510
|
+
]);
|
|
9511
|
+
}
|
|
9512
|
+
function nextContext() {
|
|
9513
|
+
return importExpr(Identifiers.nextContext).callFn([]);
|
|
9514
|
+
}
|
|
9515
|
+
function getCurrentView() {
|
|
9516
|
+
return importExpr(Identifiers.getCurrentView).callFn([]);
|
|
9517
|
+
}
|
|
9518
|
+
function restoreView(savedView) {
|
|
9519
|
+
return importExpr(Identifiers.restoreView).callFn([
|
|
9520
|
+
savedView,
|
|
9521
|
+
]);
|
|
9522
|
+
}
|
|
9523
|
+
function resetView(returnValue) {
|
|
9524
|
+
return importExpr(Identifiers.reference).callFn([
|
|
9525
|
+
returnValue,
|
|
9526
|
+
]);
|
|
9527
|
+
}
|
|
9528
|
+
function text(slot, initialValue) {
|
|
9529
|
+
const args = [literal(slot)];
|
|
9530
|
+
if (initialValue !== '') {
|
|
9531
|
+
args.push(literal(initialValue));
|
|
9532
|
+
}
|
|
9533
|
+
return call(Identifiers.text, args);
|
|
9534
|
+
}
|
|
9535
|
+
function property(name, expression) {
|
|
9536
|
+
return call(Identifiers.property, [
|
|
9537
|
+
literal(name),
|
|
9538
|
+
expression,
|
|
9539
|
+
]);
|
|
9540
|
+
}
|
|
9541
|
+
function textInterpolate(strings, expressions) {
|
|
9542
|
+
if (strings.length < 1 || expressions.length !== strings.length - 1) {
|
|
9543
|
+
throw new Error(`AssertionError: expected specific shape of args for strings/expressions in interpolation`);
|
|
9544
|
+
}
|
|
9545
|
+
const interpolationArgs = [];
|
|
9546
|
+
let idx;
|
|
9547
|
+
for (idx = 0; idx < expressions.length; idx++) {
|
|
9548
|
+
interpolationArgs.push(literal(strings[idx]), expressions[idx]);
|
|
9549
|
+
}
|
|
9550
|
+
// idx points at the last string.
|
|
9551
|
+
interpolationArgs.push(literal(strings[idx]));
|
|
9552
|
+
return callInterpolation(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs);
|
|
9553
|
+
}
|
|
9554
|
+
function call(instruction, args) {
|
|
9555
|
+
return createStatementOp(importExpr(instruction).callFn(args).toStmt());
|
|
9556
|
+
}
|
|
9557
|
+
/**
|
|
9558
|
+
* `InterpolationConfig` for the `textInterpolate` instruction.
|
|
9559
|
+
*/
|
|
9560
|
+
const TEXT_INTERPOLATE_CONFIG = {
|
|
9561
|
+
constant: [
|
|
9562
|
+
Identifiers.textInterpolate,
|
|
9563
|
+
Identifiers.textInterpolate1,
|
|
9564
|
+
Identifiers.textInterpolate2,
|
|
9565
|
+
Identifiers.textInterpolate3,
|
|
9566
|
+
Identifiers.textInterpolate4,
|
|
9567
|
+
Identifiers.textInterpolate5,
|
|
9568
|
+
Identifiers.textInterpolate6,
|
|
9569
|
+
Identifiers.textInterpolate7,
|
|
9570
|
+
Identifiers.textInterpolate8,
|
|
9571
|
+
],
|
|
9572
|
+
variable: Identifiers.textInterpolateV,
|
|
9573
|
+
};
|
|
9574
|
+
function callInterpolation(config, baseArgs, interpolationArgs) {
|
|
9575
|
+
if (interpolationArgs.length % 2 === 0) {
|
|
9576
|
+
throw new Error(`Expected odd number of interpolation arguments`);
|
|
9577
|
+
}
|
|
9578
|
+
const n = (interpolationArgs.length - 1) / 2;
|
|
9579
|
+
if (n < config.constant.length) {
|
|
9580
|
+
// Constant calling pattern.
|
|
9581
|
+
return call(config.constant[n], [...baseArgs, ...interpolationArgs]);
|
|
9582
|
+
}
|
|
9583
|
+
else {
|
|
9584
|
+
// Variable calling pattern.
|
|
9585
|
+
return call(config.variable, [...baseArgs, literalArr(interpolationArgs)]);
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9588
|
+
|
|
9589
|
+
/**
|
|
9590
|
+
* Compiles semantic operations across all views and generates output `o.Statement`s with actual
|
|
9591
|
+
* runtime calls in their place.
|
|
9592
|
+
*
|
|
9593
|
+
* Reification replaces semantic operations with selected Ivy instructions and other generated code
|
|
9594
|
+
* structures. After reification, the create/update operation lists of all views should only contain
|
|
9595
|
+
* `ir.StatementOp`s (which wrap generated `o.Statement`s).
|
|
9596
|
+
*/
|
|
9597
|
+
function phaseReify(cpl) {
|
|
9598
|
+
for (const [_, view] of cpl.views) {
|
|
9599
|
+
reifyCreateOperations(view, view.create);
|
|
9600
|
+
reifyUpdateOperations(view, view.update);
|
|
9601
|
+
}
|
|
9602
|
+
}
|
|
9603
|
+
function reifyCreateOperations(view, ops) {
|
|
9604
|
+
for (const op of ops) {
|
|
9605
|
+
transformExpressionsInOp(op, reifyIrExpression);
|
|
9606
|
+
switch (op.kind) {
|
|
9607
|
+
case OpKind.Text:
|
|
9608
|
+
OpList.replace(op, text(op.slot, op.initialValue));
|
|
9609
|
+
break;
|
|
9610
|
+
case OpKind.ElementStart:
|
|
9611
|
+
OpList.replace(op, elementStart(op.slot, op.tag, op.attributes, op.localRefs));
|
|
9612
|
+
break;
|
|
9613
|
+
case OpKind.Element:
|
|
9614
|
+
OpList.replace(op, element(op.slot, op.tag, op.attributes, op.localRefs));
|
|
9615
|
+
break;
|
|
9616
|
+
case OpKind.ElementEnd:
|
|
9617
|
+
OpList.replace(op, elementEnd());
|
|
9618
|
+
break;
|
|
9619
|
+
case OpKind.Template:
|
|
9620
|
+
const childView = view.tpl.views.get(op.xref);
|
|
9621
|
+
OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.localRefs));
|
|
9622
|
+
break;
|
|
9623
|
+
case OpKind.Listener:
|
|
9624
|
+
const listenerFn = reifyListenerHandler(view, op.handlerFnName, op.handlerOps);
|
|
9625
|
+
OpList.replace(op, listener(op.name, listenerFn));
|
|
9626
|
+
break;
|
|
9627
|
+
case OpKind.Variable:
|
|
9628
|
+
if (op.name === null) {
|
|
9629
|
+
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
9630
|
+
}
|
|
9631
|
+
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.name, op.initializer)));
|
|
9632
|
+
break;
|
|
9633
|
+
case OpKind.Statement:
|
|
9634
|
+
// Pass statement operations directly through.
|
|
9635
|
+
break;
|
|
9636
|
+
default:
|
|
9637
|
+
throw new Error(`AssertionError: Unsupported reification of create op ${OpKind[op.kind]}`);
|
|
9638
|
+
}
|
|
9639
|
+
}
|
|
9640
|
+
}
|
|
9641
|
+
function reifyUpdateOperations(_view, ops) {
|
|
9642
|
+
for (const op of ops) {
|
|
9643
|
+
transformExpressionsInOp(op, reifyIrExpression);
|
|
9644
|
+
switch (op.kind) {
|
|
9645
|
+
case OpKind.Advance:
|
|
9646
|
+
OpList.replace(op, advance(op.delta));
|
|
9647
|
+
break;
|
|
9648
|
+
case OpKind.Property:
|
|
9649
|
+
OpList.replace(op, property(op.name, op.expression));
|
|
9650
|
+
break;
|
|
9651
|
+
case OpKind.InterpolateText:
|
|
9652
|
+
OpList.replace(op, textInterpolate(op.strings, op.expressions));
|
|
9653
|
+
break;
|
|
9654
|
+
case OpKind.Variable:
|
|
9655
|
+
if (op.name === null) {
|
|
9656
|
+
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
9657
|
+
}
|
|
9658
|
+
OpList.replace(op, createStatementOp(new DeclareVarStmt(op.name, op.initializer)));
|
|
9659
|
+
break;
|
|
9660
|
+
case OpKind.Statement:
|
|
9661
|
+
// Pass statement operations directly through.
|
|
9662
|
+
break;
|
|
9663
|
+
default:
|
|
9664
|
+
throw new Error(`AssertionError: Unsupported reification of update op ${OpKind[op.kind]}`);
|
|
9665
|
+
}
|
|
9666
|
+
}
|
|
9667
|
+
}
|
|
9668
|
+
function reifyIrExpression(expr) {
|
|
9669
|
+
switch (expr.kind) {
|
|
9670
|
+
case ExpressionKind.NextContext:
|
|
9671
|
+
return nextContext();
|
|
9672
|
+
case ExpressionKind.Reference:
|
|
9673
|
+
return reference(expr.slot + 1 + expr.offset);
|
|
9674
|
+
case ExpressionKind.LexicalRead:
|
|
9675
|
+
throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
|
|
9676
|
+
case ExpressionKind.RestoreView:
|
|
9677
|
+
if (typeof expr.view === 'number') {
|
|
9678
|
+
throw new Error(`AssertionError: unresolved RestoreView`);
|
|
9679
|
+
}
|
|
9680
|
+
return restoreView(expr.view);
|
|
9681
|
+
case ExpressionKind.ResetView:
|
|
9682
|
+
return resetView(expr.expr);
|
|
9683
|
+
case ExpressionKind.GetCurrentView:
|
|
9684
|
+
return getCurrentView();
|
|
9685
|
+
case ExpressionKind.ReadVariable:
|
|
9686
|
+
if (expr.name === null) {
|
|
9687
|
+
throw new Error(`Read of unnamed variable ${expr.xref}`);
|
|
9688
|
+
}
|
|
9689
|
+
return variable(expr.name);
|
|
9690
|
+
default:
|
|
9691
|
+
throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
|
|
9692
|
+
}
|
|
9693
|
+
}
|
|
9694
|
+
/**
|
|
9695
|
+
* Listeners get turned into a function expression, which may or may not have the `$event`
|
|
9696
|
+
* parameter defined.
|
|
9697
|
+
*/
|
|
9698
|
+
function reifyListenerHandler(view, name, handlerOps) {
|
|
9699
|
+
const lookForEvent = new LookForEventVisitor();
|
|
9700
|
+
// First, reify all instruction calls within `handlerOps`.
|
|
9701
|
+
reifyUpdateOperations(view, handlerOps);
|
|
9702
|
+
// Next, extract all the `o.Statement`s from the reified operations. We can expect that at this
|
|
9703
|
+
// point, all operations have been converted to statements.
|
|
9704
|
+
const handlerStmts = [];
|
|
9705
|
+
for (const op of handlerOps) {
|
|
9706
|
+
if (op.kind !== OpKind.Statement) {
|
|
9707
|
+
throw new Error(`AssertionError: expected reified statements, but found op ${OpKind[op.kind]}`);
|
|
9708
|
+
}
|
|
9709
|
+
handlerStmts.push(op.statement);
|
|
9710
|
+
}
|
|
9711
|
+
// Scan the statement list for usages of `$event`. If referenced, we need to generate it as a
|
|
9712
|
+
// parameter.
|
|
9713
|
+
lookForEvent.visitAllStatements(handlerStmts, null);
|
|
9714
|
+
const params = [];
|
|
9715
|
+
if (lookForEvent.seenEventRead) {
|
|
9716
|
+
// We need the `$event` parameter.
|
|
9717
|
+
params.push(new FnParam('$event'));
|
|
9718
|
+
}
|
|
9719
|
+
return fn(params, handlerStmts, undefined, undefined, name);
|
|
9720
|
+
}
|
|
9721
|
+
/**
|
|
9722
|
+
* Visitor which scans for reads of the `$event` special variable.
|
|
9723
|
+
*/
|
|
9724
|
+
class LookForEventVisitor extends RecursiveAstVisitor$1 {
|
|
9725
|
+
constructor() {
|
|
9726
|
+
super(...arguments);
|
|
9727
|
+
this.seenEventRead = false;
|
|
9728
|
+
}
|
|
9729
|
+
visitReadVarExpr(ast, context) {
|
|
9730
|
+
if (ast.name === '$event') {
|
|
9731
|
+
this.seenEventRead = true;
|
|
9732
|
+
}
|
|
9733
|
+
}
|
|
9734
|
+
}
|
|
9735
|
+
|
|
9736
|
+
/**
|
|
9737
|
+
* Assign data slots for all operations which implement `ConsumesSlotOpTrait`, and propagate the
|
|
9738
|
+
* assigned data slots of those operations to any expressions which reference them via
|
|
9739
|
+
* `UsesSlotIndexExprTrait`.
|
|
9740
|
+
*
|
|
9741
|
+
* This phase is also responsible for counting the number of slots used for each view (its `decls`)
|
|
9742
|
+
* and propagating that number into the `Template` operations which declare embedded views.
|
|
9743
|
+
*/
|
|
9744
|
+
function phaseSlotAllocation(cpl) {
|
|
9745
|
+
// Map of all declarations in all views within the component which require an assigned slot index.
|
|
9746
|
+
// This map needs to be global (across all views within the component) since it's possible to
|
|
9747
|
+
// reference a slot from one view from an expression within another (e.g. local references work
|
|
9748
|
+
// this way).
|
|
9749
|
+
const slotMap = new Map();
|
|
9750
|
+
// Process all views in the component and assign slot indexes.
|
|
9751
|
+
for (const [_, view] of cpl.views) {
|
|
9752
|
+
// Slot indices start at 0 for each view (and are not unique between views).
|
|
9753
|
+
let slotCount = 0;
|
|
9754
|
+
for (const op of view.create) {
|
|
9755
|
+
// Only consider declarations which consume data slots.
|
|
9756
|
+
if (!hasConsumesSlotTrait(op)) {
|
|
9757
|
+
continue;
|
|
9758
|
+
}
|
|
9759
|
+
// Assign slots to this declaration starting at the current `slotCount`.
|
|
9760
|
+
op.slot = slotCount;
|
|
9761
|
+
// And track its assigned slot in the `slotMap`.
|
|
9762
|
+
slotMap.set(op.xref, op.slot);
|
|
9763
|
+
// Each declaration may use more than 1 slot, so increment `slotCount` to reserve the number
|
|
9764
|
+
// of slots required.
|
|
9765
|
+
slotCount += op.numSlotsUsed;
|
|
9766
|
+
}
|
|
9767
|
+
// Record the total number of slots used on the view itself. This will later be propagated into
|
|
9768
|
+
// `ir.TemplateOp`s which declare those views (except for the root view).
|
|
9769
|
+
view.decls = slotCount;
|
|
9770
|
+
}
|
|
9771
|
+
// After slot assignment, `slotMap` now contains slot assignments for every declaration in the
|
|
9772
|
+
// whole template, across all views. Next, look for expressions which implement
|
|
9773
|
+
// `UsesSlotIndexExprTrait` and propagate the assigned slot indexes into them.
|
|
9774
|
+
// Additionally, this second scan allows us to find `ir.TemplateOp`s which declare views and
|
|
9775
|
+
// propagate the number of slots used for each view into the operation which declares it.
|
|
9776
|
+
for (const [_, view] of cpl.views) {
|
|
9777
|
+
for (const op of view.ops()) {
|
|
9778
|
+
if (op.kind === OpKind.Template) {
|
|
9779
|
+
// Record the number of slots used by the view this `ir.TemplateOp` declares in the
|
|
9780
|
+
// operation itself, so it can be emitted later.
|
|
9781
|
+
const childView = cpl.views.get(op.xref);
|
|
9782
|
+
op.decls = childView.decls;
|
|
9783
|
+
}
|
|
9784
|
+
// Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
|
|
9785
|
+
visitExpressionsInOp(op, expr => {
|
|
9786
|
+
if (!hasUsesSlotIndexTrait(expr) || expr.slot !== null) {
|
|
9787
|
+
return;
|
|
9788
|
+
}
|
|
9789
|
+
// The `UsesSlotIndexExprTrait` indicates that this expression references something declared
|
|
9790
|
+
// in this component template by its slot index. Use the `target` `ir.XrefId` to find the
|
|
9791
|
+
// allocated slot for that declaration in `slotMap`.
|
|
9792
|
+
if (!slotMap.has(expr.target)) {
|
|
9793
|
+
// We do expect to find a slot allocated for everything which might be referenced.
|
|
9794
|
+
throw new Error(`AssertionError: no slot allocated for ${expr.constructor.name} target ${expr.target}`);
|
|
9795
|
+
}
|
|
9796
|
+
// Record the allocated slot on the expression.
|
|
9797
|
+
expr.slot = slotMap.get(expr.target);
|
|
9798
|
+
});
|
|
9799
|
+
}
|
|
9800
|
+
}
|
|
9801
|
+
}
|
|
9802
|
+
|
|
9803
|
+
/**
|
|
9804
|
+
* Counts the number of variable slots used within each view, and stores that on the view itself, as
|
|
9805
|
+
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
9806
|
+
*/
|
|
9807
|
+
function phaseVarCounting(cpl) {
|
|
9808
|
+
// First, count the vars used in each view, and update the view-level counter.
|
|
9809
|
+
for (const [_, view] of cpl.views) {
|
|
9810
|
+
let varCount = 0;
|
|
9811
|
+
for (const op of view.ops()) {
|
|
9812
|
+
if (hasConsumesVarsTrait(op)) {
|
|
9813
|
+
varCount += varsUsedByOp(op);
|
|
9814
|
+
}
|
|
9815
|
+
visitExpressionsInOp(op, expr => {
|
|
9816
|
+
if (hasConsumesVarsTrait(expr)) {
|
|
9817
|
+
varCount += varsUsedByIrExpression(expr);
|
|
9818
|
+
}
|
|
9819
|
+
});
|
|
9820
|
+
}
|
|
9821
|
+
view.vars = varCount;
|
|
9822
|
+
}
|
|
9823
|
+
// Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is an
|
|
9824
|
+
// embedded view).
|
|
9825
|
+
for (const [_, view] of cpl.views) {
|
|
9826
|
+
for (const op of view.create) {
|
|
9827
|
+
if (op.kind !== OpKind.Template) {
|
|
9828
|
+
continue;
|
|
9829
|
+
}
|
|
9830
|
+
const childView = cpl.views.get(op.xref);
|
|
9831
|
+
op.vars = childView.vars;
|
|
9832
|
+
}
|
|
9833
|
+
}
|
|
9834
|
+
}
|
|
9835
|
+
/**
|
|
9836
|
+
* Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so
|
|
9837
|
+
* count the variables used by any particular `op`.
|
|
9838
|
+
*/
|
|
9839
|
+
function varsUsedByOp(op) {
|
|
9840
|
+
switch (op.kind) {
|
|
9841
|
+
case OpKind.Property:
|
|
9842
|
+
// Property bindings use 1 variable slot.
|
|
9843
|
+
return 1;
|
|
9844
|
+
case OpKind.InterpolateText:
|
|
9845
|
+
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
9846
|
+
return op.expressions.length;
|
|
9847
|
+
default:
|
|
9848
|
+
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
9849
|
+
}
|
|
9850
|
+
}
|
|
9851
|
+
function varsUsedByIrExpression(expr) {
|
|
9852
|
+
return 0;
|
|
9853
|
+
}
|
|
9854
|
+
|
|
9855
|
+
/**
|
|
9856
|
+
* Generate names for functions and variables across all views.
|
|
9857
|
+
*
|
|
9858
|
+
* This includes propagating those names into any `ir.ReadVariableExpr`s of those variables, so that
|
|
9859
|
+
* the reads can be emitted correctly.
|
|
9860
|
+
*/
|
|
9861
|
+
function phaseNaming(cpl) {
|
|
9862
|
+
// TODO(alxhub): convert this temporary name to match how the `TemplateDefinitionBuilder`
|
|
9863
|
+
// names the main component template function.
|
|
9864
|
+
cpl.root.fnName = `${cpl.componentName}_Template`;
|
|
9865
|
+
for (const [id, view] of cpl.views) {
|
|
9866
|
+
let vIndex = 0;
|
|
9867
|
+
if (view.fnName === null) {
|
|
9868
|
+
// TODO(alxhub): convert this temporary name to match how the `TemplateDefinitionBuilder`
|
|
9869
|
+
// names embedded view functions.
|
|
9870
|
+
view.fnName = `${cpl.componentName}_EmbeddedView_${id}`;
|
|
9871
|
+
}
|
|
9872
|
+
// Keep track of the names we assign to variables in the view. We'll need to propagate these
|
|
9873
|
+
// into reads of those variables afterwards.
|
|
9874
|
+
const varNames = new Map();
|
|
9875
|
+
for (const op of view.ops()) {
|
|
9876
|
+
switch (op.kind) {
|
|
9877
|
+
case OpKind.Listener:
|
|
9878
|
+
if (op.handlerFnName === null) {
|
|
9879
|
+
// TODO(alxhub): convert this temporary name to match how the
|
|
9880
|
+
// `TemplateDefinitionBuilder` names listener functions.
|
|
9881
|
+
op.handlerFnName = `${view.fnName}_${op.name}_listener`;
|
|
9882
|
+
}
|
|
9883
|
+
break;
|
|
9884
|
+
case OpKind.Variable:
|
|
9885
|
+
if (op.name === null) {
|
|
9886
|
+
op.name = `_r${vIndex++}`;
|
|
9887
|
+
varNames.set(op.xref, op.name);
|
|
9888
|
+
}
|
|
9889
|
+
break;
|
|
9890
|
+
}
|
|
9891
|
+
}
|
|
9892
|
+
// Having named all variables declared in the view, now we can push those names into the
|
|
9893
|
+
// `ir.ReadVariableExpr` expressions which represent reads of those variables.
|
|
9894
|
+
for (const op of view.ops()) {
|
|
9895
|
+
visitExpressionsInOp(op, expr => {
|
|
9896
|
+
if (!(expr instanceof ReadVariableExpr) || expr.name !== null) {
|
|
9897
|
+
return;
|
|
9898
|
+
}
|
|
9899
|
+
if (!varNames.has(expr.xref)) {
|
|
9900
|
+
throw new Error(`Variable ${expr.xref} not yet named`);
|
|
9901
|
+
}
|
|
9902
|
+
expr.name = varNames.get(expr.xref);
|
|
9903
|
+
});
|
|
9904
|
+
}
|
|
9905
|
+
}
|
|
9906
|
+
}
|
|
9907
|
+
|
|
9908
|
+
/**
|
|
9909
|
+
* Lifts local reference declarations on element-like structures within each view into an entry in
|
|
9910
|
+
* the `consts` array for the whole component.
|
|
9911
|
+
*/
|
|
9912
|
+
function phaseLocalRefs(cpl) {
|
|
9913
|
+
for (const view of cpl.views.values()) {
|
|
9914
|
+
for (const op of view.create) {
|
|
9915
|
+
switch (op.kind) {
|
|
9916
|
+
case OpKind.ElementStart:
|
|
9917
|
+
case OpKind.Element:
|
|
9918
|
+
case OpKind.Template:
|
|
9919
|
+
if (!Array.isArray(op.localRefs)) {
|
|
9920
|
+
throw new Error(`AssertionError: expected localRefs to be an array still`);
|
|
9921
|
+
}
|
|
9922
|
+
op.numSlotsUsed += op.localRefs.length;
|
|
9923
|
+
if (op.localRefs.length > 0) {
|
|
9924
|
+
const localRefs = serializeLocalRefs(op.localRefs);
|
|
9925
|
+
op.localRefs = cpl.addConst(localRefs);
|
|
9926
|
+
}
|
|
9927
|
+
else {
|
|
9928
|
+
op.localRefs = null;
|
|
9929
|
+
}
|
|
9930
|
+
break;
|
|
9931
|
+
}
|
|
9932
|
+
}
|
|
9933
|
+
}
|
|
9934
|
+
}
|
|
9935
|
+
function serializeLocalRefs(refs) {
|
|
9936
|
+
const constRefs = [];
|
|
9937
|
+
for (const ref of refs) {
|
|
9938
|
+
constRefs.push(literal(ref.name), literal(ref.target));
|
|
9939
|
+
}
|
|
9940
|
+
return literalArr(constRefs);
|
|
9941
|
+
}
|
|
9942
|
+
|
|
9943
|
+
/**
|
|
9944
|
+
* Generate a preamble sequence for each view creation block and listener function which declares
|
|
9945
|
+
* any variables that be referenced in other operations in the block.
|
|
9946
|
+
*
|
|
9947
|
+
* Variables generated include:
|
|
9948
|
+
* * a saved view context to be used to restore the current view in event listeners.
|
|
9949
|
+
* * the context of the restored view within event listener handlers.
|
|
9950
|
+
* * context variables from the current view as well as all parent views (including the root
|
|
9951
|
+
* context if needed).
|
|
9952
|
+
* * local references from elements within the current view and any lexical parents.
|
|
9953
|
+
*
|
|
9954
|
+
* Variables are generated here unconditionally, and may optimized away in future operations if it
|
|
9955
|
+
* turns out their values (and any side effects) are unused.
|
|
9956
|
+
*/
|
|
9957
|
+
function phaseGenerateVariables(cpl) {
|
|
9958
|
+
recursivelyProcessView(cpl.root, /* there is no parent scope for the root view */ null);
|
|
9959
|
+
}
|
|
9960
|
+
/**
|
|
9961
|
+
* Process the given `ViewCompilation` and generate preambles for it and any listeners that it
|
|
9962
|
+
* declares.
|
|
9963
|
+
*
|
|
9964
|
+
* @param `parentScope` a scope extracted from the parent view which captures any variables which
|
|
9965
|
+
* should be inherited by this view. `null` if the current view is the root view.
|
|
9966
|
+
*/
|
|
9967
|
+
function recursivelyProcessView(view, parentScope) {
|
|
9968
|
+
// Extract a `Scope` from this view.
|
|
9969
|
+
const scope = getScopeForView(view, parentScope);
|
|
9970
|
+
// Start the view creation block with an operation to save the current view context. This may be
|
|
9971
|
+
// used to restore the view context in any listeners that may be present.
|
|
9972
|
+
view.create.prepend([
|
|
9973
|
+
createVariableOp(view.tpl.allocateXrefId(), {
|
|
9974
|
+
kind: SemanticVariableKind.SavedView,
|
|
9975
|
+
view: view.xref,
|
|
9976
|
+
}, new GetCurrentViewExpr()),
|
|
9977
|
+
]);
|
|
9978
|
+
for (const op of view.create) {
|
|
9979
|
+
switch (op.kind) {
|
|
9980
|
+
case OpKind.Template:
|
|
9981
|
+
// Descend into child embedded views.
|
|
9982
|
+
recursivelyProcessView(view.tpl.views.get(op.xref), scope);
|
|
9983
|
+
break;
|
|
9984
|
+
case OpKind.Listener:
|
|
9985
|
+
// Listeners get a preamble which starts with a call to restore the view.
|
|
9986
|
+
const preambleOps = [
|
|
9987
|
+
createVariableOp(view.tpl.allocateXrefId(), {
|
|
9988
|
+
kind: SemanticVariableKind.Context,
|
|
9989
|
+
view: view.xref,
|
|
9990
|
+
}, new RestoreViewExpr(view.xref)),
|
|
9991
|
+
// And includes all variables available to this view.
|
|
9992
|
+
...generateVariablesInScopeForView(view, scope)
|
|
9993
|
+
];
|
|
9994
|
+
op.handlerOps.prepend(preambleOps);
|
|
9995
|
+
// The "restore view" operation in listeners requires a call to `resetView` to reset the
|
|
9996
|
+
// context prior to returning from the listener operation. Find any `return` statements in
|
|
9997
|
+
// the listener body and wrap them in a call to reset the view.
|
|
9998
|
+
for (const handlerOp of op.handlerOps) {
|
|
9999
|
+
if (handlerOp.kind === OpKind.Statement &&
|
|
10000
|
+
handlerOp.statement instanceof ReturnStatement) {
|
|
10001
|
+
handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value);
|
|
10002
|
+
}
|
|
10003
|
+
}
|
|
10004
|
+
break;
|
|
10005
|
+
}
|
|
10006
|
+
}
|
|
10007
|
+
// Prepend the declarations for all available variables in scope to the `update` block.
|
|
10008
|
+
const preambleOps = generateVariablesInScopeForView(view, scope);
|
|
10009
|
+
view.update.prepend(preambleOps);
|
|
10010
|
+
}
|
|
10011
|
+
/**
|
|
10012
|
+
* Process a view and generate a `Scope` representing the variables available for reference within
|
|
10013
|
+
* that view.
|
|
10014
|
+
*/
|
|
10015
|
+
function getScopeForView(view, parent) {
|
|
10016
|
+
const scope = {
|
|
10017
|
+
view: view.xref,
|
|
10018
|
+
references: [],
|
|
10019
|
+
parent,
|
|
10020
|
+
};
|
|
10021
|
+
for (const op of view.create) {
|
|
10022
|
+
switch (op.kind) {
|
|
10023
|
+
case OpKind.Element:
|
|
10024
|
+
case OpKind.ElementStart:
|
|
10025
|
+
case OpKind.Template:
|
|
10026
|
+
if (!Array.isArray(op.localRefs)) {
|
|
10027
|
+
throw new Error(`AssertionError: expected localRefs to be an array`);
|
|
10028
|
+
}
|
|
10029
|
+
// Record available local references from this element.
|
|
10030
|
+
for (let offset = 0; offset < op.localRefs.length; offset++) {
|
|
10031
|
+
scope.references.push({
|
|
10032
|
+
name: op.localRefs[offset].name,
|
|
10033
|
+
targetId: op.xref,
|
|
10034
|
+
offset,
|
|
10035
|
+
});
|
|
10036
|
+
}
|
|
10037
|
+
break;
|
|
10038
|
+
}
|
|
10039
|
+
}
|
|
10040
|
+
return scope;
|
|
10041
|
+
}
|
|
10042
|
+
/**
|
|
10043
|
+
* Generate declarations for all variables that are in scope for a given view.
|
|
10044
|
+
*
|
|
10045
|
+
* This is a recursive process, as views inherit variables available from their parent view, which
|
|
10046
|
+
* itself may have inherited variables, etc.
|
|
10047
|
+
*/
|
|
10048
|
+
function generateVariablesInScopeForView(view, scope) {
|
|
10049
|
+
const newOps = [];
|
|
10050
|
+
if (scope.view !== view.xref) {
|
|
10051
|
+
// Before generating variables for a parent view, we need to switch to the context of the parent
|
|
10052
|
+
// view with a `nextContext` expression. This context switching operation itself declares a
|
|
10053
|
+
// variable, because the context of the view may be referenced directly.
|
|
10054
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), {
|
|
10055
|
+
kind: SemanticVariableKind.Context,
|
|
10056
|
+
view: scope.view,
|
|
10057
|
+
}, new NextContextExpr()));
|
|
10058
|
+
}
|
|
10059
|
+
// Add variables for all context variables available in this scope's view.
|
|
10060
|
+
for (const [name, value] of view.tpl.views.get(scope.view).contextVariables) {
|
|
10061
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), {
|
|
10062
|
+
kind: SemanticVariableKind.Identifier,
|
|
10063
|
+
name,
|
|
10064
|
+
}, new ReadPropExpr(new ContextExpr(view.xref), value)));
|
|
10065
|
+
}
|
|
10066
|
+
// Add variables for all local references declared for elements in this scope.
|
|
10067
|
+
for (const ref of scope.references) {
|
|
10068
|
+
newOps.push(createVariableOp(view.tpl.allocateXrefId(), {
|
|
10069
|
+
kind: SemanticVariableKind.Identifier,
|
|
10070
|
+
name: ref.name,
|
|
10071
|
+
}, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
10072
|
+
}
|
|
10073
|
+
if (scope.parent !== null) {
|
|
10074
|
+
// Recursively add variables from the parent scope.
|
|
10075
|
+
newOps.push(...generateVariablesInScopeForView(view, scope.parent));
|
|
10076
|
+
}
|
|
10077
|
+
return newOps;
|
|
10078
|
+
}
|
|
10079
|
+
|
|
10080
|
+
/**
|
|
10081
|
+
* Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to
|
|
10082
|
+
* property reads on the top-level component context.
|
|
10083
|
+
*
|
|
10084
|
+
* Also matches `ir.RestoreViewExpr` expressions with the variables of their corresponding saved
|
|
10085
|
+
* views.
|
|
10086
|
+
*/
|
|
10087
|
+
function phaseResolveNames(cpl) {
|
|
10088
|
+
for (const [_, view] of cpl.views) {
|
|
10089
|
+
processLexicalScope$1(view, view.create, null);
|
|
10090
|
+
processLexicalScope$1(view, view.update, null);
|
|
10091
|
+
}
|
|
10092
|
+
}
|
|
10093
|
+
function processLexicalScope$1(view, ops, savedView) {
|
|
10094
|
+
// Maps names defined in the lexical scope of this template to the `ir.XrefId`s of the variable
|
|
10095
|
+
// declarations which represent those values.
|
|
10096
|
+
//
|
|
10097
|
+
// Since variables are generated in each view for the entire lexical scope (including any
|
|
10098
|
+
// identifiers from parent templates) only local variables need be considered here.
|
|
10099
|
+
const scope = new Map();
|
|
10100
|
+
// First, step through the operations list and:
|
|
10101
|
+
// 1) build up the `scope` mapping
|
|
10102
|
+
// 2) recurse into any listener functions
|
|
10103
|
+
for (const op of ops) {
|
|
10104
|
+
switch (op.kind) {
|
|
10105
|
+
case OpKind.Variable:
|
|
10106
|
+
switch (op.variable.kind) {
|
|
10107
|
+
case SemanticVariableKind.Identifier:
|
|
10108
|
+
// This variable represents some kind of identifier which can be used in the template.
|
|
10109
|
+
if (scope.has(op.variable.name)) {
|
|
10110
|
+
continue;
|
|
10111
|
+
}
|
|
10112
|
+
scope.set(op.variable.name, op.xref);
|
|
10113
|
+
break;
|
|
10114
|
+
case SemanticVariableKind.SavedView:
|
|
10115
|
+
// This variable represents a snapshot of the current view context, and can be used to
|
|
10116
|
+
// restore that context within listener functions.
|
|
10117
|
+
savedView = {
|
|
10118
|
+
view: op.variable.view,
|
|
10119
|
+
variable: op.xref,
|
|
10120
|
+
};
|
|
10121
|
+
break;
|
|
10122
|
+
}
|
|
10123
|
+
break;
|
|
10124
|
+
case OpKind.Listener:
|
|
10125
|
+
// Listener functions have separate variable declarations, so process them as a separate
|
|
10126
|
+
// lexical scope.
|
|
10127
|
+
processLexicalScope$1(view, op.handlerOps, savedView);
|
|
10128
|
+
break;
|
|
10129
|
+
}
|
|
10130
|
+
}
|
|
10131
|
+
// Next, use the `scope` mapping to match `ir.LexicalReadExpr` with defined names in the lexical
|
|
10132
|
+
// scope. Also, look for `ir.RestoreViewExpr`s and match them with the snapshotted view context
|
|
10133
|
+
// variable.
|
|
10134
|
+
for (const op of ops) {
|
|
10135
|
+
transformExpressionsInOp(op, expr => {
|
|
10136
|
+
if (expr instanceof LexicalReadExpr) {
|
|
10137
|
+
// `expr` is a read of a name within the lexical scope of this view.
|
|
10138
|
+
// Either that name is defined within the current view, or it represents a property from the
|
|
10139
|
+
// main component context.
|
|
10140
|
+
if (scope.has(expr.name)) {
|
|
10141
|
+
// This was a defined variable in the current scope.
|
|
10142
|
+
return new ReadVariableExpr(scope.get(expr.name));
|
|
10143
|
+
}
|
|
10144
|
+
else {
|
|
10145
|
+
// Reading from the component context.
|
|
10146
|
+
return new ReadPropExpr(new ContextExpr(view.tpl.root.xref), expr.name);
|
|
10147
|
+
}
|
|
10148
|
+
}
|
|
10149
|
+
else if (expr instanceof RestoreViewExpr && typeof expr.view === 'number') {
|
|
10150
|
+
// `ir.RestoreViewExpr` happens in listener functions and restores a saved view from the
|
|
10151
|
+
// parent creation list. We expect to find that we captured the `savedView` previously, and
|
|
10152
|
+
// that it matches the expected view to be restored.
|
|
10153
|
+
if (savedView === null || savedView.view !== expr.view) {
|
|
10154
|
+
throw new Error(`AssertionError: no saved view ${expr.view} from view ${view.xref}`);
|
|
10155
|
+
}
|
|
10156
|
+
expr.view = new ReadVariableExpr(savedView.variable);
|
|
10157
|
+
return expr;
|
|
10158
|
+
}
|
|
10159
|
+
else {
|
|
10160
|
+
return expr;
|
|
10161
|
+
}
|
|
10162
|
+
});
|
|
10163
|
+
}
|
|
10164
|
+
}
|
|
10165
|
+
|
|
10166
|
+
/**
|
|
10167
|
+
* Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
|
|
10168
|
+
* either the `ctx` parameter to component functions (for the current view context) or to variables
|
|
10169
|
+
* that store those contexts (for contexts accessed via the `nextContext()` instruction).
|
|
10170
|
+
*/
|
|
10171
|
+
function phaseResolveContexts(cpl) {
|
|
10172
|
+
for (const view of cpl.views.values()) {
|
|
10173
|
+
processLexicalScope(view, view.create);
|
|
10174
|
+
processLexicalScope(view, view.update);
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
function processLexicalScope(view, ops) {
|
|
10178
|
+
// Track the expressions used to access all available contexts within the current view, by the
|
|
10179
|
+
// view `ir.XrefId`.
|
|
10180
|
+
const scope = new Map();
|
|
10181
|
+
// The current view's context is accessible via the `ctx` parameter.
|
|
10182
|
+
scope.set(view.xref, variable('ctx'));
|
|
10183
|
+
for (const op of ops) {
|
|
10184
|
+
switch (op.kind) {
|
|
10185
|
+
case OpKind.Variable:
|
|
10186
|
+
switch (op.variable.kind) {
|
|
10187
|
+
case SemanticVariableKind.Context:
|
|
10188
|
+
scope.set(op.variable.view, new ReadVariableExpr(op.xref));
|
|
10189
|
+
break;
|
|
10190
|
+
}
|
|
10191
|
+
break;
|
|
10192
|
+
case OpKind.Listener:
|
|
10193
|
+
processLexicalScope(view, op.handlerOps);
|
|
10194
|
+
break;
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10197
|
+
for (const op of ops) {
|
|
10198
|
+
transformExpressionsInOp(op, expr => {
|
|
10199
|
+
if (expr instanceof ContextExpr) {
|
|
10200
|
+
if (!scope.has(expr.view)) {
|
|
10201
|
+
throw new Error(`No context found for reference to view ${expr.view} from view ${view.xref}`);
|
|
10202
|
+
}
|
|
10203
|
+
return scope.get(expr.view);
|
|
10204
|
+
}
|
|
10205
|
+
else {
|
|
10206
|
+
return expr;
|
|
10207
|
+
}
|
|
10208
|
+
});
|
|
10209
|
+
}
|
|
10210
|
+
}
|
|
10211
|
+
|
|
10212
|
+
/**
|
|
10213
|
+
* Run all transformation phases in the correct order against a `ComponentCompilation`. After this
|
|
10214
|
+
* processing, the compilation should be in a state where it can be emitted via `emitTemplateFn`.s
|
|
10215
|
+
*/
|
|
10216
|
+
function transformTemplate(cpl) {
|
|
10217
|
+
phaseGenerateVariables(cpl);
|
|
10218
|
+
phaseResolveNames(cpl);
|
|
10219
|
+
phaseResolveContexts(cpl);
|
|
10220
|
+
phaseLocalRefs(cpl);
|
|
10221
|
+
phaseEmptyElements(cpl);
|
|
10222
|
+
phaseConstCollection(cpl);
|
|
10223
|
+
phaseSlotAllocation(cpl);
|
|
10224
|
+
phaseVarCounting(cpl);
|
|
10225
|
+
phaseGenerateAdvance(cpl);
|
|
10226
|
+
phaseNaming(cpl);
|
|
10227
|
+
phaseReify(cpl);
|
|
10228
|
+
}
|
|
10229
|
+
/**
|
|
10230
|
+
* Compile all views in the given `ComponentCompilation` into the final template function, which may
|
|
10231
|
+
* reference constants defined in a `ConstantPool`.
|
|
10232
|
+
*/
|
|
10233
|
+
function emitTemplateFn(tpl, pool) {
|
|
10234
|
+
const rootFn = emitView(tpl.root);
|
|
10235
|
+
for (const view of tpl.views.values()) {
|
|
10236
|
+
if (view === tpl.root) {
|
|
10237
|
+
continue;
|
|
10238
|
+
}
|
|
10239
|
+
const viewFn = emitView(view);
|
|
10240
|
+
pool.statements.push(viewFn.toDeclStmt(viewFn.name));
|
|
10241
|
+
}
|
|
10242
|
+
return rootFn;
|
|
10243
|
+
}
|
|
10244
|
+
/**
|
|
10245
|
+
* Emit a template function for an individual `ViewCompilation` (which may be either the root view
|
|
10246
|
+
* or an embedded view).
|
|
10247
|
+
*/
|
|
10248
|
+
function emitView(view) {
|
|
10249
|
+
if (view.fnName === null) {
|
|
10250
|
+
throw new Error(`AssertionError: view ${view.xref} is unnamed`);
|
|
10251
|
+
}
|
|
10252
|
+
const createStatements = [];
|
|
10253
|
+
for (const op of view.create) {
|
|
10254
|
+
if (op.kind !== OpKind.Statement) {
|
|
10255
|
+
throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
10256
|
+
}
|
|
10257
|
+
createStatements.push(op.statement);
|
|
10258
|
+
}
|
|
10259
|
+
const updateStatements = [];
|
|
10260
|
+
for (const op of view.update) {
|
|
10261
|
+
if (op.kind !== OpKind.Statement) {
|
|
10262
|
+
throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
10263
|
+
}
|
|
10264
|
+
updateStatements.push(op.statement);
|
|
10265
|
+
}
|
|
10266
|
+
const rf = variable('rf');
|
|
10267
|
+
const createCond = ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, rf, literal(1)), createStatements);
|
|
10268
|
+
const updateCond = ifStmt(new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, rf, literal(2)), updateStatements);
|
|
10269
|
+
return fn([
|
|
10270
|
+
new FnParam('rf'),
|
|
10271
|
+
new FnParam('ctx'),
|
|
10272
|
+
], [createCond, updateCond], /* type */ undefined, /* sourceSpan */ undefined, view.fnName);
|
|
10273
|
+
}
|
|
10274
|
+
|
|
10275
|
+
/**
|
|
10276
|
+
* Compilation-in-progress of a whole component's template, including the main template and any
|
|
10277
|
+
* embedded views or host bindings.
|
|
10278
|
+
*/
|
|
10279
|
+
class ComponentCompilation {
|
|
10280
|
+
constructor(componentName) {
|
|
10281
|
+
this.componentName = componentName;
|
|
10282
|
+
/**
|
|
10283
|
+
* Tracks the next `ir.XrefId` which can be assigned as template structures are ingested.
|
|
10284
|
+
*/
|
|
10285
|
+
this.nextXrefId = 0;
|
|
10286
|
+
/**
|
|
10287
|
+
* Map of view IDs to `ViewCompilation`s.
|
|
10288
|
+
*/
|
|
10289
|
+
this.views = new Map();
|
|
10290
|
+
/**
|
|
10291
|
+
* Constant expressions used by operations within this component's compilation.
|
|
10292
|
+
*
|
|
10293
|
+
* This will eventually become the `consts` array in the component definition.
|
|
10294
|
+
*/
|
|
10295
|
+
this.consts = [];
|
|
10296
|
+
// Allocate the root view.
|
|
10297
|
+
const root = new ViewCompilation(this, this.allocateXrefId(), null);
|
|
10298
|
+
this.views.set(root.xref, root);
|
|
10299
|
+
this.root = root;
|
|
10300
|
+
}
|
|
10301
|
+
/**
|
|
10302
|
+
* Add a `ViewCompilation` for a new embedded view to this compilation.
|
|
10303
|
+
*/
|
|
10304
|
+
allocateView(parent) {
|
|
10305
|
+
const view = new ViewCompilation(this, this.allocateXrefId(), parent);
|
|
10306
|
+
this.views.set(view.xref, view);
|
|
10307
|
+
return view;
|
|
10308
|
+
}
|
|
10309
|
+
/**
|
|
10310
|
+
* Generate a new unique `ir.XrefId`.
|
|
10311
|
+
*/
|
|
10312
|
+
allocateXrefId() {
|
|
10313
|
+
return this.nextXrefId++;
|
|
10314
|
+
}
|
|
10315
|
+
/**
|
|
10316
|
+
* Add a constant `o.Expression` to the compilation and return its index in the `consts` array.
|
|
10317
|
+
*/
|
|
10318
|
+
addConst(newConst) {
|
|
10319
|
+
for (let idx = 0; idx < this.consts.length; idx++) {
|
|
10320
|
+
if (this.consts[idx].isEquivalent(newConst)) {
|
|
10321
|
+
return idx;
|
|
10322
|
+
}
|
|
10323
|
+
}
|
|
10324
|
+
const idx = this.consts.length;
|
|
10325
|
+
this.consts.push(newConst);
|
|
10326
|
+
return idx;
|
|
10327
|
+
}
|
|
10328
|
+
}
|
|
10329
|
+
/**
|
|
10330
|
+
* Compilation-in-progress of an individual view within a template.
|
|
10331
|
+
*/
|
|
10332
|
+
class ViewCompilation {
|
|
10333
|
+
constructor(tpl, xref, parent) {
|
|
10334
|
+
this.tpl = tpl;
|
|
10335
|
+
this.xref = xref;
|
|
10336
|
+
this.parent = parent;
|
|
10337
|
+
/**
|
|
10338
|
+
* Name of the function which will be generated for this view.
|
|
10339
|
+
*
|
|
10340
|
+
* May be `null` if not yet determined.
|
|
10341
|
+
*/
|
|
10342
|
+
this.fnName = null;
|
|
10343
|
+
/**
|
|
10344
|
+
* List of creation operations for this view.
|
|
10345
|
+
*
|
|
10346
|
+
* Creation operations may internally contain other operations, including update operations.
|
|
10347
|
+
*/
|
|
10348
|
+
this.create = new OpList();
|
|
10349
|
+
/**
|
|
10350
|
+
* List of update operations for this view.
|
|
10351
|
+
*/
|
|
10352
|
+
this.update = new OpList();
|
|
10353
|
+
/**
|
|
10354
|
+
* Map of declared variables available within this view to the property on the context object
|
|
10355
|
+
* which they alias.
|
|
10356
|
+
*/
|
|
10357
|
+
this.contextVariables = new Map();
|
|
10358
|
+
/**
|
|
10359
|
+
* Number of declaration slots used within this view, or `null` if slots have not yet been
|
|
10360
|
+
* allocated.
|
|
10361
|
+
*/
|
|
10362
|
+
this.decls = null;
|
|
10363
|
+
/**
|
|
10364
|
+
* Number of variable slots used within this view, or `null` if variables have not yet been
|
|
10365
|
+
* counted.
|
|
10366
|
+
*/
|
|
10367
|
+
this.vars = null;
|
|
10368
|
+
}
|
|
10369
|
+
/**
|
|
10370
|
+
* Iterate over all `ir.Op`s within this view.
|
|
10371
|
+
*
|
|
10372
|
+
* Some operations may have child operations, which this iterator will visit.
|
|
10373
|
+
*/
|
|
10374
|
+
*ops() {
|
|
10375
|
+
for (const op of this.create) {
|
|
10376
|
+
yield op;
|
|
10377
|
+
if (op.kind === OpKind.Listener) {
|
|
10378
|
+
for (const listenerOp of op.handlerOps) {
|
|
10379
|
+
yield listenerOp;
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
for (const op of this.update) {
|
|
10384
|
+
yield op;
|
|
10385
|
+
}
|
|
8221
10386
|
}
|
|
8222
|
-
return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
|
|
8223
10387
|
}
|
|
10388
|
+
|
|
8224
10389
|
/**
|
|
8225
|
-
*
|
|
8226
|
-
*
|
|
8227
|
-
* placeholders
|
|
10390
|
+
* Process a template AST and convert it into a `ComponentCompilation` in the intermediate
|
|
10391
|
+
* representation.
|
|
8228
10392
|
*/
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
}
|
|
10393
|
+
function ingest(componentName, template) {
|
|
10394
|
+
const cpl = new ComponentCompilation(componentName);
|
|
10395
|
+
ingestNodes(cpl.root, template);
|
|
10396
|
+
return cpl;
|
|
10397
|
+
}
|
|
8234
10398
|
/**
|
|
8235
|
-
*
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
* @param input the original css text.
|
|
8251
|
-
*
|
|
8252
|
-
* @returns the css text with specific characters in strings replaced by placeholders.
|
|
8253
|
-
**/
|
|
8254
|
-
function escapeInStrings(input) {
|
|
8255
|
-
let result = input;
|
|
8256
|
-
let currentQuoteChar = null;
|
|
8257
|
-
for (let i = 0; i < result.length; i++) {
|
|
8258
|
-
const char = result[i];
|
|
8259
|
-
if (char === '\\') {
|
|
8260
|
-
i++;
|
|
10399
|
+
* Ingest the nodes of a template AST into the given `ViewCompilation`.
|
|
10400
|
+
*/
|
|
10401
|
+
function ingestNodes(view, template) {
|
|
10402
|
+
for (const node of template) {
|
|
10403
|
+
if (node instanceof Element$1) {
|
|
10404
|
+
ingestElement(view, node);
|
|
10405
|
+
}
|
|
10406
|
+
else if (node instanceof Template) {
|
|
10407
|
+
ingestTemplate(view, node);
|
|
10408
|
+
}
|
|
10409
|
+
else if (node instanceof Text$3) {
|
|
10410
|
+
ingestText(view, node);
|
|
10411
|
+
}
|
|
10412
|
+
else if (node instanceof BoundText) {
|
|
10413
|
+
ingestBoundText(view, node);
|
|
8261
10414
|
}
|
|
8262
10415
|
else {
|
|
8263
|
-
|
|
8264
|
-
// index i is inside a quoted sub-string
|
|
8265
|
-
if (char === currentQuoteChar) {
|
|
8266
|
-
currentQuoteChar = null;
|
|
8267
|
-
}
|
|
8268
|
-
else {
|
|
8269
|
-
const placeholder = ESCAPE_IN_STRING_MAP[char];
|
|
8270
|
-
if (placeholder) {
|
|
8271
|
-
result = `${result.substr(0, i)}${placeholder}${result.substr(i + 1)}`;
|
|
8272
|
-
i += placeholder.length - 1;
|
|
8273
|
-
}
|
|
8274
|
-
}
|
|
8275
|
-
}
|
|
8276
|
-
else if (char === '\'' || char === '"') {
|
|
8277
|
-
currentQuoteChar = char;
|
|
8278
|
-
}
|
|
10416
|
+
throw new Error(`Unsupported template node: ${node.constructor.name}`);
|
|
8279
10417
|
}
|
|
8280
10418
|
}
|
|
8281
|
-
return result;
|
|
8282
10419
|
}
|
|
8283
10420
|
/**
|
|
8284
|
-
*
|
|
8285
|
-
* original representation, this is simply used to revert the changes applied by the
|
|
8286
|
-
* escapeInStrings function.
|
|
8287
|
-
*
|
|
8288
|
-
* For example it reverts the text:
|
|
8289
|
-
* `animation: "my-anim%COLON_IN_PLACEHOLDER%at\"ion" 1s;`
|
|
8290
|
-
* to it's original form of:
|
|
8291
|
-
* `animation: "my-anim:at\"ion" 1s;`
|
|
8292
|
-
*
|
|
8293
|
-
* Note: For the sake of simplicity this function does not check that the placeholders are
|
|
8294
|
-
* actually inside strings as it would anyway be extremely unlikely to find them outside of strings.
|
|
8295
|
-
*
|
|
8296
|
-
* @param input the css text containing the placeholders.
|
|
8297
|
-
*
|
|
8298
|
-
* @returns the css text without the placeholders.
|
|
10421
|
+
* Ingest an element AST from the template into the given `ViewCompilation`.
|
|
8299
10422
|
*/
|
|
8300
|
-
function
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
10423
|
+
function ingestElement(view, element) {
|
|
10424
|
+
const staticAttributes = {};
|
|
10425
|
+
for (const attr of element.attributes) {
|
|
10426
|
+
staticAttributes[attr.name] = attr.value;
|
|
10427
|
+
}
|
|
10428
|
+
const id = view.tpl.allocateXrefId();
|
|
10429
|
+
const startOp = createElementStartOp(element.name, id);
|
|
10430
|
+
view.create.push(startOp);
|
|
10431
|
+
ingestAttributes(startOp, element);
|
|
10432
|
+
ingestBindings(view, startOp, element);
|
|
10433
|
+
ingestReferences(startOp, element);
|
|
10434
|
+
ingestNodes(view, element.children);
|
|
10435
|
+
view.create.push(createElementEndOp(id));
|
|
8305
10436
|
}
|
|
8306
10437
|
/**
|
|
8307
|
-
*
|
|
8308
|
-
* quoted.
|
|
8309
|
-
*
|
|
8310
|
-
* This generates a "canonical" representation of strings which can be used to match strings
|
|
8311
|
-
* which would otherwise only differ because of differently escaped quotes.
|
|
8312
|
-
*
|
|
8313
|
-
* For example it converts the string (assumed to be quoted):
|
|
8314
|
-
* `this \\"is\\" a \\'\\\\'test`
|
|
8315
|
-
* to:
|
|
8316
|
-
* `this "is" a '\\\\'test`
|
|
8317
|
-
* (note that the latter backslashes are not removed as they are not actually escaping the single
|
|
8318
|
-
* quote)
|
|
8319
|
-
*
|
|
8320
|
-
*
|
|
8321
|
-
* @param input the string possibly containing escaped quotes.
|
|
8322
|
-
* @param isQuoted boolean indicating whether the string was quoted inside a bigger string (if not
|
|
8323
|
-
* then it means that it doesn't represent an inner string and thus no unescaping is required)
|
|
8324
|
-
*
|
|
8325
|
-
* @returns the string in the "canonical" representation without escaped quotes.
|
|
10438
|
+
* Ingest an `ng-template` node from the AST into the given `ViewCompilation`.
|
|
8326
10439
|
*/
|
|
8327
|
-
function
|
|
8328
|
-
|
|
10440
|
+
function ingestTemplate(view, tmpl) {
|
|
10441
|
+
const childView = view.tpl.allocateView(view.xref);
|
|
10442
|
+
// TODO: validate the fallback tag name here.
|
|
10443
|
+
const tplOp = createTemplateOp(childView.xref, tmpl.tagName ?? 'ng-template');
|
|
10444
|
+
view.create.push(tplOp);
|
|
10445
|
+
ingestAttributes(tplOp, tmpl);
|
|
10446
|
+
ingestBindings(view, tplOp, tmpl);
|
|
10447
|
+
ingestReferences(tplOp, tmpl);
|
|
10448
|
+
ingestNodes(childView, tmpl.children);
|
|
10449
|
+
for (const { name, value } of tmpl.variables) {
|
|
10450
|
+
childView.contextVariables.set(name, value);
|
|
10451
|
+
}
|
|
8329
10452
|
}
|
|
8330
10453
|
/**
|
|
8331
|
-
*
|
|
8332
|
-
* to create a selector that matches the same as `:host-context()`.
|
|
8333
|
-
*
|
|
8334
|
-
* Given a single context selector `A` we need to output selectors that match on the host and as an
|
|
8335
|
-
* ancestor of the host:
|
|
8336
|
-
*
|
|
8337
|
-
* ```
|
|
8338
|
-
* A <hostMarker>, A<hostMarker> {}
|
|
8339
|
-
* ```
|
|
8340
|
-
*
|
|
8341
|
-
* When there is more than one context selector we also have to create combinations of those
|
|
8342
|
-
* selectors with each other. For example if there are `A` and `B` selectors the output is:
|
|
8343
|
-
*
|
|
8344
|
-
* ```
|
|
8345
|
-
* AB<hostMarker>, AB <hostMarker>, A B<hostMarker>,
|
|
8346
|
-
* B A<hostMarker>, A B <hostMarker>, B A <hostMarker> {}
|
|
8347
|
-
* ```
|
|
8348
|
-
*
|
|
8349
|
-
* And so on...
|
|
8350
|
-
*
|
|
8351
|
-
* @param hostMarker the string that selects the host element.
|
|
8352
|
-
* @param contextSelectors an array of context selectors that will be combined.
|
|
8353
|
-
* @param otherSelectors the rest of the selectors that are not context selectors.
|
|
10454
|
+
* Ingest a literal text node from the AST into the given `ViewCompilation`.
|
|
8354
10455
|
*/
|
|
8355
|
-
function
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
10456
|
+
function ingestText(view, text) {
|
|
10457
|
+
view.create.push(createTextOp(view.tpl.allocateXrefId(), text.value));
|
|
10458
|
+
}
|
|
10459
|
+
/**
|
|
10460
|
+
* Ingest an interpolated text node from the AST into the given `ViewCompilation`.
|
|
10461
|
+
*/
|
|
10462
|
+
function ingestBoundText(view, text) {
|
|
10463
|
+
let value = text.value;
|
|
10464
|
+
if (value instanceof ASTWithSource) {
|
|
10465
|
+
value = value.ast;
|
|
8362
10466
|
}
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
10467
|
+
if (!(value instanceof Interpolation)) {
|
|
10468
|
+
throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${value.constructor.name}`);
|
|
10469
|
+
}
|
|
10470
|
+
const textXref = view.tpl.allocateXrefId();
|
|
10471
|
+
view.create.push(createTextOp(textXref, ''));
|
|
10472
|
+
view.update.push(createInterpolateTextOp(textXref, value.strings, value.expressions.map(expr => convertAst(expr))));
|
|
10473
|
+
}
|
|
10474
|
+
/**
|
|
10475
|
+
* Convert a template AST expression into an output AST expression.
|
|
10476
|
+
*/
|
|
10477
|
+
function convertAst(ast) {
|
|
10478
|
+
if (ast instanceof ASTWithSource) {
|
|
10479
|
+
return convertAst(ast.ast);
|
|
10480
|
+
}
|
|
10481
|
+
else if (ast instanceof PropertyRead) {
|
|
10482
|
+
if (ast.receiver instanceof ImplicitReceiver) {
|
|
10483
|
+
return new LexicalReadExpr(ast.name);
|
|
10484
|
+
}
|
|
10485
|
+
else {
|
|
10486
|
+
return new ReadPropExpr(convertAst(ast.receiver), ast.name);
|
|
8375
10487
|
}
|
|
8376
10488
|
}
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
10489
|
+
else if (ast instanceof Call) {
|
|
10490
|
+
if (ast.receiver instanceof ImplicitReceiver) {
|
|
10491
|
+
throw new Error(`Unexpected ImplicitReceiver`);
|
|
10492
|
+
}
|
|
10493
|
+
else {
|
|
10494
|
+
return new InvokeFunctionExpr(convertAst(ast.receiver), ast.args.map(arg => convertAst(arg)));
|
|
10495
|
+
}
|
|
10496
|
+
}
|
|
10497
|
+
else {
|
|
10498
|
+
throw new Error(`Unhandled expression type: ${ast.constructor.name}`);
|
|
10499
|
+
}
|
|
8384
10500
|
}
|
|
8385
10501
|
/**
|
|
8386
|
-
*
|
|
8387
|
-
*
|
|
8388
|
-
*
|
|
8389
|
-
* For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the
|
|
8390
|
-
* newly added groups will be clones of the original.
|
|
8391
|
-
*
|
|
8392
|
-
* @param groups An array of groups of strings that will be repeated. This array is mutated
|
|
8393
|
-
* in-place.
|
|
8394
|
-
* @param multiples The number of times the current groups should appear.
|
|
10502
|
+
* Process all of the attributes on an element-like structure in the template AST and convert them
|
|
10503
|
+
* to their IR representation.
|
|
8395
10504
|
*/
|
|
8396
|
-
function
|
|
8397
|
-
|
|
8398
|
-
for (
|
|
8399
|
-
|
|
8400
|
-
|
|
10505
|
+
function ingestAttributes(op, element) {
|
|
10506
|
+
assertIsElementAttributes(op.attributes);
|
|
10507
|
+
for (const attr of element.attributes) {
|
|
10508
|
+
op.attributes.add(ElementAttributeKind.Attribute, attr.name, literal(attr.value));
|
|
10509
|
+
}
|
|
10510
|
+
for (const input of element.inputs) {
|
|
10511
|
+
op.attributes.add(ElementAttributeKind.Binding, input.name, null);
|
|
10512
|
+
}
|
|
10513
|
+
for (const output of element.outputs) {
|
|
10514
|
+
op.attributes.add(ElementAttributeKind.Binding, output.name, null);
|
|
10515
|
+
}
|
|
10516
|
+
if (element instanceof Template) {
|
|
10517
|
+
for (const attr of element.templateAttrs) {
|
|
10518
|
+
// TODO: what do we do about the value here?
|
|
10519
|
+
op.attributes.add(ElementAttributeKind.Template, attr.name, null);
|
|
10520
|
+
}
|
|
10521
|
+
}
|
|
10522
|
+
}
|
|
10523
|
+
/**
|
|
10524
|
+
* Process all of the bindings on an element-like structure in the template AST and convert them
|
|
10525
|
+
* to their IR representation.
|
|
10526
|
+
*/
|
|
10527
|
+
function ingestBindings(view, op, element) {
|
|
10528
|
+
if (element instanceof Template) {
|
|
10529
|
+
for (const attr of element.templateAttrs) {
|
|
10530
|
+
if (typeof attr.value === 'string') {
|
|
10531
|
+
throw new Error(`TODO: unhandled static attribute bindings (is this a thing?)`);
|
|
10532
|
+
}
|
|
10533
|
+
else {
|
|
10534
|
+
view.update.push(createPropertyOp(op.xref, attr.name, convertAst(attr.value)));
|
|
10535
|
+
}
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10538
|
+
else {
|
|
10539
|
+
for (const input of element.inputs) {
|
|
10540
|
+
view.update.push(createPropertyOp(op.xref, input.name, convertAst(input.value)));
|
|
8401
10541
|
}
|
|
10542
|
+
for (const output of element.outputs) {
|
|
10543
|
+
const listenerOp = createListenerOp(op.xref, output.name);
|
|
10544
|
+
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(output.handler))));
|
|
10545
|
+
view.create.push(listenerOp);
|
|
10546
|
+
}
|
|
10547
|
+
}
|
|
10548
|
+
}
|
|
10549
|
+
/**
|
|
10550
|
+
* Process all of the local references on an element-like structure in the template AST and convert
|
|
10551
|
+
* them to their IR representation.
|
|
10552
|
+
*/
|
|
10553
|
+
function ingestReferences(op, element) {
|
|
10554
|
+
assertIsArray(op.localRefs);
|
|
10555
|
+
for (const { name, value } of element.references) {
|
|
10556
|
+
op.localRefs.push({
|
|
10557
|
+
name,
|
|
10558
|
+
target: value,
|
|
10559
|
+
});
|
|
10560
|
+
}
|
|
10561
|
+
}
|
|
10562
|
+
/**
|
|
10563
|
+
* Assert that the given value is an array.
|
|
10564
|
+
*/
|
|
10565
|
+
function assertIsArray(value) {
|
|
10566
|
+
if (!Array.isArray(value)) {
|
|
10567
|
+
throw new Error(`AssertionError: expected an array`);
|
|
8402
10568
|
}
|
|
8403
10569
|
}
|
|
8404
10570
|
|
|
10571
|
+
const USE_TEMPLATE_PIPELINE = false;
|
|
10572
|
+
|
|
8405
10573
|
/**
|
|
8406
10574
|
* Parses string representation of a style and converts it into object literal.
|
|
8407
10575
|
*
|
|
@@ -10762,41 +12930,6 @@ class RecursiveVisitor {
|
|
|
10762
12930
|
}
|
|
10763
12931
|
}
|
|
10764
12932
|
|
|
10765
|
-
var TagContentType;
|
|
10766
|
-
(function (TagContentType) {
|
|
10767
|
-
TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
|
10768
|
-
TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
|
10769
|
-
TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
|
10770
|
-
})(TagContentType || (TagContentType = {}));
|
|
10771
|
-
function splitNsName(elementName) {
|
|
10772
|
-
if (elementName[0] != ':') {
|
|
10773
|
-
return [null, elementName];
|
|
10774
|
-
}
|
|
10775
|
-
const colonIndex = elementName.indexOf(':', 1);
|
|
10776
|
-
if (colonIndex === -1) {
|
|
10777
|
-
throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
|
10778
|
-
}
|
|
10779
|
-
return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
|
10780
|
-
}
|
|
10781
|
-
// `<ng-container>` tags work the same regardless the namespace
|
|
10782
|
-
function isNgContainer(tagName) {
|
|
10783
|
-
return splitNsName(tagName)[1] === 'ng-container';
|
|
10784
|
-
}
|
|
10785
|
-
// `<ng-content>` tags work the same regardless the namespace
|
|
10786
|
-
function isNgContent(tagName) {
|
|
10787
|
-
return splitNsName(tagName)[1] === 'ng-content';
|
|
10788
|
-
}
|
|
10789
|
-
// `<ng-template>` tags work the same regardless the namespace
|
|
10790
|
-
function isNgTemplate(tagName) {
|
|
10791
|
-
return splitNsName(tagName)[1] === 'ng-template';
|
|
10792
|
-
}
|
|
10793
|
-
function getNsPrefix(fullName) {
|
|
10794
|
-
return fullName === null ? null : splitNsName(fullName)[0];
|
|
10795
|
-
}
|
|
10796
|
-
function mergeNsAndName(prefix, localName) {
|
|
10797
|
-
return prefix ? `:${prefix}:${localName}` : localName;
|
|
10798
|
-
}
|
|
10799
|
-
|
|
10800
12933
|
class ElementSchemaRegistry {
|
|
10801
12934
|
}
|
|
10802
12935
|
|
|
@@ -18745,34 +20878,56 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
|
18745
20878
|
const templateTypeName = meta.name;
|
|
18746
20879
|
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
|
18747
20880
|
const changeDetection = meta.changeDetection;
|
|
18748
|
-
|
|
18749
|
-
|
|
18750
|
-
|
|
18751
|
-
|
|
18752
|
-
|
|
18753
|
-
|
|
18754
|
-
|
|
18755
|
-
|
|
18756
|
-
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
|
|
18763
|
-
|
|
18764
|
-
|
|
18765
|
-
|
|
18766
|
-
|
|
18767
|
-
|
|
18768
|
-
|
|
18769
|
-
//
|
|
18770
|
-
|
|
18771
|
-
|
|
18772
|
-
}
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
20881
|
+
// Template compilation is currently conditional as we're in the process of rewriting it.
|
|
20882
|
+
if (!USE_TEMPLATE_PIPELINE) {
|
|
20883
|
+
// This is the main path currently used in compilation, which compiles the template with the
|
|
20884
|
+
// legacy `TemplateDefinitionBuilder`.
|
|
20885
|
+
const template = meta.template;
|
|
20886
|
+
const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, Identifiers.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
|
20887
|
+
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
|
20888
|
+
// We need to provide this so that dynamically generated components know what
|
|
20889
|
+
// projected content blocks to pass through to the component when it is
|
|
20890
|
+
// instantiated.
|
|
20891
|
+
const ngContentSelectors = templateBuilder.getNgContentSelectors();
|
|
20892
|
+
if (ngContentSelectors) {
|
|
20893
|
+
definitionMap.set('ngContentSelectors', ngContentSelectors);
|
|
20894
|
+
}
|
|
20895
|
+
// e.g. `decls: 2`
|
|
20896
|
+
// definitionMap.set('decls', o.literal(tpl.root.decls!));
|
|
20897
|
+
definitionMap.set('decls', literal(templateBuilder.getConstCount()));
|
|
20898
|
+
// e.g. `vars: 2`
|
|
20899
|
+
// definitionMap.set('vars', o.literal(tpl.root.vars!));
|
|
20900
|
+
definitionMap.set('vars', literal(templateBuilder.getVarCount()));
|
|
20901
|
+
// Generate `consts` section of ComponentDef:
|
|
20902
|
+
// - either as an array:
|
|
20903
|
+
// `consts: [['one', 'two'], ['three', 'four']]`
|
|
20904
|
+
// - or as a factory function in case additional statements are present (to support i18n):
|
|
20905
|
+
// `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0];
|
|
20906
|
+
// }`
|
|
20907
|
+
const { constExpressions, prepareStatements } = templateBuilder.getConsts();
|
|
20908
|
+
if (constExpressions.length > 0) {
|
|
20909
|
+
let constsExpr = literalArr(constExpressions);
|
|
20910
|
+
// Prepare statements are present - turn `consts` into a function.
|
|
20911
|
+
if (prepareStatements.length > 0) {
|
|
20912
|
+
constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
|
|
20913
|
+
}
|
|
20914
|
+
definitionMap.set('consts', constsExpr);
|
|
20915
|
+
}
|
|
20916
|
+
definitionMap.set('template', templateFunctionExpression);
|
|
20917
|
+
}
|
|
20918
|
+
else {
|
|
20919
|
+
// This path compiles the template using the prototype template pipeline. First the template is
|
|
20920
|
+
// ingested into IR:
|
|
20921
|
+
const tpl = ingest(meta.name, meta.template.nodes);
|
|
20922
|
+
// Then the IR is transformed to prepare it for cod egeneration.
|
|
20923
|
+
transformTemplate(tpl);
|
|
20924
|
+
// Finally we emit the template function:
|
|
20925
|
+
const templateFn = emitTemplateFn(tpl, constantPool);
|
|
20926
|
+
definitionMap.set('template', templateFn);
|
|
20927
|
+
definitionMap.set('decls', literal(tpl.root.decls));
|
|
20928
|
+
definitionMap.set('vars', literal(tpl.root.vars));
|
|
20929
|
+
definitionMap.set('consts', literalArr(tpl.consts));
|
|
20930
|
+
}
|
|
18776
20931
|
if (meta.declarations.length > 0) {
|
|
18777
20932
|
definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode));
|
|
18778
20933
|
}
|
|
@@ -19904,7 +22059,7 @@ function publishFacade(global) {
|
|
|
19904
22059
|
* @description
|
|
19905
22060
|
* Entry point for all public APIs of the compiler package.
|
|
19906
22061
|
*/
|
|
19907
|
-
const VERSION = new Version('16.0.0-
|
|
22062
|
+
const VERSION = new Version('16.0.0-rc.1');
|
|
19908
22063
|
|
|
19909
22064
|
class CompilerConfig {
|
|
19910
22065
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -21828,7 +23983,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
21828
23983
|
function compileDeclareClassMetadata(metadata) {
|
|
21829
23984
|
const definitionMap = new DefinitionMap();
|
|
21830
23985
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
21831
|
-
definitionMap.set('version', literal('16.0.0-
|
|
23986
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
21832
23987
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
21833
23988
|
definitionMap.set('type', metadata.type);
|
|
21834
23989
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -21931,7 +24086,7 @@ function compileDeclareDirectiveFromMetadata(meta) {
|
|
|
21931
24086
|
function createDirectiveDefinitionMap(meta) {
|
|
21932
24087
|
const definitionMap = new DefinitionMap();
|
|
21933
24088
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
21934
|
-
definitionMap.set('version', literal('16.0.0-
|
|
24089
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
21935
24090
|
// e.g. `type: MyDirective`
|
|
21936
24091
|
definitionMap.set('type', meta.type.value);
|
|
21937
24092
|
if (meta.isStandalone) {
|
|
@@ -22156,7 +24311,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
22156
24311
|
function compileDeclareFactoryFunction(meta) {
|
|
22157
24312
|
const definitionMap = new DefinitionMap();
|
|
22158
24313
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
22159
|
-
definitionMap.set('version', literal('16.0.0-
|
|
24314
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
22160
24315
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
22161
24316
|
definitionMap.set('type', meta.type.value);
|
|
22162
24317
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -22191,7 +24346,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
22191
24346
|
function createInjectableDefinitionMap(meta) {
|
|
22192
24347
|
const definitionMap = new DefinitionMap();
|
|
22193
24348
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
22194
|
-
definitionMap.set('version', literal('16.0.0-
|
|
24349
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
22195
24350
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
22196
24351
|
definitionMap.set('type', meta.type.value);
|
|
22197
24352
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -22242,7 +24397,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
22242
24397
|
function createInjectorDefinitionMap(meta) {
|
|
22243
24398
|
const definitionMap = new DefinitionMap();
|
|
22244
24399
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
22245
|
-
definitionMap.set('version', literal('16.0.0-
|
|
24400
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
22246
24401
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
22247
24402
|
definitionMap.set('type', meta.type.value);
|
|
22248
24403
|
definitionMap.set('providers', meta.providers);
|
|
@@ -22272,7 +24427,7 @@ function compileDeclareNgModuleFromMetadata(meta) {
|
|
|
22272
24427
|
function createNgModuleDefinitionMap(meta) {
|
|
22273
24428
|
const definitionMap = new DefinitionMap();
|
|
22274
24429
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
22275
|
-
definitionMap.set('version', literal('16.0.0-
|
|
24430
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
22276
24431
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
22277
24432
|
definitionMap.set('type', meta.type.value);
|
|
22278
24433
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -22323,7 +24478,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
22323
24478
|
function createPipeDefinitionMap(meta) {
|
|
22324
24479
|
const definitionMap = new DefinitionMap();
|
|
22325
24480
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
22326
|
-
definitionMap.set('version', literal('16.0.0-
|
|
24481
|
+
definitionMap.set('version', literal('16.0.0-rc.1'));
|
|
22327
24482
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
22328
24483
|
// e.g. `type: MyPipe`
|
|
22329
24484
|
definitionMap.set('type', meta.type.value);
|