refactor(ivy): make styling instructions use the new styling algorithm (#30742)

This commit is the final patch of the ivy styling algorithm refactor.
This patch swaps functionality from the old styling mechanism to the
new refactored code by changing the instruction code the compiler
generates and by pointing the runtime instruction code to the new
styling algorithm.

PR Close #30742
This commit is contained in:
Matias Niemelä
2019-05-28 10:31:01 -07:00
committed by Kara Erickson
parent f14693b9a4
commit 9c954ebc62
57 changed files with 2287 additions and 2612 deletions

View File

@ -586,14 +586,8 @@ function createHostBindingsFunction(
hostBindingsMetadata: R3HostMetadata, typeSourceSpan: ParseSourceSpan,
bindingParser: BindingParser, constantPool: ConstantPool, selector: string,
name?: string): o.Expression|null {
// Initialize hostVarsCount to number of bound host properties (interpolations illegal),
// except 'style' and 'class' properties, since they should *not* allocate host var slots
const hostVarsCount = Object.keys(hostBindingsMetadata.properties)
.filter(name => {
const prefix = getStylingPrefix(name);
return prefix !== 'style' && prefix !== 'class';
})
.length;
// 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);
@ -733,7 +727,10 @@ function createHostBindingsFunction(
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings
// are evaluated and updated for the element.
styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
totalHostVarsCount += instruction.allocateBindingSlots;
// 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(instruction.allocateBindingSlots - 1, 0);
updateStatements.push(createStylingStmt(instruction, bindingContext, bindingFn));
});
}

View File

