refactor(ivy): minor refactoring of Host Bindings function generation (#28379)

Prior to this change, generation of host bindings and host styles was guarded by the "if" statement, which always returned true. Enforcing more strict check for bindings length broke some tests, since host styling instructions generation were inside the same "if" block. This update decouples bindings instruction generation from styling instructions, which makes it less error prone.

PR Close #28379
This commit is contained in:
Andrew Kushnir 2019-01-25 12:17:50 -08:00 committed by Jason Aden
parent 66ce3b2f2f
commit 778d5739e2
1 changed files with 102 additions and 98 deletions

View File

@ -643,6 +643,23 @@ function createHostBindingsFunction(
const hostBindingSourceSpan = meta.typeSourceSpan; const hostBindingSourceSpan = meta.typeSourceSpan;
const directiveSummary = metadataAsSummary(meta); const directiveSummary = metadataAsSummary(meta);
let valueConverter: ValueConverter;
const getValueConverter = () => {
if (!valueConverter) {
const hostVarsCountFn = (numSlots: number): number => {
const originalVarsCount = totalHostVarsCount;
totalHostVarsCount += numSlots;
return originalVarsCount;
};
valueConverter = new ValueConverter(
constantPool,
() => error('Unexpected node'), // new nodes are illegal here
hostVarsCountFn,
() => error('Unexpected pipe')); // pipes are illegal here
}
return valueConverter;
};
// Calculate host event bindings // Calculate host event bindings
const eventBindings = const eventBindings =
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan); bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
@ -653,23 +670,7 @@ function createHostBindingsFunction(
// Calculate the host property bindings // Calculate the host property bindings
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan); const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
(bindings || []).forEach((binding: ParsedProperty) => {
const bindingFn = (implicit: any, value: AST) => {
return convertPropertyBinding(
null, implicit, value, 'b', BindingForm.TrySimple, () => error('Unexpected interpolation'));
};
if (bindings) {
const hostVarsCountFn = (numSlots: number): number => {
const originalVarsCount = totalHostVarsCount;
totalHostVarsCount += numSlots;
return originalVarsCount;
};
const valueConverter = new ValueConverter(
constantPool,
/* new nodes are illegal here */ () => error('Unexpected node'), hostVarsCountFn,
/* pipes are illegal here */ () => error('Unexpected pipe'));
for (const binding of bindings) {
const name = binding.name; const name = binding.name;
const stylePrefix = getStylingPrefix(name); const stylePrefix = getStylingPrefix(name);
if (stylePrefix === 'style') { if (stylePrefix === 'style') {
@ -680,14 +681,13 @@ function createHostBindingsFunction(
parseNamedProperty(name).propertyName, binding.expression, binding.sourceSpan); parseNamedProperty(name).propertyName, binding.expression, binding.sourceSpan);
} else { } else {
// resolve literal arrays and literal objects // resolve literal arrays and literal objects
const value = binding.expression.visit(valueConverter); const value = binding.expression.visit(getValueConverter());
const bindingExpr = bindingFn(bindingContext, value); const bindingExpr = bindingFn(bindingContext, value);
const {bindingName, instruction, isAttribute} = getBindingNameAndInstruction(binding); const {bindingName, instruction, isAttribute} = getBindingNameAndInstruction(binding);
const securityContexts = const securityContexts =
bindingParser bindingParser.calcPossibleSecurityContexts(meta.selector || '', bindingName, isAttribute)
.calcPossibleSecurityContexts(meta.selector || '', bindingName, isAttribute)
.filter(context => context !== core.SecurityContext.NONE); .filter(context => context !== core.SecurityContext.NONE);
let sanitizerFn: o.ExternalExpr|null = null; let sanitizerFn: o.ExternalExpr|null = null;
@ -695,8 +695,8 @@ function createHostBindingsFunction(
if (securityContexts.length === 2 && if (securityContexts.length === 2 &&
securityContexts.indexOf(core.SecurityContext.URL) > -1 && securityContexts.indexOf(core.SecurityContext.URL) > -1 &&
securityContexts.indexOf(core.SecurityContext.RESOURCE_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 // Special case for some URL attributes (such as "src" and "href") that may be a part
// different security contexts. In this case we use special santitization function and // 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 // select the actual sanitizer at runtime based on a tag name that is provided while
// invoking sanitization function. // invoking sanitization function.
sanitizerFn = o.importExpr(R3.sanitizeUrlOrResourceUrl); sanitizerFn = o.importExpr(R3.sanitizeUrlOrResourceUrl);
@ -723,7 +723,7 @@ function createHostBindingsFunction(
updateStatements.push(...bindingExpr.stmts); updateStatements.push(...bindingExpr.stmts);
updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt()); updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt());
} }
} });
// since we're dealing with directives/components and both have hostBinding // since we're dealing with directives/components and both have hostBinding
// functions, we need to generate a special hostAttrs instruction that deals // functions, we need to generate a special hostAttrs instruction that deals
@ -754,11 +754,10 @@ function createHostBindingsFunction(
// finally each binding that was registered in the statement above will need to be added to // finally each binding that was registered in the statement above will need to be added to
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings // the update block of a component/directive templateFn/hostBindingsFn so that the bindings
// are evaluated and updated for the element. // are evaluated and updated for the element.
styleBuilder.buildUpdateLevelInstructions(valueConverter).forEach(instruction => { styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
updateStatements.push(createStylingStmt(instruction, bindingContext, bindingFn)); updateStatements.push(createStylingStmt(instruction, bindingContext, bindingFn));
}); });
} }
}
if (totalHostVarsCount) { if (totalHostVarsCount) {
createStatements.unshift( createStatements.unshift(
@ -785,6 +784,11 @@ function createHostBindingsFunction(
return null; return null;
} }
function bindingFn(implicit: any, value: AST) {
return convertPropertyBinding(
null, implicit, value, 'b', BindingForm.TrySimple, () => error('Unexpected interpolation'));
}
function createStylingStmt( function createStylingStmt(
instruction: Instruction, bindingContext: any, bindingFn: Function): o.Statement { instruction: Instruction, bindingContext: any, bindingFn: Function): o.Statement {
const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr); const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr);