refactor(ivy): generate 2 slots per styling instruction (#34616)

Compiler keeps track of number of slots (`vars`) which are needed for binding instructions. Normally each binding instructions allocates a single slot in the `LView` but styling instructions need to allocate two slots.

PR Close #34616
This commit is contained in:
Matias Niemelä
2019-12-14 19:48:24 -08:00
committed by Miško Hevery
parent b7ff38b1ef
commit 4005815114
6 changed files with 243 additions and 129 deletions

View File

@ -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 {StylingBuilder, StylingInstructionCall} from './styling_builder';
import {MIN_STYLING_BINDING_SLOTS_REQUIRED, StylingBuilder, StylingInstructionCall} 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';
@ -530,8 +530,6 @@ function createHostBindingsFunction(
hostBindingsMetadata: R3HostMetadata, typeSourceSpan: ParseSourceSpan,
bindingParser: BindingParser, constantPool: ConstantPool, selector: string, name: string,
definitionMap: DefinitionMap): o.Expression|null {
// Initialize hostVarsCount to number of bound host properties (interpolations illegal)
const hostVarsCount = Object.keys(hostBindingsMetadata.properties).length;
const elVarExp = o.variable('elIndex');
const bindingContext = o.variable(CONTEXT_NAME);
const styleBuilder = new StylingBuilder(elVarExp, bindingContext);
@ -547,10 +545,38 @@ function createHostBindingsFunction(
const createStatements: o.Statement[] = [];
const updateStatements: o.Statement[] = [];
let totalHostVarsCount = hostVarsCount;
const hostBindingSourceSpan = typeSourceSpan;
const directiveSummary = metadataAsSummary(hostBindingsMetadata);
// Calculate host event bindings
const eventBindings =
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
if (eventBindings && eventBindings.length) {
const listeners = createHostListeners(eventBindings, name);
createStatements.push(...listeners);
}
// Calculate the host property bindings
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
const allOtherBindings: ParsedProperty[] = [];
// We need to calculate the total amount of binding slots required by
// all the instructions together before any value conversions happen.
// Value conversions may require additional slots for interpolation and
// bindings with pipes. These calculates happen after this block.
let totalHostVarsCount = 0;
bindings && bindings.forEach((binding: ParsedProperty) => {
const name = binding.name;
const stylingInputWasSet =
styleBuilder.registerInputBasedOnName(name, binding.expression, binding.sourceSpan);
if (stylingInputWasSet) {
totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
} else {
allOtherBindings.push(binding);
totalHostVarsCount++;
}
});
let valueConverter: ValueConverter;
const getValueConverter = () => {
if (!valueConverter) {
@ -568,66 +594,50 @@ function createHostBindingsFunction(
return valueConverter;
};
// Calculate host event bindings
const eventBindings =
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
if (eventBindings && eventBindings.length) {
const listeners = createHostListeners(eventBindings, name);
createStatements.push(...listeners);
}
// Calculate the host property bindings
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
const propertyBindings: o.Expression[][] = [];
const attributeBindings: o.Expression[][] = [];
const syntheticHostBindings: o.Expression[][] = [];
allOtherBindings.forEach((binding: ParsedProperty) => {
// resolve literal arrays and literal objects
const value = binding.expression.visit(getValueConverter());
const bindingExpr = bindingFn(bindingContext, value);
bindings && bindings.forEach((binding: ParsedProperty) => {
const name = binding.name;
const stylingInputWasSet =
styleBuilder.registerInputBasedOnName(name, binding.expression, binding.sourceSpan);
if (!stylingInputWasSet) {
// resolve literal arrays and literal objects
const value = binding.expression.visit(getValueConverter());
const bindingExpr = bindingFn(bindingContext, value);
const {bindingName, instruction, isAttribute} = getBindingNameAndInstruction(binding);
const {bindingName, instruction, isAttribute} = getBindingNameAndInstruction(binding);
const securityContexts =
bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
.filter(context => context !== core.SecurityContext.NONE);
const securityContexts =
bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
.filter(context => context !== core.SecurityContext.NONE);
let sanitizerFn: o.ExternalExpr|null = null;
if (securityContexts.length) {
if (securityContexts.length === 2 &&
securityContexts.indexOf(core.SecurityContext.URL) > -1 &&
securityContexts.indexOf(core.SecurityContext.RESOURCE_URL) > -1) {
// Special case for some URL attributes (such as "src" and "href") that may be a part
// of different security contexts. In this case we use special santitization function and
// select the actual sanitizer at runtime based on a tag name that is provided while
// invoking sanitization function.
sanitizerFn = o.importExpr(R3.sanitizeUrlOrResourceUrl);
} else {
sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
}
}
const instructionParams = [o.literal(bindingName), bindingExpr.currValExpr];
if (sanitizerFn) {
instructionParams.push(sanitizerFn);
}
updateStatements.push(...bindingExpr.stmts);
if (instruction === R3.hostProperty) {
propertyBindings.push(instructionParams);
} else if (instruction === R3.attribute) {
attributeBindings.push(instructionParams);
} else if (instruction === R3.updateSyntheticHostBinding) {
syntheticHostBindings.push(instructionParams);
let sanitizerFn: o.ExternalExpr|null = null;
if (securityContexts.length) {
if (securityContexts.length === 2 &&
securityContexts.indexOf(core.SecurityContext.URL) > -1 &&
securityContexts.indexOf(core.SecurityContext.RESOURCE_URL) > -1) {
// Special case for some URL attributes (such as "src" and "href") that may be a part
// of different security contexts. In this case we use special santitization function and
// select the actual sanitizer at runtime based on a tag name that is provided while
// invoking sanitization function.
sanitizerFn = o.importExpr(R3.sanitizeUrlOrResourceUrl);
} else {
updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt());
sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
}
}
const instructionParams = [o.literal(bindingName), bindingExpr.currValExpr];
if (sanitizerFn) {
instructionParams.push(sanitizerFn);
}
updateStatements.push(...bindingExpr.stmts);
if (instruction === R3.hostProperty) {
propertyBindings.push(instructionParams);
} else if (instruction === R3.attribute) {
attributeBindings.push(instructionParams);
} else if (instruction === R3.updateSyntheticHostBinding) {
syntheticHostBindings.push(instructionParams);
} else {
updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt());
}
});
if (propertyBindings.length > 0) {
@ -664,7 +674,8 @@ function createHostBindingsFunction(
instruction.calls.forEach(call => {
// we subtract a value of `1` here because the binding slot was already allocated
// at the top of this method when all the input bindings were counted.
totalHostVarsCount += Math.max(call.allocateBindingSlots - 1, 0);
totalHostVarsCount +=
Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
calls.push(convertStylingCall(call, bindingContext, bindingFn));
});