perf(ivy): chain styling instructions (#33837)

Adds support for chaining of `styleProp`, `classProp` and `stylePropInterpolateX` instructions whenever possible which should help generate less code. Note that one complication here is for `stylePropInterpolateX` instructions where we have to break into multiple chains if there are other styling instructions inbetween the interpolations which helps maintain the execution order.

PR Close #33837
This commit is contained in:
Kristiyan Kostadinov
2019-11-18 20:13:23 +01:00
committed by Alex Rickabaugh
parent f69c6e204a
commit 8a052dc858
6 changed files with 465 additions and 94 deletions

View File

@ -708,8 +708,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const limit = stylingInstructions.length - 1;
for (let i = 0; i <= limit; i++) {
const instruction = stylingInstructions[i];
this._bindingSlots += instruction.allocateBindingSlots;
this.processStylingInstruction(elementIndex, instruction, false);
this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction);
}
// the reason why `undefined` is used is because the renderer understands this as a
@ -1071,25 +1070,30 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
});
}
private processStylingInstruction(
elementIndex: number, instruction: StylingInstruction|null, createMode: boolean) {
private processStylingUpdateInstruction(
elementIndex: number, instruction: StylingInstruction|null) {
let allocateBindingSlots = 0;
if (instruction) {
if (createMode) {
this.creationInstruction(instruction.sourceSpan, instruction.reference, () => {
return instruction.params(value => this.convertPropertyBinding(value)) as o.Expression[];
});
} else {
this.updateInstructionWithAdvance(
elementIndex, instruction.sourceSpan, instruction.reference, () => {
return instruction
.params(value => {
return (instruction.supportsInterpolation && value instanceof Interpolation) ?
const calls: ChainableBindingInstruction[] = [];
instruction.calls.forEach(call => {
allocateBindingSlots += call.allocateBindingSlots;
calls.push({
sourceSpan: call.sourceSpan,
value: () => {
return call
.params(
value => (call.supportsInterpolation && value instanceof Interpolation) ?
this.getUpdateInstructionArguments(value) :
this.convertPropertyBinding(value);
}) as o.Expression[];
});
}
this.convertPropertyBinding(value)) as o.Expression[];
}
});
});
this.updateInstructionChainWithAdvance(elementIndex, instruction.reference, calls);
}
return allocateBindingSlots;
}
private creationInstruction(
@ -1127,8 +1131,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
this._updateCodeFns.push(() => {
const calls = bindings.map(property => {
const fnParams = [property.value(), ...(property.params || [])];
const value = property.value();
const fnParams = Array.isArray(value) ? value : [value];
if (property.params) {
fnParams.push(...property.params);
}
if (property.name) {
// We want the property name to always be the first function parameter.
fnParams.unshift(o.literal(property.name));
}
return fnParams;
@ -2045,7 +2054,7 @@ function hasTextChildrenOnly(children: t.Node[]): boolean {
interface ChainableBindingInstruction {
name?: string;
sourceSpan: ParseSourceSpan|null;
value: () => o.Expression;
value: () => o.Expression | o.Expression[];
params?: any[];
}