diff --git a/src/qweb/compilation_context.ts b/src/qweb/compilation_context.ts
index c2656771..35dde215 100644
--- a/src/qweb/compilation_context.ts
+++ b/src/qweb/compilation_context.ts
@@ -196,11 +196,12 @@ export class CompilationContext {
const protectID = this.generateID();
this.rootContext.protectedScopeNumber++;
this.rootContext.shouldDefineScope = true;
- const scopeExpr = codeBlock
- ? `Object.create(scope);`
- : `Object.assign(Object.create(context), scope);`;
+ const scopeExpr = `Object.create(scope);`;
this.addLine(`let _origScope${protectID} = scope;`);
this.addLine(`scope = ${scopeExpr}`);
+ if (!codeBlock) {
+ this.addLine(`scope.__access_mode__ = 'ro';`);
+ }
return protectID;
}
stopProtectScope(protectID: number) {
diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts
index bf48c88a..662a841c 100644
--- a/src/qweb/qweb.ts
+++ b/src/qweb/qweb.ts
@@ -133,7 +133,7 @@ const UTILS: Utils = {
},
getScope(obj, property: string) {
const obj0 = obj;
- while (obj && !obj.hasOwnProperty(property)) {
+ while (obj && !obj.hasOwnProperty(property) && !(obj.hasOwnProperty('__access_mode__') && obj.__access_mode__ === 'ro')) {
const newObj = obj.__proto__;
if (!newObj || isComponent(newObj)) {
return obj0;
diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap
index 5e7fc722..6ea5f6d3 100644
--- a/tests/component/__snapshots__/component.test.ts.snap
+++ b/tests/component/__snapshots__/component.test.ts.snap
@@ -1282,7 +1282,8 @@ exports[`other directives with t-component t-set can't alter in t-call body 1`]
}
{
let _origScope4 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'iter').iter = 'inCall';
diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap
index 5f587349..3a0d8d16 100644
--- a/tests/qweb/__snapshots__/qweb.test.ts.snap
+++ b/tests/qweb/__snapshots__/qweb.test.ts.snap
@@ -774,12 +774,14 @@ exports[`misc global 1`] = `
}
{
let _origScope10 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
{
let _origScope13 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'foo').foo = 'aaa';
@@ -1150,7 +1152,8 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] =
let vn1 = h('div', p1, c1);
{
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
let c4 = [], p4 = {key:4};
@@ -1198,7 +1201,8 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
let vn1 = h('div', p1, c1);
{
let _origScope12 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
let c13 = [], p13 = {key:13};
@@ -1288,7 +1292,8 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
let vn1 = h('div', p1, c1);
{
let _origScope11 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'node').node = scope['root'];
@@ -1339,7 +1344,8 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
let key1 = i1;
{
let _origScope9 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'node').node = scope['subtree'];
@@ -1365,7 +1371,8 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
let vn1 = h('div', p1, c1);
{
let _origScope11 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'node').node = scope['root'];
@@ -1416,7 +1423,93 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
let key1 = i1;
{
let _origScope9 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
+ {
+ let c__0 = [];
+ utils.getScope(scope, 'node').node = scope['subtree'];
+ scope[utils.zero] = c__0;
+ }
+ let k10 = \`__10__\${key0}__\${key1}__\`;
+ this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
+ scope = _origScope9;
+ }
+ }
+ scope = _origScope8;
+}"
+`;
+
+exports[`t-call (template calling recursive template, part 4: with t-set recursive index 1`] = `
+"function anonymous(context, extra
+) {
+ // Template name: \\"Parent\\"
+ let utils = this.constructor.utils;
+ let scope = Object.create(context);
+ let h = this.h;
+ let c1 = [], p1 = {key:1};
+ let vn1 = h('div', p1, c1);
+ {
+ let _origScope11 = scope;
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
+ {
+ let c__0 = [];
+ utils.getScope(scope, 'recursive_idx').recursive_idx = 1;
+ utils.getScope(scope, 'node').node = scope['root'];
+ scope[utils.zero] = c__0;
+ }
+ this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
+ scope = _origScope11;
+ }
+ return vn1;
+}"
+`;
+
+exports[`t-call (template calling recursive template, part 4: with t-set recursive index 2`] = `
+"function anonymous(context, extra
+) {
+ // Template name: \\"nodeTemplate\\"
+ let utils = this.constructor.utils;
+ let scope = Object.create(context);
+ let h = this.h;
+ let c1 = extra.parentNode;
+ let key0 = extra.key || \\"\\";
+ let c2 = [], p2 = {key:\`\${key0}_2\`};
+ let vn2 = h('div', p2, c2);
+ c1.push(vn2);
+ scope.recursive_idx = scope.recursive_idx+1;
+ let c3 = [], p3 = {key:\`\${key0}_3\`};
+ let vn3 = h('p', p3, c3);
+ c2.push(vn3);
+ let _4 = scope['node'].val;
+ if (_4 != null) {
+ c3.push({text: _4});
+ }
+ c3.push({text: \` \`});
+ if (scope.recursive_idx != null) {
+ c3.push({text: scope.recursive_idx});
+ }
+ let _5 = scope['node'].children||[];
+ if (!_5) { throw new Error('QWeb error: Invalid loop expression')}
+ let _6 = _7 = _5;
+ if (!(_5 instanceof Array)) {
+ _6 = Object.keys(_5);
+ _7 = Object.values(_5);
+ }
+ let _length6 = _6.length;
+ let _origScope8 = scope;
+ scope = Object.create(scope);
+ for (let i1 = 0; i1 < _length6; i1++) {
+ scope.subtree_first = i1 === 0
+ scope.subtree_last = i1 === _length6 - 1
+ scope.subtree_index = i1
+ scope.subtree = _6[i1]
+ scope.subtree_value = _7[i1]
+ let key1 = i1;
+ {
+ let _origScope9 = scope;
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'node').node = scope['subtree'];
@@ -1442,7 +1535,8 @@ exports[`t-call (template calling scoped parameters 1`] = `
let vn1 = h('div', p1, c1);
{
let _origScope2 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'foo').foo = 42;
@@ -1517,7 +1611,8 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
utils.getScope(scope, 'val').val = scope['v'].val;
{
let _origScope8 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'val3').val3 = scope.val*3;
@@ -1564,7 +1659,8 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
else if (scope.v1==='elif') {
{
let _origScope6 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'v').v = 'success';
@@ -1602,7 +1698,8 @@ exports[`t-call (template calling with unused body 1`] = `
let h = this.h;
{
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
c__0.push({text: \`WHEEE\`});
@@ -1627,7 +1724,8 @@ exports[`t-call (template calling with unused setbody 1`] = `
let h = this.h;
{
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'qux').qux = 3;
@@ -1652,7 +1750,8 @@ exports[`t-call (template calling with used body 1`] = `
let h = this.h;
{
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
c__0.push({text: \`ok\`});
@@ -1678,7 +1777,8 @@ exports[`t-call (template calling with used set body 1`] = `
let vn1 = h('span', p1, c1);
{
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
utils.getScope(scope, 'foo').foo = 'ok';
@@ -1839,7 +1939,8 @@ exports[`t-esc t-esc is escaped 1`] = `
scope.var = c2
if (scope.var != null) {
let _origScope4 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.var = scope.var instanceof utils.VDomArray ? utils.vDomToString(scope.var) : scope.var;
c1.push({text: scope.var});
scope = _origScope4;
@@ -1875,7 +1976,8 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
let vn1 = h('div', p1, c1);
{
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
{
let c__0 = [];
let c4 = [], p4 = {key:4};
@@ -2948,7 +3050,8 @@ exports[`t-set set from body literal 1`] = `
scope.value = c1
if (scope.value != null) {
let _origScope2 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.value = scope.value instanceof utils.VDomArray ? utils.vDomToString(scope.value) : scope.value;
let vn3 = {text: scope.value};
result = vn3
@@ -2975,7 +3078,8 @@ exports[`t-set set from body lookup 1`] = `
scope.stuff = c2
if (scope.stuff != null) {
let _origScope4 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.stuff = scope.stuff instanceof utils.VDomArray ? utils.vDomToString(scope.stuff) : scope.stuff;
c1.push({text: scope.stuff});
scope = _origScope4;
@@ -3123,7 +3227,8 @@ exports[`t-set t-set with content and sub t-esc 1`] = `
scope.setvar = c2
if (scope.setvar != null) {
let _origScope4 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.setvar = scope.setvar instanceof utils.VDomArray ? utils.vDomToString(scope.setvar) : scope.setvar;
c1.push({text: scope.setvar});
scope = _origScope4;
@@ -3215,7 +3320,8 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 1 1`] = `
}
if (scope.ourvar != null) {
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.ourvar = scope.ourvar instanceof utils.VDomArray ? utils.vDomToString(scope.ourvar) : scope.ourvar;
c1.push({text: scope.ourvar});
scope = _origScope3;
@@ -3243,7 +3349,8 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 1 2`] = `
}
if (scope.ourvar != null) {
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.ourvar = scope.ourvar instanceof utils.VDomArray ? utils.vDomToString(scope.ourvar) : scope.ourvar;
c1.push({text: scope.ourvar});
scope = _origScope3;
@@ -3271,7 +3378,8 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 2 1`] = `
}
if (scope.ourvar != null) {
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.ourvar = scope.ourvar instanceof utils.VDomArray ? utils.vDomToString(scope.ourvar) : scope.ourvar;
c1.push({text: scope.ourvar});
scope = _origScope3;
@@ -3299,7 +3407,8 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 2 2`] = `
}
if (scope.ourvar != null) {
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.ourvar = scope.ourvar instanceof utils.VDomArray ? utils.vDomToString(scope.ourvar) : scope.ourvar;
c1.push({text: scope.ourvar});
scope = _origScope3;
@@ -3325,7 +3434,8 @@ exports[`t-set value priority 1`] = `
}
if (scope.value != null) {
let _origScope3 = scope;
- scope = Object.assign(Object.create(context), scope);
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
scope.value = scope.value instanceof utils.VDomArray ? utils.vDomToString(scope.value) : scope.value;
c1.push({text: scope.value});
scope = _origScope3;
diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts
index 6e95ab76..e5d75b88 100644
--- a/tests/qweb/qweb.test.ts
+++ b/tests/qweb/qweb.test.ts
@@ -944,6 +944,35 @@ describe("t-call (template calling", () => {
expect(recursiveFn.toString()).toMatchSnapshot();
});
+ test("recursive template, part 4: with t-set recursive index", () => {
+ qweb.addTemplates(`
+
a 2
b 3
c 4
d 5