refactor(ivy): move hostVars/hostAttrs from instruction to DirectiveDef (#34683)

This change moves information from instructions to declarative position:
- `ɵɵallocHostVars(vars)` => `DirectiveDef.hostVars`
- `ɵɵelementHostAttrs(attrs)` => `DirectiveDef.hostAttrs`

When merging directives it is necessary to know about `hostVars` and `hostAttrs`. Before this change the information was stored in the `hostBindings` function. This was problematic, because in order to get to the information the `hostBindings` would have to be executed. In order for `hostBindings` to be executed the directives would have to be instantiated. This means that the directive instantiation would happen before we had knowledge about the `hostAttrs` and as a result the directive could observe in the constructor that not all of the `hostAttrs` have been applied. This further complicates the runtime as we have to apply `hostAttrs` in parts over many invocations.

`ɵɵallocHostVars` was unnecessarily complicated because it would have to update the `LView` (and Blueprint) while existing directives are already executing. By moving it out of `hostBindings` function we can access it statically and we can create correct `LView` (and Blueprint) in a single pass.

This change only changes how the instructions are generated, but does not change the runtime much. (We cheat by emulating the old behavior by calling `ɵɵallocHostVars` and `ɵɵelementHostAttrs`) Subsequent change will refactor the runtime to take advantage of the static information.

PR Close #34683
This commit is contained in:
Miško Hevery
2020-01-08 11:32:33 -08:00
parent 94504ff5c8
commit 2961bf06c6
33 changed files with 751 additions and 185 deletions

View File

@ -115,8 +115,6 @@ export class Identifiers {
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
static elementHostAttrs: o.ExternalReference = {name: 'ɵɵelementHostAttrs', moduleName: CORE};
static containerCreate: o.ExternalReference = {name: 'ɵɵcontainer', moduleName: CORE};
static nextContext: o.ExternalReference = {name: 'ɵɵnextContext', moduleName: CORE};
@ -129,8 +127,6 @@ export class Identifiers {
static disableBindings: o.ExternalReference = {name: 'ɵɵdisableBindings', moduleName: CORE};
static allocHostVars: o.ExternalReference = {name: 'ɵɵallocHostVars', moduleName: CORE};
static getCurrentView: o.ExternalReference = {name: 'ɵɵgetCurrentView', moduleName: CORE};
static textInterpolate: o.ExternalReference = {name: 'ɵɵtextInterpolate', moduleName: CORE};

View File

@ -67,7 +67,7 @@ function baseDirectiveFields(
definitionMap.set(
'hostBindings', createHostBindingsFunction(
meta.host, meta.typeSourceSpan, bindingParser, constantPool,
meta.selector || '', meta.name));
meta.selector || '', meta.name, definitionMap));
// e.g 'inputs: {a: 'a'}`
definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
@ -528,8 +528,8 @@ function createViewQueriesFunction(
// Return a host binding function or null if one is not necessary.
function createHostBindingsFunction(
hostBindingsMetadata: R3HostMetadata, typeSourceSpan: ParseSourceSpan,
bindingParser: BindingParser, constantPool: ConstantPool, selector: string,
name?: string): o.Expression|null {
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');
@ -651,14 +651,7 @@ function createHostBindingsFunction(
// to the host element alongside any of the provided host attributes that were
// collected earlier.
const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes);
const hostInstruction = styleBuilder.buildHostAttrsInstruction(null, hostAttrs, constantPool);
if (hostInstruction && hostInstruction.calls.length > 0) {
createStatements.push(
chainedInstruction(
hostInstruction.reference,
hostInstruction.calls.map(call => convertStylingCall(call, bindingContext, bindingFn)))
.toStmt());
}
styleBuilder.assignHostAttrs(hostAttrs, definitionMap);
if (styleBuilder.hasBindings) {
// finally each binding that was registered in the statement above will need to be added to
@ -681,8 +674,7 @@ function createHostBindingsFunction(
}
if (totalHostVarsCount) {
createStatements.unshift(
o.importExpr(R3.allocHostVars).callFn([o.literal(totalHostVarsCount)]).toStmt());
definitionMap.set('hostVars', o.literal(totalHostVarsCount));
}
if (createStatements.length > 0 || updateStatements.length > 0) {

View File

@ -16,7 +16,7 @@ import {Identifiers as R3} from '../r3_identifiers';
import {hyphenate, parse as parseStyle} from './style_parser';
import {ValueConverter} from './template';
import {getInterpolationArgsLength} from './util';
import {DefinitionMap, getInterpolationArgsLength} from './util';
const IMPORTANT_FLAG = '!important';
@ -279,27 +279,11 @@ export class StylingBuilder {
* responsible for registering initial styles (within a directive hostBindings' creation block),
* as well as any of the provided attribute values, to the directive host element.
*/
buildHostAttrsInstruction(
sourceSpan: ParseSourceSpan|null, attrs: o.Expression[],
constantPool: ConstantPool): StylingInstruction|null {
assignHostAttrs(attrs: o.Expression[], definitionMap: DefinitionMap): void {
if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
return {
reference: R3.elementHostAttrs,
calls: [{
sourceSpan,
allocateBindingSlots: 0,
params: () => {
// params => elementHostAttrs(attrs)
this.populateInitialStylingAttrs(attrs);
const attrArray = !attrs.some(attr => attr instanceof o.WrappedNodeExpr) ?
getConstantLiteralFromArray(constantPool, attrs) :
o.literalArr(attrs);
return [attrArray];
}
}]
};
this.populateInitialStylingAttrs(attrs);
definitionMap.set('hostAttrs', o.literalArr(attrs));
}
return null;
}
/**