fix(compiler): update compiler to generate new slot allocations (#25607)
PR Close #25607
This commit is contained in:

committed by
Matias Niemelä

parent
5c95b4b3a3
commit
27e2039630
@ -190,9 +190,6 @@ export class Identifiers {
|
||||
moduleName: CORE,
|
||||
};
|
||||
|
||||
// Reserve slots for pure functions
|
||||
static reserveSlots: o.ExternalReference = {name: 'ɵreserveSlots', moduleName: CORE};
|
||||
|
||||
// sanitization-related functions
|
||||
static sanitizeHtml: o.ExternalReference = {name: 'ɵzh', moduleName: CORE};
|
||||
static sanitizeStyle: o.ExternalReference = {name: 'ɵzs', moduleName: CORE};
|
||||
|
@ -110,7 +110,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
|
||||
this._valueConverter = new ValueConverter(
|
||||
constantPool, () => this.allocateDataSlot(),
|
||||
(numSlots: number): number => this._pureFunctionSlots += numSlots,
|
||||
(numSlots: number) => this.allocatePureFunctionSlots(numSlots),
|
||||
(name, localName, slot, value: o.ReadVarExpr) => {
|
||||
const pipeType = pipeTypeByName.get(name);
|
||||
if (pipeType) {
|
||||
@ -171,24 +171,27 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// This is the initial pass through the nodes of this template. In this pass, we
|
||||
// queue all creation mode and update mode instructions for generation in the second
|
||||
// pass. It's necessary to separate the passes to ensure local refs are defined before
|
||||
// resolving bindings.
|
||||
// resolving bindings. We also count bindings in this pass as we walk bound expressions.
|
||||
t.visitAll(this, nodes);
|
||||
|
||||
// Add total binding count to pure function count so pure function instructions are
|
||||
// generated with the correct slot offset when update instructions are processed.
|
||||
this._pureFunctionSlots += this._bindingSlots;
|
||||
|
||||
// Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
||||
// `pipeBind` update instructions), so we have to update the slot offsets manually
|
||||
// to account for bindings.
|
||||
this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
||||
|
||||
// Nested templates must be processed before creation instructions so template()
|
||||
// instructions can be generated with the correct internal const count.
|
||||
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
||||
|
||||
// Generate all the update mode instructions (e.g. resolve property or text bindings)
|
||||
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
|
||||
|
||||
// Generate all the creation mode instructions (e.g. resolve bindings in listeners)
|
||||
const creationStatements = this._creationCodeFns.map((fn: () => o.Statement) => fn());
|
||||
|
||||
// To count slots for the reserveSlots() instruction, all bindings must have been visited.
|
||||
if (this._pureFunctionSlots > 0) {
|
||||
creationStatements.push(
|
||||
instruction(null, R3.reserveSlots, [o.literal(this._pureFunctionSlots)]).toStmt());
|
||||
}
|
||||
// Generate all the update mode instructions (e.g. resolve property or text bindings)
|
||||
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
|
||||
|
||||
// Variable declaration must occur after binding resolution so we can generate context
|
||||
// instructions that build on each other. e.g. const b = x().$implicit(); const b = x();
|
||||
@ -635,6 +638,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
|
||||
// TODO(chuckj): runtime: security context?
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(input.sourceSpan, instruction, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(input.name),
|
||||
@ -722,6 +726,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const context = o.variable(CONTEXT_NAME);
|
||||
template.inputs.forEach(input => {
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(template.sourceSpan, R3.elementProperty, () => {
|
||||
return [
|
||||
o.literal(templateIndex), o.literal(input.name),
|
||||
@ -767,6 +772,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
this.creationInstruction(text.sourceSpan, R3.text, [o.literal(nodeIndex)]);
|
||||
|
||||
const value = text.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(
|
||||
text.sourceSpan, R3.textBinding,
|
||||
() => [o.literal(nodeIndex), this.convertPropertyBinding(o.variable(CONTEXT_NAME), value)]);
|
||||
@ -800,7 +806,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
|
||||
getConstCount() { return this._dataIndex; }
|
||||
|
||||
getVarCount() { return this._bindingSlots + this._pureFunctionSlots; }
|
||||
getVarCount() { return this._pureFunctionSlots; }
|
||||
|
||||
private bindingContext() { return `${this._bindingContext++}`; }
|
||||
|
||||
@ -829,10 +835,18 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
|
||||
}
|
||||
|
||||
private allocatePureFunctionSlots(numSlots: number): number {
|
||||
const originalSlots = this._pureFunctionSlots;
|
||||
this._pureFunctionSlots += numSlots;
|
||||
return originalSlots;
|
||||
}
|
||||
|
||||
private allocateBindingSlots(value: AST) {
|
||||
this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
|
||||
}
|
||||
|
||||
private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
|
||||
o.Expression {
|
||||
if (!skipBindFn) this._bindingSlots++;
|
||||
|
||||
const interpolationFn =
|
||||
value instanceof Interpolation ? interpolate : () => error('Unexpected interpolation');
|
||||
|
||||
@ -875,6 +889,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
|
||||
class ValueConverter extends AstMemoryEfficientTransformer {
|
||||
private _pipeBindExprs: FunctionCall[] = [];
|
||||
|
||||
constructor(
|
||||
private constantPool: ConstantPool, private allocateSlot: () => number,
|
||||
private allocatePureFunctionSlots: (numSlots: number) => number,
|
||||
@ -897,11 +913,21 @@ class ValueConverter extends AstMemoryEfficientTransformer {
|
||||
const convertedArgs: AST[] =
|
||||
isVarLength ? this.visitAll([new LiteralArray(pipe.span, args)]) : this.visitAll(args);
|
||||
|
||||
return new FunctionCall(pipe.span, target, [
|
||||
const pipeBindExpr = new FunctionCall(pipe.span, target, [
|
||||
new LiteralPrimitive(pipe.span, slot),
|
||||
new LiteralPrimitive(pipe.span, pureFunctionSlot),
|
||||
...convertedArgs,
|
||||
]);
|
||||
this._pipeBindExprs.push(pipeBindExpr);
|
||||
return pipeBindExpr;
|
||||
}
|
||||
|
||||
updatePipeSlotOffsets(bindingSlots: number) {
|
||||
this._pipeBindExprs.forEach((pipe: FunctionCall) => {
|
||||
// update the slot offset arg (index 1) to account for binding slots
|
||||
const slotOffset = pipe.args[1] as LiteralPrimitive;
|
||||
(slotOffset.value as number) += bindingSlots;
|
||||
});
|
||||
}
|
||||
|
||||
visitLiteralArray(array: LiteralArray, context: any): AST {
|
||||
|
Reference in New Issue
Block a user