fix(ivy): property bindings use correct indices (#30129)
- Extracts and documents code that will be common to interpolation instructions - Ensures that binding indices are updated at the proper time during compilation - Adds additional tests Related #30011 PR Close #30129
This commit is contained in:
parent
876ceb3688
commit
f3ce8eeb83
@ -470,7 +470,7 @@ describe('compiler compliance: bindings', () => {
|
|||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
i0.ɵɵselect(0);
|
i0.ɵɵselect(0);
|
||||||
i0.ɵɵpropertyInterpolateV("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j");
|
i0.ɵɵpropertyInterpolateV("title", ["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
|
||||||
i0.ɵɵselect(1);
|
i0.ɵɵselect(1);
|
||||||
i0.ɵɵpropertyInterpolate8("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
|
i0.ɵɵpropertyInterpolate8("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
|
||||||
i0.ɵɵselect(2);
|
i0.ɵɵselect(2);
|
||||||
|
@ -155,16 +155,12 @@ export function convertPropertyBinding(
|
|||||||
localResolver = new DefaultLocalResolver();
|
localResolver = new DefaultLocalResolver();
|
||||||
}
|
}
|
||||||
const currValExpr = createCurrValueExpr(bindingId);
|
const currValExpr = createCurrValueExpr(bindingId);
|
||||||
const stmts: o.Statement[] = [];
|
|
||||||
const visitor =
|
const visitor =
|
||||||
new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
|
new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
|
||||||
const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
|
const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
|
||||||
|
const stmts: o.Statement[] = getStatementsFromVisitor(visitor, bindingId);
|
||||||
|
|
||||||
if (visitor.temporaryCount) {
|
if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
|
||||||
for (let i = 0; i < visitor.temporaryCount; i++) {
|
|
||||||
stmts.push(temporaryDeclaration(bindingId, i));
|
|
||||||
}
|
|
||||||
} else if (form == BindingForm.TrySimple) {
|
|
||||||
return new ConvertPropertyBindingResult([], outputExpr);
|
return new ConvertPropertyBindingResult([], outputExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,6 +168,58 @@ export function convertPropertyBinding(
|
|||||||
return new ConvertPropertyBindingResult(stmts, currValExpr);
|
return new ConvertPropertyBindingResult(stmts, currValExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given some expression, such as a binding or interpolation expression, and a context expression to
|
||||||
|
* look values up on, visit each facet of the given expression resolving values from the context
|
||||||
|
* expression such that a list of arguments can be derived from the found values that can be used as
|
||||||
|
* arguments to an external update instruction.
|
||||||
|
*
|
||||||
|
* @param localResolver The resolver to use to look up expressions by name appropriately
|
||||||
|
* @param contextVariableExpression The expression representing the context variable used to create
|
||||||
|
* the final argument expressions
|
||||||
|
* @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
|
||||||
|
* be resolved and what arguments list to build.
|
||||||
|
* @param bindingId A name prefix used to create temporary variable names if they're needed for the
|
||||||
|
* arguments generated
|
||||||
|
* @returns An array of expressions that can be passed as arguments to instruction expressions like
|
||||||
|
* `o.importExpr(R3.propertyInterpolate).callFn(result)`
|
||||||
|
*/
|
||||||
|
export function convertUpdateArguments(
|
||||||
|
localResolver: LocalResolver, contextVariableExpression: o.Expression,
|
||||||
|
expressionWithArgumentsToExtract: cdAst.AST, bindingId: string) {
|
||||||
|
const visitor =
|
||||||
|
new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
|
||||||
|
const outputExpr: o.InvokeFunctionExpr =
|
||||||
|
expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
|
||||||
|
|
||||||
|
const stmts = getStatementsFromVisitor(visitor, bindingId);
|
||||||
|
|
||||||
|
// Removing the first argument, because it was a length for ViewEngine, not Ivy.
|
||||||
|
let args = outputExpr.args.slice(1);
|
||||||
|
if (expressionWithArgumentsToExtract instanceof cdAst.Interpolation) {
|
||||||
|
// If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
|
||||||
|
// args returned to just the value, because we're going to pass it to a special instruction.
|
||||||
|
const strings = expressionWithArgumentsToExtract.strings;
|
||||||
|
if (args.length === 3 && strings[0] === '' && strings[1] === '') {
|
||||||
|
// Single argument interpolate instructions.
|
||||||
|
args = [args[1]];
|
||||||
|
} else if (args.length >= 19) {
|
||||||
|
// 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
|
||||||
|
// an array of arguments
|
||||||
|
args = [o.literalArr(args)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {stmts, args};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatementsFromVisitor(visitor: _AstToIrVisitor, bindingId: string) {
|
||||||
|
const stmts: o.Statement[] = [];
|
||||||
|
for (let i = 0; i < visitor.temporaryCount; i++) {
|
||||||
|
stmts.push(temporaryDeclaration(bindingId, i));
|
||||||
|
}
|
||||||
|
return stmts;
|
||||||
|
}
|
||||||
|
|
||||||
function convertBuiltins(converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
|
function convertBuiltins(converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
|
||||||
const visitor = new _BuiltinAstConverter(converterFactory);
|
const visitor = new _BuiltinAstConverter(converterFactory);
|
||||||
return ast.visit(visitor);
|
return ast.visit(visitor);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {flatten, sanitizeIdentifier} from '../../compile_metadata';
|
import {flatten, sanitizeIdentifier} from '../../compile_metadata';
|
||||||
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding, convertUpdateArguments} from '../../compiler_util/expression_converter';
|
||||||
import {ConstantPool} from '../../constant_pool';
|
import {ConstantPool} from '../../constant_pool';
|
||||||
import * as core from '../../core';
|
import * as core from '../../core';
|
||||||
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
|
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
|
||||||
@ -750,23 +750,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
|
|
||||||
if (inputType === BindingType.Property) {
|
if (inputType === BindingType.Property) {
|
||||||
if (value instanceof Interpolation) {
|
if (value instanceof Interpolation) {
|
||||||
// Interpolated properties
|
|
||||||
const {currValExpr} = convertPropertyBinding(
|
|
||||||
this, implicit, value, this.bindingContext(), BindingForm.TrySimple);
|
|
||||||
|
|
||||||
let args: o.Expression[] = (currValExpr as any).args;
|
|
||||||
args.shift(); // ViewEngine required a count, we don't need that.
|
|
||||||
|
|
||||||
// For interpolations like attr="{{foo}}", we don't need ["", foo, ""], just [foo].
|
|
||||||
if (args.length === 3 && isEmptyStringExpression(args[0]) &&
|
|
||||||
isEmptyStringExpression(args[2])) {
|
|
||||||
args = [args[1]];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateInstruction(
|
this.updateInstruction(
|
||||||
elementIndex, input.sourceSpan, propertyInterpolate(args.length), () => {
|
elementIndex, input.sourceSpan, getPropertyInterpolationExpression(value),
|
||||||
return [o.literal(attrName), ...args, ...params];
|
() =>
|
||||||
});
|
[o.literal(attrName),
|
||||||
|
...this.getUpdateInstructionArguments(o.variable(CONTEXT_NAME), value),
|
||||||
|
...params]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Bound, un-interpolated properties
|
// Bound, un-interpolated properties
|
||||||
this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
|
this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
|
||||||
@ -1076,6 +1066,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
o.importExpr(R3.bind).callFn([valExpr]);
|
o.importExpr(R3.bind).callFn([valExpr]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of argument expressions to pass to an update instruction expression. Also updates
|
||||||
|
* the temp variables state with temp variables that were identified as needing to be created
|
||||||
|
* while visiting the arguments.
|
||||||
|
* @param contextExpression The expression for the context variable used to create arguments
|
||||||
|
* @param value The original expression we will be resolving an arguments list from.
|
||||||
|
*/
|
||||||
|
private getUpdateInstructionArguments(contextExpression: o.Expression, value: AST):
|
||||||
|
o.Expression[] {
|
||||||
|
const {args, stmts} =
|
||||||
|
convertUpdateArguments(this, contextExpression, value, this.bindingContext());
|
||||||
|
this._tempVariables.push(...stmts);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
private matchDirectives(tagName: string, elOrTpl: t.Element|t.Template) {
|
private matchDirectives(tagName: string, elOrTpl: t.Element|t.Template) {
|
||||||
if (this.directiveMatcher) {
|
if (this.directiveMatcher) {
|
||||||
const selector = createCssSelector(tagName, getAttrsForDirectiveMatching(elOrTpl));
|
const selector = createCssSelector(tagName, getAttrsForDirectiveMatching(elOrTpl));
|
||||||
@ -1646,16 +1651,12 @@ function interpolate(args: o.Expression[]): o.Expression {
|
|||||||
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
|
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmptyStringExpression(exp: o.Expression) {
|
/**
|
||||||
return exp instanceof o.LiteralExpr && exp.value === '';
|
* Gets the instruction to generate for an interpolated property
|
||||||
}
|
* @param interpolation An Interpolation AST
|
||||||
|
*/
|
||||||
function propertyInterpolate(argsLength: number) {
|
function getPropertyInterpolationExpression(interpolation: Interpolation) {
|
||||||
if (argsLength % 2 !== 1) {
|
switch (getInterpolationArgsLength(interpolation)) {
|
||||||
error(`Invalid propertyInterpolate argument length ${argsLength}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (argsLength) {
|
|
||||||
case 1:
|
case 1:
|
||||||
return R3.propertyInterpolate;
|
return R3.propertyInterpolate;
|
||||||
case 3:
|
case 3:
|
||||||
@ -1679,6 +1680,22 @@ function propertyInterpolate(argsLength: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()`.
|
* Options that can be used to modify how a template is parsed by `parseTemplate()`.
|
||||||
*/
|
*/
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import {assertEqual, assertLessThan} from '../../util/assert';
|
import {assertEqual, assertLessThan} from '../../util/assert';
|
||||||
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
|
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
|
||||||
|
import {SanitizerFn} from '../interfaces/sanitization';
|
||||||
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
|
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
|
||||||
import {getLView, getSelectedIndex} from '../state';
|
import {getLView, getSelectedIndex} from '../state';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
@ -15,6 +16,7 @@ import {renderStringify} from '../util/misc_utils';
|
|||||||
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
|
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create interpolation bindings with a variable number of expressions.
|
* Create interpolation bindings with a variable number of expressions.
|
||||||
*
|
*
|
||||||
@ -290,12 +292,6 @@ export function ɵɵinterpolation8(
|
|||||||
/// NEW INSTRUCTIONS
|
/// NEW INSTRUCTIONS
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shared reference to a string, used in `ɵɵpropertyInterpolate`.
|
|
||||||
*/
|
|
||||||
const EMPTY_STRING = '';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Update an interpolated property on an element with a lone bound value
|
* Update an interpolated property on an element with a lone bound value
|
||||||
@ -321,11 +317,13 @@ const EMPTY_STRING = '';
|
|||||||
* @param prefix Static value used for concatenation only.
|
* @param prefix Static value used for concatenation only.
|
||||||
* @param v0 Value checked for change.
|
* @param v0 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009 {
|
export function ɵɵpropertyInterpolate(
|
||||||
ɵɵpropertyInterpolate1(propName, EMPTY_STRING, v0, EMPTY_STRING);
|
propName: string, v0: any, sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
|
ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer);
|
||||||
return ɵɵpropertyInterpolate;
|
return ɵɵpropertyInterpolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,13 +352,15 @@ export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue
|
|||||||
* @param prefix Static value used for concatenation only.
|
* @param prefix Static value used for concatenation only.
|
||||||
* @param v0 Value checked for change.
|
* @param v0 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate1(
|
export function ɵɵpropertyInterpolate1(
|
||||||
propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009 {
|
propName: string, prefix: string, v0: any, suffix: string,
|
||||||
|
sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(index, propName, ɵɵinterpolation1(prefix, v0, suffix));
|
elementPropertyInternal(index, propName, ɵɵinterpolation1(prefix, v0, suffix), sanitizer);
|
||||||
return ɵɵpropertyInterpolate1;
|
return ɵɵpropertyInterpolate1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,14 +390,15 @@ export function ɵɵpropertyInterpolate1(
|
|||||||
* @param i0 Static value used for concatenation only.
|
* @param i0 Static value used for concatenation only.
|
||||||
* @param v1 Value checked for change.
|
* @param v1 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate2(
|
export function ɵɵpropertyInterpolate2(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string,
|
||||||
suffix: string): TsickleIssue1009 {
|
sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(index, propName, ɵɵinterpolation2(prefix, v0, i0, v1, suffix));
|
elementPropertyInternal(index, propName, ɵɵinterpolation2(prefix, v0, i0, v1, suffix), sanitizer);
|
||||||
return ɵɵpropertyInterpolate2;
|
return ɵɵpropertyInterpolate2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,14 +431,16 @@ export function ɵɵpropertyInterpolate2(
|
|||||||
* @param i1 Static value used for concatenation only.
|
* @param i1 Static value used for concatenation only.
|
||||||
* @param v2 Value checked for change.
|
* @param v2 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate3(
|
export function ɵɵpropertyInterpolate3(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
|
||||||
suffix: string): TsickleIssue1009 {
|
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(index, propName, ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix));
|
elementPropertyInternal(
|
||||||
|
index, propName, ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix), sanitizer);
|
||||||
return ɵɵpropertyInterpolate3;
|
return ɵɵpropertyInterpolate3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,15 +475,16 @@ export function ɵɵpropertyInterpolate3(
|
|||||||
* @param i2 Static value used for concatenation only.
|
* @param i2 Static value used for concatenation only.
|
||||||
* @param v3 Value checked for change.
|
* @param v3 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate4(
|
export function ɵɵpropertyInterpolate4(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||||
v3: any, suffix: string): TsickleIssue1009 {
|
v3: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(
|
elementPropertyInternal(
|
||||||
index, propName, ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix));
|
index, propName, ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix), sanitizer);
|
||||||
return ɵɵpropertyInterpolate4;
|
return ɵɵpropertyInterpolate4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,15 +521,17 @@ export function ɵɵpropertyInterpolate4(
|
|||||||
* @param i3 Static value used for concatenation only.
|
* @param i3 Static value used for concatenation only.
|
||||||
* @param v4 Value checked for change.
|
* @param v4 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate5(
|
export function ɵɵpropertyInterpolate5(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||||
v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009 {
|
v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(
|
elementPropertyInternal(
|
||||||
index, propName, ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix));
|
index, propName, ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix),
|
||||||
|
sanitizer);
|
||||||
return ɵɵpropertyInterpolate5;
|
return ɵɵpropertyInterpolate5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,16 +570,18 @@ export function ɵɵpropertyInterpolate5(
|
|||||||
* @param i4 Static value used for concatenation only.
|
* @param i4 Static value used for concatenation only.
|
||||||
* @param v5 Value checked for change.
|
* @param v5 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate6(
|
export function ɵɵpropertyInterpolate6(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||||
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): TsickleIssue1009 {
|
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string,
|
||||||
|
sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(
|
elementPropertyInternal(
|
||||||
index, propName,
|
index, propName, ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix),
|
||||||
ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix));
|
sanitizer);
|
||||||
return ɵɵpropertyInterpolate6;
|
return ɵɵpropertyInterpolate6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,17 +622,19 @@ export function ɵɵpropertyInterpolate6(
|
|||||||
* @param i5 Static value used for concatenation only.
|
* @param i5 Static value used for concatenation only.
|
||||||
* @param v6 Value checked for change.
|
* @param v6 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate7(
|
export function ɵɵpropertyInterpolate7(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||||
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any,
|
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string,
|
||||||
suffix: string): TsickleIssue1009 {
|
sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(
|
elementPropertyInternal(
|
||||||
index, propName,
|
index, propName,
|
||||||
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix));
|
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix),
|
||||||
|
sanitizer);
|
||||||
return ɵɵpropertyInterpolate7;
|
return ɵɵpropertyInterpolate7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,17 +677,19 @@ export function ɵɵpropertyInterpolate7(
|
|||||||
* @param i6 Static value used for concatenation only.
|
* @param i6 Static value used for concatenation only.
|
||||||
* @param v7 Value checked for change.
|
* @param v7 Value checked for change.
|
||||||
* @param suffix Static value used for concatenation only.
|
* @param suffix Static value used for concatenation only.
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolate8(
|
export function ɵɵpropertyInterpolate8(
|
||||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||||
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
|
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
|
||||||
suffix: string): TsickleIssue1009 {
|
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
elementPropertyInternal(
|
elementPropertyInternal(
|
||||||
index, propName,
|
index, propName,
|
||||||
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix));
|
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix),
|
||||||
|
sanitizer);
|
||||||
return ɵɵpropertyInterpolate8;
|
return ɵɵpropertyInterpolate8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,12 +719,14 @@ export function ɵɵpropertyInterpolate8(
|
|||||||
* @param values The a collection of values and the strings inbetween those values, beginning with a
|
* @param values The a collection of values and the strings inbetween those values, beginning with a
|
||||||
* string prefix and ending with a string suffix.
|
* string prefix and ending with a string suffix.
|
||||||
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
|
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
|
||||||
|
* @param sanitizer An optional sanitizer function
|
||||||
* @returns itself, so that it may be chained.
|
* @returns itself, so that it may be chained.
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009 {
|
export function ɵɵpropertyInterpolateV(
|
||||||
|
propName: string, values: any[], sanitizer?: SanitizerFn): TsickleIssue1009 {
|
||||||
const index = getSelectedIndex();
|
const index = getSelectedIndex();
|
||||||
|
|
||||||
elementPropertyInternal(index, propName, ɵɵinterpolationV(values));
|
elementPropertyInternal(index, propName, ɵɵinterpolationV(values), sanitizer);
|
||||||
return ɵɵpropertyInterpolateV;
|
return ɵɵpropertyInterpolateV;
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,9 @@ import {Component, Input} from '@angular/core';
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser';
|
import {By} from '@angular/platform-browser';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
import {of } from 'rxjs';
|
||||||
|
|
||||||
describe('elementProperty', () => {
|
describe('property instructions', () => {
|
||||||
it('should bind to properties whose names do not correspond to their attribute names', () => {
|
it('should bind to properties whose names do not correspond to their attribute names', () => {
|
||||||
@Component({template: '<label [for]="forValue"></label>'})
|
@Component({template: '<label [for]="forValue"></label>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
@ -33,6 +34,25 @@ describe('elementProperty', () => {
|
|||||||
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-textarea');
|
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-textarea');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not allow unsanitary urls in bound properties', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<img [src]="naughty">
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const img = fixture.nativeElement.querySelector('img');
|
||||||
|
|
||||||
|
expect(img.src.indexOf('unsafe:')).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not map properties whose names do not correspond to their attribute names, ' +
|
it('should not map properties whose names do not correspond to their attribute names, ' +
|
||||||
'if they correspond to inputs',
|
'if they correspond to inputs',
|
||||||
() => {
|
() => {
|
||||||
@ -60,4 +80,136 @@ describe('elementProperty', () => {
|
|||||||
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
|
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
|
||||||
expect(myCompNode.componentInstance.for).toBe('hej');
|
expect(myCompNode.componentInstance.for).toBe('hej');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle all flavors of interpolated properties', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c{{three}}d"></div>
|
||||||
|
<div title="a{{one}}b{{two}}c"></div>
|
||||||
|
<div title="a{{one}}b"></div>
|
||||||
|
<div title="{{one}}"></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
one = 1;
|
||||||
|
two = 2;
|
||||||
|
three = 3;
|
||||||
|
four = 4;
|
||||||
|
five = 5;
|
||||||
|
six = 6;
|
||||||
|
seven = 7;
|
||||||
|
eight = 8;
|
||||||
|
nine = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const titles = Array.from(fixture.nativeElement.querySelectorAll('div[title]'))
|
||||||
|
.map((div: HTMLDivElement) => div.title);
|
||||||
|
|
||||||
|
expect(titles).toEqual([
|
||||||
|
'a1b2c3d4e5f6g7h8i9j',
|
||||||
|
'a1b2c3d4e5f6g7h8i',
|
||||||
|
'a1b2c3d4e5f6g7h',
|
||||||
|
'a1b2c3d4e5f6g',
|
||||||
|
'a1b2c3d4e5f',
|
||||||
|
'a1b2c3d4e',
|
||||||
|
'a1b2c3d',
|
||||||
|
'a1b2c',
|
||||||
|
'a1b',
|
||||||
|
'1',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pipes in interpolated properties', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<img title="{{(details | async)?.title}}" src="{{(details | async)?.url}}" />
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
details = of ({
|
||||||
|
title: 'cool image',
|
||||||
|
url: 'http://somecooldomain:1234/cool_image.png',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
|
||||||
|
expect(img.src).toBe('http://somecooldomain:1234/cool_image.png');
|
||||||
|
expect(img.title).toBe('cool image');
|
||||||
|
});
|
||||||
|
|
||||||
|
// From https://angular-team.atlassian.net/browse/FW-1287
|
||||||
|
it('should handle multiple elvis operators', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<img src="{{leadSurgeon?.getCommonInfo()?.getPhotoUrl() }}">
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
/** Clearly this is a doctor of heavy metals. */
|
||||||
|
leadSurgeon = {
|
||||||
|
getCommonInfo() {
|
||||||
|
return {getPhotoUrl() { return 'http://somecooldomain:1234/cool_image.png'; }};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const img = fixture.nativeElement.querySelector('img');
|
||||||
|
|
||||||
|
expect(img.src).toBe('http://somecooldomain:1234/cool_image.png');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow unsanitary urls in interpolated properties', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<img src="{{naughty}}">
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
|
||||||
|
|
||||||
|
expect(img.src.indexOf('unsafe:')).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow unsanitary urls in interpolated properties, even if you are tricky', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<img src="{{ja}}{{va}}script:{{naughty}}">
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
ja = 'ja';
|
||||||
|
va = 'va';
|
||||||
|
naughty = 'alert("I am a h4xx0rz1!!");';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const img = fixture.nativeElement.querySelector('img');
|
||||||
|
|
||||||
|
expect(img.src.indexOf('unsafe:')).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
20
tools/public_api_guard/core/core.d.ts
vendored
20
tools/public_api_guard/core/core.d.ts
vendored
@ -943,25 +943,25 @@ export declare function ɵɵprojectionDef(selectors?: CssSelectorList[]): void;
|
|||||||
|
|
||||||
export declare function ɵɵproperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
|
export declare function ɵɵproperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate(propName: string, v0: any, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate1(propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate1(propName: string, prefix: string, v0: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate2(propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate2(propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate3(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate3(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate4(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate4(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate5(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate5(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate6(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate6(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate7(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate7(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolate8(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, suffix: string): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolate8(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009;
|
export declare function ɵɵpropertyInterpolateV(propName: string, values: any[], sanitizer?: SanitizerFn): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵProvidersFeature<T>(providers: Provider[], viewProviders?: Provider[]): (definition: DirectiveDef<T>) => void;
|
export declare function ɵɵProvidersFeature<T>(providers: Provider[], viewProviders?: Provider[]): (definition: DirectiveDef<T>) => void;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user