perf(ivy): chain host binding instructions (#31296)

Adds chaining to the `property`, `attribute` and `updateSyntheticHostBinding` instructions when they're used in a host binding.

This PR resolves FW-1404.

PR Close #31296
This commit is contained in:
crisbeto
2019-06-27 20:23:15 +02:00
committed by Alex Rickabaugh
parent c6b29f4c6d
commit 81332150aa
9 changed files with 309 additions and 49 deletions

View File

@ -30,7 +30,7 @@ import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, type
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api';
import {Instruction, StylingBuilder} from './styling_builder';
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
const EMPTY_ARRAY: any[] = [];
@ -638,6 +638,10 @@ function createHostBindingsFunction(
// Calculate the host property bindings
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
const propertyBindings: o.Expression[][] = [];
const attributeBindings: o.Expression[][] = [];
const syntheticHostBindings: o.Expression[][] = [];
(bindings || []).forEach((binding: ParsedProperty) => {
const name = binding.name;
const stylingInputWasSet =
@ -681,10 +685,32 @@ function createHostBindingsFunction(
}
updateStatements.push(...bindingExpr.stmts);
updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt());
if (instruction === R3.property) {
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) {
updateStatements.push(chainedInstruction(R3.property, propertyBindings).toStmt());
}
if (attributeBindings.length > 0) {
updateStatements.push(chainedInstruction(R3.attribute, attributeBindings).toStmt());
}
if (syntheticHostBindings.length > 0) {
updateStatements.push(
chainedInstruction(R3.updateSyntheticHostBinding, syntheticHostBindings).toStmt());
}
// since we're dealing with directives/components and both have hostBinding
// functions, we need to generate a special hostAttrs instruction that deals
// with both the assignment of styling as well as static attributes to the host

View File

@ -37,7 +37,8 @@ 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, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, chainedInstruction, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
// Selector attribute name of `<ng-content>`
@ -1105,7 +1106,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
return fnParams;
});
return chainedInstruction(span, reference, calls).toStmt();
return chainedInstruction(reference, calls, span).toStmt();
});
}
@ -1417,22 +1418,6 @@ function instruction(
return o.importExpr(reference, null, span).callFn(params, span);
}
function chainedInstruction(
span: ParseSourceSpan | null, reference: o.ExternalReference, calls: o.Expression[][]) {
let expression = o.importExpr(reference, null, span) as o.Expression;
if (calls.length > 0) {
for (let i = 0; i < calls.length; i++) {
expression = expression.callFn(calls[i], span);
}
} else {
// Add a blank invocation, in case the `calls` array is empty.
expression = expression.callFn([], span);
}
return expression;
}
// e.g. x(2);
function generateNextContextExpr(relativeLevelDiff: number): o.Expression {
return o.importExpr(R3.nextContext)

View File

@ -8,11 +8,14 @@
import {ConstantPool} from '../../constant_pool';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import {splitAtColon} from '../../util';
import * as t from '../r3_ast';
import {R3QueryMetadata} from './api';
import {isI18nAttribute} from './i18n/util';
/**
* Checks whether an object key contains potentially unsafe chars, thus the key should be wrapped in
* quotes. Note: we do not wrap all keys into quotes, as it may have impact on minification and may
@ -181,3 +184,20 @@ export function getAttrsForDirectiveMatching(elOrTpl: t.Element | t.Template):
return attributesMap;
}
/** Returns a call expression to a chained instruction, e.g. `property(params[0])(params[1])`. */
export function chainedInstruction(
reference: o.ExternalReference, calls: o.Expression[][], span?: ParseSourceSpan | null) {
let expression = o.importExpr(reference, null, span) as o.Expression;
if (calls.length > 0) {
for (let i = 0; i < calls.length; i++) {
expression = expression.callFn(calls[i], span);
}
} else {
// Add a blank invocation, in case the `calls` array is empty.
expression = expression.callFn([], span);
}
return expression;
}