@ -15,8 +15,7 @@ import {error} from '../../util';
import * as t from '../r3_ast';
import {Identifiers as R3} from '../r3_identifiers';
import {parse as parseStyle} from './style_parser';
import {compilerIsNewStylingInUse} from './styling_state';
import {hyphenate, parse as parseStyle} from './style_parser';
import {ValueConverter} from './template';
import {getInterpolationArgsLength} from './util';
@ -69,7 +68,7 @@ interface BoundStylingEntry {
* classMap(...)
* styleProp(...)
* classProp(...)
* stylingApp(...)
* stylingApply(...)
* }
*
* The creation/update methods within the builder class produce these instructions.
@ -171,6 +170,7 @@ export class StylingBuilder {
if (isEmptyExpression(value)) {
return null;
}
name = normalizePropName(name);
const {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name);
const entry: BoundStylingEntry = {
name: property,
@ -296,46 +296,7 @@ export class StylingBuilder {
sourceSpan,
allocateBindingSlots: 0,
reference: R3.styling,
params: () => {
// a string array of every style-based binding
const styleBindingProps =
this._singleStyleInputs ? this._singleStyleInputs.map(i => o.literal(i.name)) : [];
// a string array of every class-based binding
const classBindingNames =
this._singleClassInputs ? this._singleClassInputs.map(i => o.literal(i.name)) : [];
// to salvage space in the AOT generated code, there is no point in passing
// in `null` into a param if any follow-up params are not used. Therefore,
// only when a trailing param is used then it will be filled with nulls in between
// (otherwise a shorter amount of params will be filled). The code below helps
// determine how many params are required in the expression code.
//
// min params => styling()
// max params => styling(classBindings, styleBindings, sanitizer)
//
const params: o.Expression[] = [];
let expectedNumberOfArgs = 0;
if (this._useDefaultSanitizer) {
expectedNumberOfArgs = 3;
} else if (styleBindingProps.length) {
expectedNumberOfArgs = 2;
} else if (classBindingNames.length) {
expectedNumberOfArgs = 1;
}
addParam(
params, classBindingNames.length > 0,
getConstantLiteralFromArray(constantPool, classBindingNames), 1,
expectedNumberOfArgs);
addParam(
params, styleBindingProps.length > 0,
getConstantLiteralFromArray(constantPool, styleBindingProps), 2,
expectedNumberOfArgs);
addParam(
params, this._useDefaultSanitizer, o.importExpr(R3.defaultStyleSanitizer), 3,
expectedNumberOfArgs);
return params;
}
params: () => [],
};
}
return null;
@ -370,12 +331,8 @@ export class StylingBuilder {
private _buildMapBasedInstruction(
valueConverter: ValueConverter, isClassBased: boolean,
stylingInput: BoundStylingEntry): StylingInstruction {
let totalBindingSlotsRequired = 0;
if (compilerIsNewStylingInUse()) {
// the old implementation does not reserve slot values for
// binding entries. The new one does.
totalBindingSlotsRequired++;
}
// each styling binding value is stored in the LView
let totalBindingSlotsRequired = 1;
// these values must be outside of the update block so that they can
// be evaluated (the AST visit call) during creation time so that any
@ -408,8 +365,11 @@ export class StylingBuilder {
StylingInstruction[] {
let totalBindingSlotsRequired = 0;
return inputs.map(input => {
const bindingIndex: number = mapIndex.get(input.name !) !;
const value = input.value.visit(valueConverter);
// each styling binding value is stored in the LView
let totalBindingSlotsRequired = 1;
if (value instanceof Interpolation) {
totalBindingSlotsRequired += value.expressions.length;
@ -417,35 +377,25 @@ export class StylingBuilder {
reference = getInterpolationExpressionFn(value);
}
}
if (compilerIsNewStylingInUse()) {
// the old implementation does not reserve slot values for
// binding entries. The new one does.
totalBindingSlotsRequired++;
}
return {
sourceSpan: input.sourceSpan,
supportsInterpolation: !!getInterpolationExpressionFn,
allocateBindingSlots: totalBindingSlotsRequired, reference,
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
// min params => stylingProp(elmIndex, bindingIndex, value)
// max params => stylingProp(elmIndex, bindingIndex, value, overrideFlag)
const params: o.Expression[] = [o.literal(bindingIndex)];
// params => stylingProp(propName, value)
const params: o.Expression[] = [];
params.push(o.literal(input.name));
const convertResult = convertFn(value);
if (Array.isArray(convertResult)) {
params.push(...convertResult);
} else {
params.push(convertResult);
}
if (allowUnits) {
if (input.unit) {
params.push(o.literal(input.unit));
} else if (input.hasOverrideFlag) {
params.push(o.NULL_EXPR);
}
}
if (input.hasOverrideFlag) {
params.push(o.literal(true));
if (allowUnits && input.unit) {
params.push(o.literal(input.unit));
}
return params;
@ -496,7 +446,7 @@ export class StylingBuilder {
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
const instructions: StylingInstruction[] = [];
if (this.hasBindings) {
if (compilerIsNewStylingInUse() && this._useDefaultSanitizer) {
if (this._useDefaultSanitizer) {
instructions.push(this._buildSanitizerFn());
}
const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
@ -630,3 +580,7 @@ function getStylePropInterpolationExpression(interpolation: Interpolation) {
return R3.stylePropInterpolateV;
}
}
function normalizePropName(prop: string): string {
return hyphenate(prop);
}

View File

@ -1,37 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* A temporary enum of states that inform the core whether or not
* to defer all styling instruction calls to the old or new
* styling implementation.
*/
export const enum CompilerStylingMode {
UseOld = 0,
UseBothOldAndNew = 1,
UseNew = 2,
}
let _stylingMode = 0;
/**
* Temporary function used to inform the existing styling algorithm
* code to delegate all styling instruction calls to the new refactored
* styling code.
*/
export function compilerSetStylingMode(mode: CompilerStylingMode) {
_stylingMode = mode;
}
export function compilerIsNewStylingInUse() {
return _stylingMode > CompilerStylingMode.UseOld;
}
export function compilerAllowOldStyling() {
return _stylingMode < CompilerStylingMode.UseNew;
}