Skip to content

Commit 8038483

Browse files
committed
cleanup variable scoping
1 parent 6acf619 commit 8038483

27 files changed

Lines changed: 389 additions & 271 deletions

src/compiler/code_generator.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ export class CodeGenerator {
578578
if (modifiers.length) {
579579
modifiersCode = `${modifiers.join(",")}, `;
580580
}
581-
return `[${modifiersCode}${this.captureExpression(handler)}, ctx]`;
581+
return `[${modifiersCode}${compileExpr(handler)}, ctx]`;
582582
}
583583

584584
compileTDomNode(ast: ASTDomNode, ctx: Context): string {
@@ -863,7 +863,10 @@ export class CodeGenerator {
863863
block = this.createBlock(block, "list", ctx);
864864
this.target.loopLevel++;
865865
const loopVar = `i${this.target.loopLevel}`;
866-
this.addLine(`ctx = Object.create(ctx);`);
866+
const ctxVar = generateId("ctx");
867+
this.addLine(`const ${ctxVar} = ctx;`);
868+
ctx.ctxVar = ctxVar
869+
// this.addLine(`ctx = Object.create(ctx);`);
867870
const vals = `v_block${block.id}`;
868871
const keys = `k_block${block.id}`;
869872
const l = `l_block${block.id}`;
@@ -876,6 +879,7 @@ export class CodeGenerator {
876879
}
877880
this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);
878881
this.target.indentLevel++;
882+
this.addLine(`const ctx = Object.create(${ctxVar});`);
879883
this.addLine(`ctx[\`${ast.elem}\`] = ${keys}[${loopVar}];`);
880884
if (!ast.hasNoFirst) {
881885
this.addLine(`ctx[\`${ast.elem}_first\`] = ${loopVar} === 0;`);
@@ -929,9 +933,9 @@ export class CodeGenerator {
929933
this.target.indentLevel--;
930934
this.target.loopLevel--;
931935
this.addLine(`}`);
932-
if (!ctx.isLast) {
933-
this.addLine(`ctx = ctx.__proto__;`);
934-
}
936+
// if (!ctx.isLast) {
937+
// this.addLine(`ctx = ${ctxVar};`);
938+
// }
935939
this.insertBlock("l", block, ctx);
936940
return block.varName;
937941
}
@@ -1006,6 +1010,7 @@ export class CodeGenerator {
10061010
? this.formatPropObject(ast.attrs, ast.attrsTranslationCtx, ctx.translationCtx)
10071011
: [];
10081012
let ctxVar = ctx.ctxVar || "ctx";
1013+
let subCtxVar = generateId("ctx");
10091014
const isDynamic = INTERP_REGEXP.test(ast.name);
10101015
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
10111016
if (block && !forceNewBlock) {
@@ -1023,6 +1028,7 @@ export class CodeGenerator {
10231028
this.helpers.add("zero");
10241029
attrs.push(`[zero]: ${zeroStr}`);
10251030
} else {
1031+
this.addLine(`const ${subCtxVar} = ${ctxVar}`);
10261032
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
10271033
this.addLine(`${ctxVar}[isBoundary] = 1;`);
10281034
this.helpers.add("isBoundary");
@@ -1032,6 +1038,7 @@ export class CodeGenerator {
10321038
this.helpers.add("zero");
10331039
this.addLine(`${ctxVar}[zero] = () => ${bl};`);
10341040
}
1041+
this.addLine(`${ctxVar} = ${subCtxVar}`);
10351042
}
10361043
}
10371044

src/runtime/rendering/template_helpers.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,17 @@ function prepareList(collection: unknown): [unknown[], unknown[], number, undefi
8181
}
8282

8383
const isBoundary = Symbol("isBoundary");
84-
8584
function setContextValue(ctx: { [key: string]: any }, key: string, value: any): void {
86-
const ctx0 = ctx;
87-
while (!ctx.hasOwnProperty(key) && !ctx.hasOwnProperty(isBoundary)) {
88-
const newCtx = ctx.__proto__;
89-
if (!newCtx) {
90-
ctx = ctx0;
91-
break;
85+
// const ctx0 = ctx;
86+
if (!(key in ctx)) {
87+
ctx[key] = value;
88+
} else {
89+
while (!ctx.hasOwnProperty(key) && !ctx.hasOwnProperty(isBoundary)) {
90+
ctx = ctx.__proto__;
9291
}
93-
ctx = newCtx;
92+
ctx[key] = value;
9493
}
95-
ctx[key] = value;
94+
9695
}
9796

9897
function toNumber(val: string): number | string {

tests/compiler/__snapshots__/event_handling.test.ts.snap

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ exports[`t-on can bind handlers with arguments 1`] = `
2222
let block1 = createBlock(\`<button block-handler-0="click">Click</button>\`);
2323
2424
return function template(ctx, node, key = "") {
25-
const v1 = ctx['add'];
26-
let hdlr1 = [()=>v1(5), ctx];
25+
let hdlr1 = [()=>ctx['add'](5), ctx];
2726
return block1([hdlr1]);
2827
}
2928
}"
@@ -37,8 +36,7 @@ exports[`t-on can bind handlers with empty object 1`] = `
3736
let block1 = createBlock(\`<button block-handler-0="click">Click</button>\`);
3837
3938
return function template(ctx, node, key = "") {
40-
const v1 = ctx['doSomething'];
41-
let hdlr1 = [()=>v1({}), ctx];
39+
let hdlr1 = [()=>ctx['doSomething']({}), ctx];
4240
return block1([hdlr1]);
4341
}
4442
}"
@@ -52,8 +50,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string)
5250
let block1 = createBlock(\`<button block-handler-0="click">Click</button>\`);
5351
5452
return function template(ctx, node, key = "") {
55-
const v1 = ctx['doSomething'];
56-
let hdlr1 = [()=>v1({}), ctx];
53+
let hdlr1 = [()=>ctx['doSomething']({}), ctx];
5754
return block1([hdlr1]);
5855
}
5956
}"
@@ -69,15 +66,14 @@ exports[`t-on can bind handlers with empty object (with non empty inner string)
6966
let block3 = createBlock(\`<li><a block-handler-0="click">link</a></li>\`);
7067
7168
return function template(ctx, node, key = "") {
72-
ctx = Object.create(ctx);
69+
const ctx1 = ctx;
7370
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['someval']);;
7471
for (let i1 = 0; i1 < l_block2; i1++) {
72+
const ctx = Object.create(ctx1);
7573
ctx[\`action\`] = k_block2[i1];
7674
ctx[\`action_index\`] = i1;
7775
const key1 = ctx['action_index'];
78-
const v1 = ctx['activate'];
79-
const v2 = ctx['action'];
80-
let hdlr1 = [()=>v1(v2), ctx];
76+
let hdlr1 = [()=>ctx['activate'](ctx['action']), ctx];
8177
c_block2[i1] = withKey(block3([hdlr1]), key1);
8278
}
8379
const b2 = list(c_block2);
@@ -94,8 +90,7 @@ exports[`t-on can bind handlers with object arguments 1`] = `
9490
let block1 = createBlock(\`<button block-handler-0="click">Click</button>\`);
9591
9692
return function template(ctx, node, key = "") {
97-
const v1 = ctx['add'];
98-
let hdlr1 = [()=>v1({val:5}), ctx];
93+
let hdlr1 = [()=>ctx['add']({val:5}), ctx];
9994
return block1([hdlr1]);
10095
}
10196
}"
@@ -139,9 +134,10 @@ exports[`t-on handler is bound to proper owner, part 2 1`] = `
139134
let block2 = createBlock(\`<button block-handler-0="click">Click</button>\`);
140135
141136
return function template(ctx, node, key = "") {
142-
ctx = Object.create(ctx);
137+
const ctx1 = ctx;
143138
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1]);;
144139
for (let i1 = 0; i1 < l_block1; i1++) {
140+
const ctx = Object.create(ctx1);
145141
ctx[\`value\`] = k_block1[i1];
146142
const key1 = ctx['value'];
147143
let hdlr1 = [ctx['add'], ctx];
@@ -186,9 +182,10 @@ exports[`t-on handler is bound to proper owner, part 4 1`] = `
186182
const callTemplate_1 = app.getTemplate(\`sub\`);
187183
188184
return function template(ctx, node, key = "") {
189-
ctx = Object.create(ctx);
185+
const ctx1 = ctx;
190186
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1]);;
191187
for (let i1 = 0; i1 < l_block1; i1++) {
188+
const ctx = Object.create(ctx1);
192189
ctx[\`value\`] = k_block1[i1];
193190
ctx[\`value_first\`] = i1 === 0;
194191
ctx[\`value_last\`] = i1 === k_block1.length - 1;
@@ -330,14 +327,13 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent modifier in t-f
330327
let block3 = createBlock(\`<a href="#" block-handler-0="click.prevent"> Edit <block-child-0/></a>\`);
331328
332329
return function template(ctx, node, key = "") {
333-
ctx = Object.create(ctx);
330+
const ctx1 = ctx;
334331
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['projects']);;
335332
for (let i1 = 0; i1 < l_block2; i1++) {
333+
const ctx = Object.create(ctx1);
336334
ctx[\`project\`] = k_block2[i1];
337335
const key1 = ctx['project'];
338-
const v1 = ctx['onEdit'];
339-
const v2 = ctx['project'];
340-
let hdlr1 = ["prevent", _ev=>v1(v2.id,_ev), ctx];
336+
let hdlr1 = ["prevent", _ev=>ctx['onEdit'](ctx['project'].id,_ev), ctx];
341337
const b4 = safeOutput(ctx['project'].name);
342338
c_block2[i1] = withKey(block3([hdlr1], [b4]), key1);
343339
}
@@ -399,8 +395,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = `
399395
let block1 = createBlock(\`<button block-handler-0="click">Click</button>\`);
400396
401397
return function template(ctx, node, key = "") {
402-
const v1 = ctx['state'];
403-
let hdlr1 = [()=>v1.incrementCounter(2), ctx];
398+
let hdlr1 = [()=>ctx['state'].incrementCounter(2), ctx];
404399
return block1([hdlr1]);
405400
}
406401
}"
@@ -414,8 +409,7 @@ exports[`t-on t-on with inline statement 1`] = `
414409
let block1 = createBlock(\`<button block-handler-0="click">Click</button>\`);
415410
416411
return function template(ctx, node, key = "") {
417-
const v1 = ctx['state'];
418-
let hdlr1 = [()=>v1.counter++, ctx];
412+
let hdlr1 = [()=>ctx['state'].counter++, ctx];
419413
return block1([hdlr1]);
420414
}
421415
}"
@@ -429,8 +423,7 @@ exports[`t-on t-on with inline statement, part 2 1`] = `
429423
let block1 = createBlock(\`<button block-handler-0="click">Toggle</button>\`);
430424
431425
return function template(ctx, node, key = "") {
432-
const v1 = ctx['state'];
433-
let hdlr1 = [()=>v1.flag=!v1.flag, ctx];
426+
let hdlr1 = [()=>ctx['state'].flag=!ctx['state'].flag, ctx];
434427
return block1([hdlr1]);
435428
}
436429
}"
@@ -444,9 +437,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = `
444437
let block1 = createBlock(\`<button block-handler-0="click">Toggle</button>\`);
445438
446439
return function template(ctx, node, key = "") {
447-
const v1 = ctx['state'];
448-
const v2 = ctx['someFunction'];
449-
let hdlr1 = [()=>v1.n=v2(3), ctx];
440+
let hdlr1 = [()=>ctx['state'].n=ctx['someFunction'](3), ctx];
450441
return block1([hdlr1]);
451442
}
452443
}"
@@ -504,9 +495,7 @@ exports[`t-on t-on, with arguments and t-call 2`] = `
504495
let block1 = createBlock(\`<p block-handler-0="click">lucas</p>\`);
505496
506497
return function template(ctx, node, key = "") {
507-
const v1 = ctx['this'];
508-
const v2 = ctx['value'];
509-
let hdlr1 = [()=>v1.update(v2), ctx];
498+
let hdlr1 = [()=>ctx['this'].update(ctx['value']), ctx];
510499
return block1([hdlr1]);
511500
}
512501
}"

tests/compiler/__snapshots__/misc.test.ts.snap

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,28 @@ exports[`misc complex template 1`] = `
3030
if (ctx['batch'].state=='preparing') {
3131
b4 = block4();
3232
}
33-
ctx = Object.create(ctx);
33+
const ctx1 = ctx;
3434
const [k_block5, v_block5, l_block5, c_block5] = prepareList(ctx['batch'].slot_ids.filter(_slot=>_slot.build_id.id&&!_slot.trigger_id.manual&&(ctx['options'].trigger_display[_slot.trigger_id.id])));;
3535
for (let i1 = 0; i1 < l_block5; i1++) {
36+
const ctx = Object.create(ctx1);
3637
ctx[\`slot\`] = k_block5[i1];
3738
const key1 = ctx['slot'].id;
3839
c_block5[i1] = withKey(comp1({class: ctx['slot_container'],slot: ctx['slot']}, key + \`__1__\${key1}\`, node, this, null), key1);
3940
}
40-
ctx = ctx.__proto__;
4141
b5 = list(c_block5);
42-
ctx = Object.create(ctx);
42+
const ctx2 = ctx;
4343
const [k_block7, v_block7, l_block7, c_block7] = prepareList([1,2,3,4]);;
4444
for (let i1 = 0; i1 < l_block7; i1++) {
45+
const ctx = Object.create(ctx2);
4546
ctx[\`x\`] = k_block7[i1];
4647
const key1 = ctx['x'];
4748
c_block7[i1] = withKey(block8(), key1);
4849
}
49-
ctx = ctx.__proto__;
5050
b7 = list(c_block7);
51-
ctx = Object.create(ctx);
51+
const ctx3 = ctx;
5252
const [k_block9, v_block9, l_block9, c_block9] = prepareList(ctx['commit_links']);;
5353
for (let i1 = 0; i1 < l_block9; i1++) {
54+
const ctx = Object.create(ctx3);
5455
ctx[\`commit_link\`] = k_block9[i1];
5556
const key1 = ctx['commit_link'].id;
5657
let b11, b12, b13, b14, b15, b16;
@@ -96,9 +97,10 @@ exports[`misc global 1`] = `
9697
return function template(ctx, node, key = "") {
9798
ctx = Object.create(ctx);
9899
ctx[isBoundary] = 1
99-
ctx = Object.create(ctx);
100+
const ctx1 = ctx;
100101
const [k_block2, v_block2, l_block2, c_block2] = prepareList([4,5,6]);;
101102
for (let i1 = 0; i1 < l_block2; i1++) {
103+
const ctx = Object.create(ctx1);
102104
ctx[\`value\`] = k_block2[i1];
103105
ctx[\`value_first\`] = i1 === 0;
104106
ctx[\`value_last\`] = i1 === k_block2.length - 1;
@@ -107,23 +109,26 @@ exports[`misc global 1`] = `
107109
const key1 = ctx['value'];
108110
const b5 = safeOutput(ctx['value']);
109111
const b4 = block4([], [b5]);
112+
const ctx2 = ctx
110113
ctx = Object.create(ctx);
111114
ctx[isBoundary] = 1;
115+
const ctx3 = ctx
112116
ctx = Object.create(ctx);
113117
ctx[isBoundary] = 1;
114118
setContextValue(ctx, "foo", 'aaa');
119+
ctx = ctx3
115120
const b8 = callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}\`);
116121
ctx = ctx.__proto__;
117122
const b9 = callTemplate_2.call(this, ctx, node, key + \`__2__\${key1}\`);
118123
setContextValue(ctx, "foo", 'bbb');
119124
const b10 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`);
120125
const b7 = multi([b8, b9, b10]);
121126
ctx[zero] = () => b7;
127+
ctx = ctx2
122128
const b6 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`);
123129
ctx = ctx.__proto__;
124130
c_block2[i1] = withKey(multi([b4, b6]), key1);
125131
}
126-
ctx = ctx.__proto__;
127132
const b2 = list(c_block2);
128133
const b11 = callTemplate_5.call(this, ctx, node, key + \`__5\`);
129134
return block1([], [b2, b11]);
@@ -221,16 +226,16 @@ exports[`misc other complex template 1`] = `
221226
let b2, b3, b6, b18, b22, b28, b29, b31, b32;
222227
let attr1 = \`/runbot/\${ctx['project'].slug}\`;
223228
b2 = safeOutput(ctx['project'].name);
224-
ctx = Object.create(ctx);
229+
const ctx1 = ctx;
225230
const [k_block3, v_block3, l_block3, c_block3] = prepareList(ctx['projects']);;
226231
for (let i1 = 0; i1 < l_block3; i1++) {
232+
const ctx = Object.create(ctx1);
227233
ctx[\`project\`] = k_block3[i1];
228234
const key1 = ctx['project'].id;
229235
let hdlr1 = [ctx['selectProject'](ctx['project']), ctx];
230236
const b5 = safeOutput(ctx['project'].name);
231237
c_block3[i1] = withKey(block4([hdlr1], [b5]), key1);
232238
}
233-
ctx = ctx.__proto__;
234239
b3 = list(c_block3);
235240
if (ctx['user']) {
236241
let b7, b8;
@@ -261,17 +266,17 @@ exports[`misc other complex template 1`] = `
261266
let hdlr2 = [ctx['toggleSettingsMenu'], ctx];
262267
let hdlr3 = [ctx['toggleMore'], ctx];
263268
if (ctx['categories']&&ctx['categories'].length>1) {
264-
ctx = Object.create(ctx);
269+
const ctx2 = ctx;
265270
const [k_block19, v_block19, l_block19, c_block19] = prepareList(ctx['categories']);;
266271
for (let i1 = 0; i1 < l_block19; i1++) {
272+
const ctx = Object.create(ctx2);
267273
ctx[\`category\`] = k_block19[i1];
268274
const key1 = ctx['category'].id;
269275
let attr6 = ctx['category'].id;
270276
let prop1 = new Boolean(ctx['category'].id==ctx['options'].active_category_id);
271277
const b21 = safeOutput(ctx['category'].name);
272278
c_block19[i1] = withKey(block20([attr6, prop1], [b21]), key1);
273279
}
274-
ctx = ctx.__proto__;
275280
const b19 = list(c_block19);
276281
b18 = block18([], [b19]);
277282
}
@@ -282,9 +287,10 @@ exports[`misc other complex template 1`] = `
282287
let hdlr6 = [ctx['clearSearch'], ctx];
283288
let ref2 = createRef(ctx['settings_menu']);
284289
if (ctx['triggers']) {
285-
ctx = Object.create(ctx);
290+
const ctx3 = ctx;
286291
const [k_block23, v_block23, l_block23, c_block23] = prepareList(ctx['triggers']);;
287292
for (let i1 = 0; i1 < l_block23; i1++) {
293+
const ctx = Object.create(ctx3);
288294
ctx[\`trigger\`] = k_block23[i1];
289295
const key1 = ctx['trigger'].id;
290296
let b25;
@@ -300,7 +306,6 @@ exports[`misc other complex template 1`] = `
300306
}
301307
c_block23[i1] = withKey(multi([b25]), key1);
302308
}
303-
ctx = ctx.__proto__;
304309
const b23 = list(c_block23);
305310
let hdlr8 = [ctx['triggerAll'], ctx];
306311
let hdlr9 = [ctx['triggerNone'], ctx];

tests/compiler/__snapshots__/qweb_memory.test.ts.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ exports[`memory t-foreach does not leak stuff in global scope 1`] = `
99
let block1 = createBlock(\`<p><block-child-0/></p>\`);
1010
1111
return function template(ctx, node, key = "") {
12-
ctx = Object.create(ctx);
12+
const ctx1 = ctx;
1313
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);;
1414
for (let i1 = 0; i1 < l_block2; i1++) {
15+
const ctx = Object.create(ctx1);
1516
ctx[\`item\`] = k_block2[i1];
1617
ctx[\`item_index\`] = i1;
1718
const key1 = ctx['item_index'];

0 commit comments

Comments
 (0)