refactor(ivy): move classMap interpolation logic internally (#31211)
Adds the new `classMapInterpolate1` through `classMapInterpolate8` instructions which handle interpolations inside the `class` attribute and moves the interpolation logic internally. This allows us to remove the `interpolationX` instructions in a follow-up PR. These changes also add an error if an interpolation is encountered inside a `style` tag (e.g. `style="width: {{value}}"`). Up until now this would actually generate valid instructions, because `styleMap` goes through the same code path as `classMap` which does support interpolation. At runtime, however, `styleMap` would set invalid styles that look like `<div style="0:w;1:i;2:d;3:t;4:h;5::;7:1;">`. In `ViewEngine` interpolations inside `style` weren't supported either, however there we'd output invalid styles like `<div style="unsafe">`, even if the content was trusted. PR Close #31211
This commit is contained in:

committed by
Alex Rickabaugh

parent
dca713c087
commit
02491a6ce8
@ -72,8 +72,46 @@ export class Identifiers {
|
||||
|
||||
static classMap: o.ExternalReference = {name: 'ɵɵclassMap', moduleName: CORE};
|
||||
|
||||
static classMapInterpolate1:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate1', moduleName: CORE};
|
||||
static classMapInterpolate2:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate2', moduleName: CORE};
|
||||
static classMapInterpolate3:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate3', moduleName: CORE};
|
||||
static classMapInterpolate4:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate4', moduleName: CORE};
|
||||
static classMapInterpolate5:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate5', moduleName: CORE};
|
||||
static classMapInterpolate6:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate6', moduleName: CORE};
|
||||
static classMapInterpolate7:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate7', moduleName: CORE};
|
||||
static classMapInterpolate8:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolate8', moduleName: CORE};
|
||||
static classMapInterpolateV:
|
||||
o.ExternalReference = {name: 'ɵɵclassMapInterpolateV', moduleName: CORE};
|
||||
|
||||
static styleProp: o.ExternalReference = {name: 'ɵɵstyleProp', moduleName: CORE};
|
||||
|
||||
static stylePropInterpolate1:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate1', moduleName: CORE};
|
||||
static stylePropInterpolate2:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate2', moduleName: CORE};
|
||||
static stylePropInterpolate3:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate3', moduleName: CORE};
|
||||
static stylePropInterpolate4:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate4', moduleName: CORE};
|
||||
static stylePropInterpolate5:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate5', moduleName: CORE};
|
||||
static stylePropInterpolate6:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate6', moduleName: CORE};
|
||||
static stylePropInterpolate7:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate7', moduleName: CORE};
|
||||
static stylePropInterpolate8:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolate8', moduleName: CORE};
|
||||
static stylePropInterpolateV:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
|
||||
|
||||
static stylingApply: o.ExternalReference = {name: 'ɵɵstylingApply', moduleName: CORE};
|
||||
|
||||
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
|
||||
|
@ -12,7 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
|
||||
import {BindingForm, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
||||
import * as core from '../../core';
|
||||
import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
|
||||
import {AST, Interpolation, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseError, ParseSourceSpan, typeSourceSpan} from '../../parse_util';
|
||||
@ -28,7 +28,7 @@ import {Render3ParseResult} from '../r3_template_transform';
|
||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
||||
|
||||
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api';
|
||||
import {Instruction, StylingBuilder} from './styling_builder';
|
||||
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
||||
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
|
||||
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
||||
|
||||
@ -777,8 +777,8 @@ function bindingFn(implicit: any, value: AST) {
|
||||
}
|
||||
|
||||
function createStylingStmt(
|
||||
instruction: Instruction, bindingContext: any, bindingFn: Function): o.Statement {
|
||||
const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr);
|
||||
instruction: StylingInstruction, bindingContext: any, bindingFn: Function): o.Statement {
|
||||
const params = instruction.params(value => bindingFn(bindingContext, value).currValExpr);
|
||||
return o.importExpr(instruction.reference, null, instruction.sourceSpan)
|
||||
.callFn(params, instruction.sourceSpan)
|
||||
.toStmt();
|
||||
|
@ -11,23 +11,26 @@ import {AST, BindingType, Interpolation} from '../../expression_parser/ast';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../parse_util';
|
||||
import {isEmptyExpression} from '../../template_parser/template_parser';
|
||||
import {error} from '../../util';
|
||||
import * as t from '../r3_ast';
|
||||
import {Identifiers as R3} from '../r3_identifiers';
|
||||
|
||||
import {parse as parseStyle} from './style_parser';
|
||||
import {compilerIsNewStylingInUse} from './styling_state';
|
||||
import {ValueConverter} from './template';
|
||||
import {getInterpolationArgsLength} from './util';
|
||||
|
||||
const IMPORTANT_FLAG = '!important';
|
||||
|
||||
/**
|
||||
* A styling expression summary that is to be processed by the compiler
|
||||
*/
|
||||
export interface Instruction {
|
||||
export interface StylingInstruction {
|
||||
sourceSpan: ParseSourceSpan|null;
|
||||
reference: o.ExternalReference;
|
||||
allocateBindingSlots: number;
|
||||
buildParams(convertFn: (value: any) => o.Expression): o.Expression[];
|
||||
supportsInterpolation?: boolean;
|
||||
params: ((convertFn: (value: any) => o.Expression | o.Expression[]) => o.Expression[]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -261,13 +264,13 @@ export class StylingBuilder {
|
||||
*/
|
||||
buildHostAttrsInstruction(
|
||||
sourceSpan: ParseSourceSpan|null, attrs: o.Expression[],
|
||||
constantPool: ConstantPool): Instruction|null {
|
||||
constantPool: ConstantPool): StylingInstruction|null {
|
||||
if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
|
||||
return {
|
||||
sourceSpan,
|
||||
reference: R3.elementHostAttrs,
|
||||
allocateBindingSlots: 0,
|
||||
buildParams: () => {
|
||||
params: () => {
|
||||
// params => elementHostAttrs(attrs)
|
||||
this.populateInitialStylingAttrs(attrs);
|
||||
const attrArray = !attrs.some(attr => attr instanceof o.WrappedNodeExpr) ?
|
||||
@ -286,14 +289,14 @@ export class StylingBuilder {
|
||||
* The instruction generation code below is used for producing the AOT statement code which is
|
||||
* responsible for registering style/class bindings to an element.
|
||||
*/
|
||||
buildStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool): Instruction
|
||||
|null {
|
||||
buildStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||
StylingInstruction|null {
|
||||
if (this.hasBindings) {
|
||||
return {
|
||||
sourceSpan,
|
||||
allocateBindingSlots: 0,
|
||||
reference: R3.styling,
|
||||
buildParams: () => {
|
||||
params: () => {
|
||||
// a string array of every style-based binding
|
||||
const styleBindingProps =
|
||||
this._singleStyleInputs ? this._singleStyleInputs.map(i => o.literal(i.name)) : [];
|
||||
@ -344,7 +347,7 @@ export class StylingBuilder {
|
||||
* The instruction data will contain all expressions for `classMap` to function
|
||||
* which includes the `[class]` expression params.
|
||||
*/
|
||||
buildClassMapInstruction(valueConverter: ValueConverter): Instruction|null {
|
||||
buildClassMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
|
||||
if (this._classMapInput) {
|
||||
return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
|
||||
}
|
||||
@ -357,7 +360,7 @@ export class StylingBuilder {
|
||||
* The instruction data will contain all expressions for `styleMap` to function
|
||||
* which includes the `[style]` expression params.
|
||||
*/
|
||||
buildStyleMapInstruction(valueConverter: ValueConverter): Instruction|null {
|
||||
buildStyleMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
|
||||
if (this._styleMapInput) {
|
||||
return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
|
||||
}
|
||||
@ -365,7 +368,8 @@ export class StylingBuilder {
|
||||
}
|
||||
|
||||
private _buildMapBasedInstruction(
|
||||
valueConverter: ValueConverter, isClassBased: boolean, stylingInput: BoundStylingEntry) {
|
||||
valueConverter: ValueConverter, isClassBased: boolean,
|
||||
stylingInput: BoundStylingEntry): StylingInstruction {
|
||||
let totalBindingSlotsRequired = 0;
|
||||
if (compilerIsNewStylingInUse()) {
|
||||
// the old implementation does not reserve slot values for
|
||||
@ -377,27 +381,42 @@ export class StylingBuilder {
|
||||
// be evaluated (the AST visit call) during creation time so that any
|
||||
// pipes can be picked up in time before the template is built
|
||||
const mapValue = stylingInput.value.visit(valueConverter);
|
||||
if (mapValue instanceof Interpolation) {
|
||||
let reference: o.ExternalReference;
|
||||
if (mapValue instanceof Interpolation && isClassBased) {
|
||||
totalBindingSlotsRequired += mapValue.expressions.length;
|
||||
reference = getClassMapInterpolationExpression(mapValue);
|
||||
} else {
|
||||
reference = isClassBased ? R3.classMap : R3.styleMap;
|
||||
}
|
||||
|
||||
const reference = isClassBased ? R3.classMap : R3.styleMap;
|
||||
return {
|
||||
sourceSpan: stylingInput.sourceSpan,
|
||||
reference,
|
||||
allocateBindingSlots: totalBindingSlotsRequired,
|
||||
buildParams: (convertFn: (value: any) => o.Expression) => { return [convertFn(mapValue)]; }
|
||||
supportsInterpolation: isClassBased,
|
||||
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
|
||||
const convertResult = convertFn(mapValue);
|
||||
return Array.isArray(convertResult) ? convertResult : [convertResult];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _buildSingleInputs(
|
||||
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
|
||||
allowUnits: boolean, valueConverter: ValueConverter): Instruction[] {
|
||||
allowUnits: boolean, valueConverter: ValueConverter,
|
||||
getInterpolationExpressionFn?: (value: Interpolation) => o.ExternalReference):
|
||||
StylingInstruction[] {
|
||||
let totalBindingSlotsRequired = 0;
|
||||
return inputs.map(input => {
|
||||
const bindingIndex: number = mapIndex.get(input.name !) !;
|
||||
const value = input.value.visit(valueConverter);
|
||||
totalBindingSlotsRequired += (value instanceof Interpolation) ? value.expressions.length : 0;
|
||||
if (value instanceof Interpolation) {
|
||||
totalBindingSlotsRequired += value.expressions.length;
|
||||
|
||||
if (getInterpolationExpressionFn) {
|
||||
reference = getInterpolationExpressionFn(value);
|
||||
}
|
||||
}
|
||||
if (compilerIsNewStylingInUse()) {
|
||||
// the old implementation does not reserve slot values for
|
||||
// binding entries. The new one does.
|
||||
@ -405,14 +424,18 @@ export class StylingBuilder {
|
||||
}
|
||||
return {
|
||||
sourceSpan: input.sourceSpan,
|
||||
supportsInterpolation: !!getInterpolationExpressionFn,
|
||||
allocateBindingSlots: totalBindingSlotsRequired, reference,
|
||||
buildParams: (convertFn: (value: any) => o.Expression) => {
|
||||
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
|
||||
// min params => stylingProp(elmIndex, bindingIndex, value)
|
||||
// max params => stylingProp(elmIndex, bindingIndex, value, overrideFlag)
|
||||
const params: o.Expression[] = [];
|
||||
params.push(o.literal(bindingIndex));
|
||||
params.push(convertFn(value));
|
||||
|
||||
const params: o.Expression[] = [o.literal(bindingIndex)];
|
||||
const convertResult = convertFn(value);
|
||||
if (Array.isArray(convertResult)) {
|
||||
params.push(...convertResult);
|
||||
} else {
|
||||
params.push(convertResult);
|
||||
}
|
||||
if (allowUnits) {
|
||||
if (input.unit) {
|
||||
params.push(o.literal(input.unit));
|
||||
@ -431,7 +454,7 @@ export class StylingBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
private _buildClassInputs(valueConverter: ValueConverter): Instruction[] {
|
||||
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
||||
if (this._singleClassInputs) {
|
||||
return this._buildSingleInputs(
|
||||
R3.classProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
|
||||
@ -439,29 +462,30 @@ export class StylingBuilder {
|
||||
return [];
|
||||
}
|
||||
|
||||
private _buildStyleInputs(valueConverter: ValueConverter): Instruction[] {
|
||||
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
||||
if (this._singleStyleInputs) {
|
||||
return this._buildSingleInputs(
|
||||
R3.styleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter);
|
||||
R3.styleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter,
|
||||
getStylePropInterpolationExpression);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private _buildApplyFn(): Instruction {
|
||||
private _buildApplyFn(): StylingInstruction {
|
||||
return {
|
||||
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
||||
reference: R3.stylingApply,
|
||||
allocateBindingSlots: 0,
|
||||
buildParams: () => { return []; }
|
||||
params: () => { return []; }
|
||||
};
|
||||
}
|
||||
|
||||
private _buildSanitizerFn() {
|
||||
private _buildSanitizerFn(): StylingInstruction {
|
||||
return {
|
||||
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
|
||||
reference: R3.styleSanitizer,
|
||||
allocateBindingSlots: 0,
|
||||
buildParams: () => [o.importExpr(R3.defaultStyleSanitizer)]
|
||||
params: () => [o.importExpr(R3.defaultStyleSanitizer)]
|
||||
};
|
||||
}
|
||||
|
||||
@ -470,7 +494,7 @@ export class StylingBuilder {
|
||||
* into the update block of a template function or a directive hostBindings function.
|
||||
*/
|
||||
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
||||
const instructions: Instruction[] = [];
|
||||
const instructions: StylingInstruction[] = [];
|
||||
if (this.hasBindings) {
|
||||
if (compilerIsNewStylingInUse() && this._useDefaultSanitizer) {
|
||||
instructions.push(this._buildSanitizerFn());
|
||||
@ -548,3 +572,61 @@ export function parseProperty(name: string):
|
||||
|
||||
return {property, unit, hasOverrideFlag};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction to generate for an interpolated class map.
|
||||
* @param interpolation An Interpolation AST
|
||||
*/
|
||||
function getClassMapInterpolationExpression(interpolation: Interpolation): o.ExternalReference {
|
||||
switch (getInterpolationArgsLength(interpolation)) {
|
||||
case 1:
|
||||
return R3.classMap;
|
||||
case 3:
|
||||
return R3.classMapInterpolate1;
|
||||
case 5:
|
||||
return R3.classMapInterpolate2;
|
||||
case 7:
|
||||
return R3.classMapInterpolate3;
|
||||
case 9:
|
||||
return R3.classMapInterpolate4;
|
||||
case 11:
|
||||
return R3.classMapInterpolate5;
|
||||
case 13:
|
||||
return R3.classMapInterpolate6;
|
||||
case 15:
|
||||
return R3.classMapInterpolate7;
|
||||
case 17:
|
||||
return R3.classMapInterpolate8;
|
||||
default:
|
||||
return R3.classMapInterpolateV;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction to generate for an interpolated style prop.
|
||||
* @param interpolation An Interpolation AST
|
||||
*/
|
||||
function getStylePropInterpolationExpression(interpolation: Interpolation) {
|
||||
switch (getInterpolationArgsLength(interpolation)) {
|
||||
case 1:
|
||||
return R3.styleProp;
|
||||
case 3:
|
||||
return R3.stylePropInterpolate1;
|
||||
case 5:
|
||||
return R3.stylePropInterpolate2;
|
||||
case 7:
|
||||
return R3.stylePropInterpolate3;
|
||||
case 9:
|
||||
return R3.stylePropInterpolate4;
|
||||
case 11:
|
||||
return R3.stylePropInterpolate5;
|
||||
case 13:
|
||||
return R3.stylePropInterpolate6;
|
||||
case 15:
|
||||
return R3.stylePropInterpolate7;
|
||||
case 17:
|
||||
return R3.stylePropInterpolate8;
|
||||
default:
|
||||
return R3.stylePropInterpolateV;
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ import {I18nContext} from './i18n/context';
|
||||
import {I18nMetaVisitor} from './i18n/meta';
|
||||
import {getSerializedI18nContent} from './i18n/serializer';
|
||||
import {I18N_ICU_MAPPING_PREFIX, TRANSLATION_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
|
||||
import {Instruction, StylingBuilder} from './styling_builder';
|
||||
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, chainedInstruction, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
||||
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
||||
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, chainedInstruction, getAttrsForDirectiveMatching, getInterpolationArgsLength, invalid, trimTrailingNulls, unsupported} from './util';
|
||||
|
||||
|
||||
|
||||
@ -1067,14 +1067,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
|
||||
private processStylingInstruction(
|
||||
elementIndex: number, instruction: Instruction|null, createMode: boolean) {
|
||||
elementIndex: number, instruction: StylingInstruction|null, createMode: boolean) {
|
||||
if (instruction) {
|
||||
const paramsFn = () => instruction.buildParams(value => this.convertPropertyBinding(value));
|
||||
if (createMode) {
|
||||
this.creationInstruction(instruction.sourceSpan, instruction.reference, paramsFn);
|
||||
this.creationInstruction(instruction.sourceSpan, instruction.reference, () => {
|
||||
return instruction.params(value => this.convertPropertyBinding(value)) as o.Expression[];
|
||||
});
|
||||
} else {
|
||||
this.updateInstruction(
|
||||
elementIndex, instruction.sourceSpan, instruction.reference, paramsFn);
|
||||
this.updateInstruction(elementIndex, instruction.sourceSpan, instruction.reference, () => {
|
||||
return instruction
|
||||
.params(value => {
|
||||
return (instruction.supportsInterpolation && value instanceof Interpolation) ?
|
||||
this.getUpdateInstructionArguments(value) :
|
||||
this.convertPropertyBinding(value);
|
||||
}) as o.Expression[];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1150,12 +1157,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
|
||||
private convertPropertyBinding(value: AST): o.Expression {
|
||||
const interpolationFn =
|
||||
value instanceof Interpolation ? interpolate : () => error('Unexpected interpolation');
|
||||
|
||||
const convertedPropertyBinding = convertPropertyBinding(
|
||||
this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.TrySimple,
|
||||
interpolationFn);
|
||||
() => error('Unexpected interpolation'));
|
||||
const valExpr = convertedPropertyBinding.currValExpr;
|
||||
this._tempVariables.push(...convertedPropertyBinding.stmts);
|
||||
|
||||
@ -1751,31 +1755,6 @@ function getNgProjectAsLiteral(attribute: t.TextAttribute): o.Expression[] {
|
||||
return [o.literal(core.AttributeMarker.ProjectAs), asLiteral(parsedR3Selector)];
|
||||
}
|
||||
|
||||
function interpolate(args: o.Expression[]): o.Expression {
|
||||
args = args.slice(1); // Ignore the length prefix added for render2
|
||||
switch (args.length) {
|
||||
case 3:
|
||||
return o.importExpr(R3.interpolation1).callFn(args);
|
||||
case 5:
|
||||
return o.importExpr(R3.interpolation2).callFn(args);
|
||||
case 7:
|
||||
return o.importExpr(R3.interpolation3).callFn(args);
|
||||
case 9:
|
||||
return o.importExpr(R3.interpolation4).callFn(args);
|
||||
case 11:
|
||||
return o.importExpr(R3.interpolation5).callFn(args);
|
||||
case 13:
|
||||
return o.importExpr(R3.interpolation6).callFn(args);
|
||||
case 15:
|
||||
return o.importExpr(R3.interpolation7).callFn(args);
|
||||
case 17:
|
||||
return o.importExpr(R3.interpolation8).callFn(args);
|
||||
}
|
||||
(args.length >= 19 && args.length % 2 == 1) ||
|
||||
error(`Invalid interpolation argument length ${args.length}`);
|
||||
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction to generate for an interpolated property
|
||||
* @param interpolation An Interpolation AST
|
||||
@ -1861,22 +1840,6 @@ function getTextInterpolationExpression(interpolation: Interpolation): o.Externa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments expected to be passed to a generated instruction in the case of
|
||||
* interpolation instructions.
|
||||
* @param interpolation An interpolation ast
|
||||
*/
|
||||
function getInterpolationArgsLength(interpolation: Interpolation) {
|
||||
const {expressions, strings} = interpolation;
|
||||
if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
|
||||
// If the interpolation has one interpolated value, but the prefix and suffix are both empty
|
||||
// strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
|
||||
// `textInterpolate`.
|
||||
return 1;
|
||||
} else {
|
||||
return expressions.length + strings.length;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Options that can be used to modify how a template is parsed by `parseTemplate()`.
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ConstantPool} from '../../constant_pool';
|
||||
import {Interpolation} from '../../expression_parser/ast';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../parse_util';
|
||||
import {splitAtColon} from '../../util';
|
||||
@ -201,3 +202,20 @@ export function chainedInstruction(
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments expected to be passed to a generated instruction in the case of
|
||||
* interpolation instructions.
|
||||
* @param interpolation An interpolation ast
|
||||
*/
|
||||
export function getInterpolationArgsLength(interpolation: Interpolation) {
|
||||
const {expressions, strings} = interpolation;
|
||||
if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
|
||||
// If the interpolation has one interpolated value, but the prefix and suffix are both empty
|
||||
// strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
|
||||
// `textInterpolate`.
|
||||
return 1;
|
||||
} else {
|
||||
return expressions.length + strings.length;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user