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(` + +
+ + + + +
+
+ +

+ + + + + +
+ +
+ `); + const root = { val: "a", children: [{ val: "b", children: [{ val: "c" , children: [{val: "d"}] }] }] }; + const expected = + "

a 2

b 3

c 4

d 5

"; + expect(renderToString(qweb, "Parent", { root })).toBe(expected); + const recursiveFn = Object.values(qweb.subTemplates)[0] as any; + expect(recursiveFn.toString()).toMatchSnapshot(); + }); + test("t-call, global templates", () => { QWeb.registerTemplate("abcd", '
'); qweb.addTemplate("john", `desk`);