Skip to content

Commit 9d06869

Browse files
karaFrederikSchlemmer
authored andcommitted
fix(compiler): update compiler to generate new slot allocations (angular#25607)
PR Close angular#25607
1 parent 11224e2 commit 9d06869

File tree

9 files changed

+62
-52
lines changed

9 files changed

+62
-52
lines changed

packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts

+14-20
Original file line numberDiff line numberDiff line change
@@ -364,13 +364,12 @@ describe('compiler compliance', () => {
364364
if (rf & 1) {
365365
$r3$.ɵelement(0, "div");
366366
$r3$.ɵpipe(1,"pipe");
367-
$r3$.ɵreserveSlots(10);
368367
}
369368
if (rf & 2) {
370-
$r3$.ɵelementProperty(0, "ternary", $r3$.ɵbind((ctx.cond ? $r3$.ɵpureFunction1(6, _c0, ctx.a): _c1)));
369+
$r3$.ɵelementProperty(0, "ternary", $r3$.ɵbind((ctx.cond ? $r3$.ɵpureFunction1(8, _c0, ctx.a): _c1)));
371370
$r3$.ɵelementProperty(0, "pipe", $r3$.ɵbind($r3$.ɵpipeBind3(1, 4, ctx.value, 1, 2)));
372-
$r3$.ɵelementProperty(0, "and", $r3$.ɵbind((ctx.cond && $r3$.ɵpureFunction1(8, _c0, ctx.b))));
373-
$r3$.ɵelementProperty(0, "or", $r3$.ɵbind((ctx.cond || $r3$.ɵpureFunction1(10, _c0, ctx.c))));
371+
$r3$.ɵelementProperty(0, "and", $r3$.ɵbind((ctx.cond && $r3$.ɵpureFunction1(10, _c0, ctx.b))));
372+
$r3$.ɵelementProperty(0, "or", $r3$.ɵbind((ctx.cond || $r3$.ɵpureFunction1(12, _c0, ctx.c))));
374373
}
375374
}
376375
`;
@@ -656,7 +655,7 @@ describe('compiler compliance', () => {
656655
template: function MyComponent_Template(rf, ctx) {
657656
if (rf & 1) {
658657
$r3$.ɵelementStart(0, "ul", null, $c1$);
659-
$r3$.ɵtemplate(2, MyComponent_li_Template_2, 2, 1, null, $c2$);
658+
$r3$.ɵtemplate(2, MyComponent_li_Template_2, 2, 2, null, $c2$);
660659
$r3$.ɵelementEnd();
661660
}
662661
},
@@ -718,10 +717,9 @@ describe('compiler compliance', () => {
718717
template: function MyApp_Template(rf, ctx) {
719718
if (rf & 1) {
720719
$r3$.ɵelement(0, "my-comp");
721-
$r3$.ɵreserveSlots(2);
722720
}
723721
if (rf & 2) {
724-
$r3$.ɵelementProperty(0, "names", $r3$.ɵbind($r3$.ɵpureFunction1(2, $e0_ff$, ctx.customName)));
722+
$r3$.ɵelementProperty(0, "names", $r3$.ɵbind($r3$.ɵpureFunction1(1, $e0_ff$, ctx.customName)));
725723
}
726724
},
727725
directives: [MyComp]
@@ -800,12 +798,11 @@ describe('compiler compliance', () => {
800798
template: function MyApp_Template(rf, ctx) {
801799
if (rf & 1) {
802800
$r3$.ɵelement(0, "my-comp");
803-
$r3$.ɵreserveSlots(10);
804801
}
805802
if (rf & 2) {
806803
$r3$.ɵelementProperty(
807804
0, "names",
808-
$r3$.ɵbind($r3$.ɵpureFunctionV(10, $e0_ff$, [ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8])));
805+
$r3$.ɵbind($r3$.ɵpureFunctionV(1, $e0_ff$, [ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8])));
809806
}
810807
},
811808
directives: [MyComp]
@@ -864,10 +861,9 @@ describe('compiler compliance', () => {
864861
template: function MyApp_Template(rf, ctx) {
865862
if (rf & 1) {
866863
$r3$.ɵelement(0, "object-comp");
867-
$r3$.ɵreserveSlots(2);
868864
}
869865
if (rf & 2) {
870-
$r3$.ɵelementProperty(0, "config", $r3$.ɵbind($r3$.ɵpureFunction1(2, $e0_ff$, ctx.name)));
866+
$r3$.ɵelementProperty(0, "config", $r3$.ɵbind($r3$.ɵpureFunction1(1, $e0_ff$, ctx.name)));
871867
}
872868
},
873869
directives: [ObjectComp]
@@ -932,12 +928,11 @@ describe('compiler compliance', () => {
932928
template: function MyApp_Template(rf, ctx) {
933929
if (rf & 1) {
934930
$r3$.ɵelement(0, "nested-comp");
935-
$r3$.ɵreserveSlots(7);
936931
}
937932
if (rf & 2) {
938933
$r3$.ɵelementProperty(
939934
0, "config",
940-
$r3$.ɵbind($r3$.ɵpureFunction2(7, $e0_ff_2$, ctx.name, $r3$.ɵpureFunction1(4, $e0_ff_1$, $r3$.ɵpureFunction1(2, $e0_ff$, ctx.duration)))));
935+
$r3$.ɵbind($r3$.ɵpureFunction2(5, $e0_ff_2$, ctx.name, $r3$.ɵpureFunction1(3, $e0_ff_1$, $r3$.ɵpureFunction1(1, $e0_ff$, ctx.duration)))));
941936
}
942937
},
943938
directives: [NestedComp]
@@ -1254,11 +1249,10 @@ describe('compiler compliance', () => {
12541249
$r3$.ɵtext(4);
12551250
$r3$.ɵpipe(5, "myPipe");
12561251
$r3$.ɵelementEnd();
1257-
$r3$.ɵreserveSlots(15);
12581252
}
12591253
if (rf & 2) {
1260-
$r3$.ɵtextBinding(0, $r3$.ɵinterpolation1("", $r3$.ɵpipeBind2(1, 3, $r3$.ɵpipeBind2(2, 6, ctx.name, ctx.size), ctx.size), ""));
1261-
$r3$.ɵtextBinding(4, $r3$.ɵinterpolation1("", $r3$.ɵpipeBindV(5, 13 , $r3$.ɵpureFunction1(15, $c0$, ctx.name)), ""));
1254+
$r3$.ɵtextBinding(0, $r3$.ɵinterpolation1("", $r3$.ɵpipeBind2(1, 2, $r3$.ɵpipeBind2(2, 5, ctx.name, ctx.size), ctx.size), ""));
1255+
$r3$.ɵtextBinding(4, $r3$.ɵinterpolation1("", $r3$.ɵpipeBindV(5, 8, $r3$.ɵpureFunction1(15, $c0$, ctx.name)), ""));
12621256
}
12631257
},
12641258
pipes: [MyPurePipe, MyPipe]
@@ -1373,7 +1367,7 @@ describe('compiler compliance', () => {
13731367
if (rf & 1) {
13741368
$r3$.ɵelementStart(0, "div");
13751369
$r3$.ɵtext(1);
1376-
$r3$.ɵtemplate(2, MyComponent_div_span_Template_2, 2, 1, null, $c2$);
1370+
$r3$.ɵtemplate(2, MyComponent_div_span_Template_2, 2, 3, null, $c2$);
13771371
$r3$.ɵelement(3, "span", null, $c4$);
13781372
$r3$.ɵelementEnd();
13791373
}
@@ -1396,7 +1390,7 @@ describe('compiler compliance', () => {
13961390
if (rf & 1) {
13971391
$r3$.ɵelement(0, "div", null, $c1$);
13981392
$r3$.ɵtext(2);
1399-
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 5, 1, null, $c2$);
1393+
$r3$.ɵtemplate(3, MyComponent_div_Template_3, 5, 2, null, $c2$);
14001394
$r3$.ɵelement(4, "div", null, $c3$);
14011395
}
14021396
if (rf & 2) {
@@ -1460,7 +1454,7 @@ describe('compiler compliance', () => {
14601454
if (rf & 1) {
14611455
$i0$.ɵelementStart(0, "div");
14621456
$i0$.ɵelement(1, "div", null, $c1$);
1463-
$i0$.ɵtemplate(3, MyComponent_div_span_Template_3, 2, 1, null, $c2$);
1457+
$i0$.ɵtemplate(3, MyComponent_div_span_Template_3, 2, 2, null, $c2$);
14641458
$i0$.ɵelementEnd();
14651459
}
14661460
if (rf & 2) {
@@ -1839,7 +1833,7 @@ describe('compiler compliance', () => {
18391833
$r3$.ɵtext(2);
18401834
$r3$.ɵelementEnd();
18411835
$r3$.ɵelementStart(3, "ul");
1842-
$r3$.ɵtemplate(4, MyComponent_li_li_Template_4, 2, 1, null, $c1$);
1836+
$r3$.ɵtemplate(4, MyComponent_li_li_Template_4, 2, 2, null, $c1$);
18431837
$r3$.ɵelementEnd();
18441838
$r3$.ɵelementEnd();
18451839
}

packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ describe('compiler compliance: template', () => {
155155
// ...
156156
template:function MyComponent_Template(rf, ctx){
157157
if (rf & 1) {
158-
$i0$.ɵtemplate(0, MyComponent_span_Template_0, 2, 1, null, _c0);
158+
$i0$.ɵtemplate(0, MyComponent_span_Template_0, 2, 2, null, _c0);
159159
}
160160
if (rf & 2) {
161161
$i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items));
@@ -211,7 +211,7 @@ describe('compiler compliance: template', () => {
211211
function MyComponent_div_Template_0(rf, ctx) {
212212
if (rf & 1) {
213213
$i0$.ɵelementStart(0, "div");
214-
$i0$.ɵtemplate(1, MyComponent_div_span_Template_1, 2, 1, null, $c1$);
214+
$i0$.ɵtemplate(1, MyComponent_div_span_Template_1, 2, 2, null, $c1$);
215215
$i0$.ɵelementEnd();
216216
}
217217
if (rf & 2) {
@@ -279,7 +279,7 @@ describe('compiler compliance: template', () => {
279279
function MyComponent_div_div_Template_1(rf, ctx) {
280280
if (rf & 1) {
281281
$i0$.ɵelementStart(0, "div");
282-
$i0$.ɵtemplate(1, MyComponent_div_div_div_Template_1, 2, 1, null, _c0);
282+
$i0$.ɵtemplate(1, MyComponent_div_div_div_Template_1, 2, 2, null, _c0);
283283
$i0$.ɵelementEnd();
284284
}
285285
if (rf & 2) {

packages/compiler/src/render3/r3_identifiers.ts

-3
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@ export class Identifiers {
190190
moduleName: CORE,
191191
};
192192

193-
// Reserve slots for pure functions
194-
static reserveSlots: o.ExternalReference = {name: 'ɵreserveSlots', moduleName: CORE};
195-
196193
// sanitization-related functions
197194
static sanitizeHtml: o.ExternalReference = {name: 'ɵzh', moduleName: CORE};
198195
static sanitizeStyle: o.ExternalReference = {name: 'ɵzs', moduleName: CORE};

packages/compiler/src/render3/view/template.ts

+40-14
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
110110

111111
this._valueConverter = new ValueConverter(
112112
constantPool, () => this.allocateDataSlot(),
113-
(numSlots: number): number => this._pureFunctionSlots += numSlots,
113+
(numSlots: number) => this.allocatePureFunctionSlots(numSlots),
114114
(name, localName, slot, value: o.ReadVarExpr) => {
115115
const pipeType = pipeTypeByName.get(name);
116116
if (pipeType) {
@@ -171,24 +171,27 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
171171
// This is the initial pass through the nodes of this template. In this pass, we
172172
// queue all creation mode and update mode instructions for generation in the second
173173
// pass. It's necessary to separate the passes to ensure local refs are defined before
174-
// resolving bindings.
174+
// resolving bindings. We also count bindings in this pass as we walk bound expressions.
175175
t.visitAll(this, nodes);
176176

177+
// Add total binding count to pure function count so pure function instructions are
178+
// generated with the correct slot offset when update instructions are processed.
179+
this._pureFunctionSlots += this._bindingSlots;
180+
181+
// Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
182+
// `pipeBind` update instructions), so we have to update the slot offsets manually
183+
// to account for bindings.
184+
this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
185+
177186
// Nested templates must be processed before creation instructions so template()
178187
// instructions can be generated with the correct internal const count.
179188
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
180189

181-
// Generate all the update mode instructions (e.g. resolve property or text bindings)
182-
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
183-
184190
// Generate all the creation mode instructions (e.g. resolve bindings in listeners)
185191
const creationStatements = this._creationCodeFns.map((fn: () => o.Statement) => fn());
186192

187-
// To count slots for the reserveSlots() instruction, all bindings must have been visited.
188-
if (this._pureFunctionSlots > 0) {
189-
creationStatements.push(
190-
instruction(null, R3.reserveSlots, [o.literal(this._pureFunctionSlots)]).toStmt());
191-
}
193+
// Generate all the update mode instructions (e.g. resolve property or text bindings)
194+
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
192195

193196
// Variable declaration must occur after binding resolution so we can generate context
194197
// 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
635638

636639
// TODO(chuckj): runtime: security context?
637640
const value = input.value.visit(this._valueConverter);
641+
this.allocateBindingSlots(value);
638642
this.updateInstruction(input.sourceSpan, instruction, () => {
639643
return [
640644
o.literal(elementIndex), o.literal(input.name),
@@ -722,6 +726,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
722726
const context = o.variable(CONTEXT_NAME);
723727
template.inputs.forEach(input => {
724728
const value = input.value.visit(this._valueConverter);
729+
this.allocateBindingSlots(value);
725730
this.updateInstruction(template.sourceSpan, R3.elementProperty, () => {
726731
return [
727732
o.literal(templateIndex), o.literal(input.name),
@@ -767,6 +772,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
767772
this.creationInstruction(text.sourceSpan, R3.text, [o.literal(nodeIndex)]);
768773

769774
const value = text.value.visit(this._valueConverter);
775+
this.allocateBindingSlots(value);
770776
this.updateInstruction(
771777
text.sourceSpan, R3.textBinding,
772778
() => [o.literal(nodeIndex), this.convertPropertyBinding(o.variable(CONTEXT_NAME), value)]);
@@ -800,7 +806,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
800806

801807
getConstCount() { return this._dataIndex; }
802808

803-
getVarCount() { return this._bindingSlots + this._pureFunctionSlots; }
809+
getVarCount() { return this._pureFunctionSlots; }
804810

805811
private bindingContext() { return `${this._bindingContext++}`; }
806812

@@ -829,10 +835,18 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
829835
this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
830836
}
831837

838+
private allocatePureFunctionSlots(numSlots: number): number {
839+
const originalSlots = this._pureFunctionSlots;
840+
this._pureFunctionSlots += numSlots;
841+
return originalSlots;
842+
}
843+
844+
private allocateBindingSlots(value: AST) {
845+
this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
846+
}
847+
832848
private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
833849
o.Expression {
834-
if (!skipBindFn) this._bindingSlots++;
835-
836850
const interpolationFn =
837851
value instanceof Interpolation ? interpolate : () => error('Unexpected interpolation');
838852

@@ -875,6 +889,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
875889
}
876890

877891
class ValueConverter extends AstMemoryEfficientTransformer {
892+
private _pipeBindExprs: FunctionCall[] = [];
893+
878894
constructor(
879895
private constantPool: ConstantPool, private allocateSlot: () => number,
880896
private allocatePureFunctionSlots: (numSlots: number) => number,
@@ -897,11 +913,21 @@ class ValueConverter extends AstMemoryEfficientTransformer {
897913
const convertedArgs: AST[] =
898914
isVarLength ? this.visitAll([new LiteralArray(pipe.span, args)]) : this.visitAll(args);
899915

900-
return new FunctionCall(pipe.span, target, [
916+
const pipeBindExpr = new FunctionCall(pipe.span, target, [
901917
new LiteralPrimitive(pipe.span, slot),
902918
new LiteralPrimitive(pipe.span, pureFunctionSlot),
903919
...convertedArgs,
904920
]);
921+
this._pipeBindExprs.push(pipeBindExpr);
922+
return pipeBindExpr;
923+
}
924+
925+
updatePipeSlotOffsets(bindingSlots: number) {
926+
this._pipeBindExprs.forEach((pipe: FunctionCall) => {
927+
// update the slot offset arg (index 1) to account for binding slots
928+
const slotOffset = pipe.args[1] as LiteralPrimitive;
929+
(slotOffset.value as number) += bindingSlots;
930+
});
905931
}
906932

907933
visitLiteralArray(array: LiteralArray, context: any): AST {

packages/core/src/core_render3_private_export.ts

-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ export {
8787
elementProperty as ɵelementProperty,
8888
projectionDef as ɵprojectionDef,
8989
reference as ɵreference,
90-
reserveSlots as ɵreserveSlots,
9190
elementAttribute as ɵelementAttribute,
9291
elementStyling as ɵelementStyling,
9392
elementStylingMap as ɵelementStylingMap,

packages/core/src/render3/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ export {
7676

7777
reference,
7878

79-
reserveSlots,
80-
8179
embeddedViewStart,
8280
embeddedViewEnd,
8381
detectChanges,

packages/core/src/render3/instructions.ts

-3
Original file line numberDiff line numberDiff line change
@@ -2559,9 +2559,6 @@ export function bind<T>(value: T): T|NO_CHANGE {
25592559
return bindingUpdated(viewData[BINDING_INDEX]++, value) ? value : NO_CHANGE;
25602560
}
25612561

2562-
// TODO(kara): Remove this when updating the compiler (cannot remove without breaking JIT test)
2563-
export function reserveSlots(numSlots: number) {}
2564-
25652562
/**
25662563
* Create interpolation bindings with a variable number of expressions.
25672564
*

packages/core/src/render3/jit/environment.ts

-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
9090
'ɵquery': r3.query,
9191
'ɵqueryRefresh': r3.queryRefresh,
9292
'ɵregisterContentQuery': r3.registerContentQuery,
93-
'ɵreserveSlots': r3.reserveSlots,
9493
'ɵreference': r3.reference,
9594
'ɵelementStyling': r3.elementStyling,
9695
'ɵelementStylingMap': r3.elementStylingMap,

packages/core/src/render3/pipe.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function getPipeDef(name: string, registry: PipeDefList | null): PipeDefInternal
6868
* the pipe only when an input to the pipe changes.
6969
*
7070
* @param index Pipe index where the pipe was stored on creation.
71-
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
71+
* @param slotOffset the offset in the reserved slot space
7272
* @param v1 1st argument to {@link PipeTransform#transform}.
7373
*/
7474
export function pipeBind1(index: number, slotOffset: number, v1: any): any {
@@ -84,7 +84,7 @@ export function pipeBind1(index: number, slotOffset: number, v1: any): any {
8484
* the pipe only when an input to the pipe changes.
8585
*
8686
* @param index Pipe index where the pipe was stored on creation.
87-
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
87+
* @param slotOffset the offset in the reserved slot space
8888
* @param v1 1st argument to {@link PipeTransform#transform}.
8989
* @param v2 2nd argument to {@link PipeTransform#transform}.
9090
*/
@@ -101,7 +101,7 @@ export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any):
101101
* the pipe only when an input to the pipe changes.
102102
*
103103
* @param index Pipe index where the pipe was stored on creation.
104-
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
104+
* @param slotOffset the offset in the reserved slot space
105105
* @param v1 1st argument to {@link PipeTransform#transform}.
106106
* @param v2 2nd argument to {@link PipeTransform#transform}.
107107
* @param v3 4rd argument to {@link PipeTransform#transform}.
@@ -120,7 +120,7 @@ export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v
120120
* the pipe only when an input to the pipe changes.
121121
*
122122
* @param index Pipe index where the pipe was stored on creation.
123-
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
123+
* @param slotOffset the offset in the reserved slot space
124124
* @param v1 1st argument to {@link PipeTransform#transform}.
125125
* @param v2 2nd argument to {@link PipeTransform#transform}.
126126
* @param v3 3rd argument to {@link PipeTransform#transform}.
@@ -141,7 +141,7 @@ export function pipeBind4(
141141
* the pipe only when an input to the pipe changes.
142142
*
143143
* @param index Pipe index where the pipe was stored on creation.
144-
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
144+
* @param slotOffset the offset in the reserved slot space
145145
* @param values Array of arguments to pass to {@link PipeTransform#transform} method.
146146
*/
147147
export function pipeBindV(index: number, slotOffset: number, values: any[]): any {

0 commit comments

Comments
 (0)