From 438b21df3fce6e8dd4c44db233d3a3bfa599c9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 4 Jun 2019 14:23:26 +0200 Subject: [PATCH] [REF] qweb: improve variable handling --- src/qweb_core.ts | 28 ++++--- src/qweb_directives.ts | 107 +++++++++++++------------- tests/__snapshots__/qweb.test.ts.snap | 34 ++++---- tests/qweb.test.ts | 2 +- 4 files changed, 88 insertions(+), 83 deletions(-) diff --git a/src/qweb_core.ts b/src/qweb_core.ts index 5abaf5d9..c85476cc 100644 --- a/src/qweb_core.ts +++ b/src/qweb_core.ts @@ -518,7 +518,8 @@ export class QWeb { // dynamic attributes if (name.startsWith("t-att-")) { let attName = name.slice(6); - let formattedValue = ctx.formatExpression(ctx.getValue(value!)); + const v = ctx.getValue(value); + let formattedValue = v.id || ctx.formatExpression(v); if ( formattedValue[0] === "{" && formattedValue[formattedValue.length - 1] === "}" @@ -623,11 +624,21 @@ export class QWeb { //------------------------------------------------------------------------------ // Compilation Context //------------------------------------------------------------------------------ +export interface QWebExprVar { + id: string; + expr: string; +} + +export interface QWebXMLVar { + xml: NodeList; +} + +export type QWebVar = QWebExprVar | QWebXMLVar; + export class Context { nextID: number = 1; code: string[] = []; - variables: { [key: string]: any } = {}; - definedVariables: { [key: string]: string } = {}; + variables: { [key: string]: QWebVar } = {}; escaping: boolean = false; parentNode: number | null = null; rootNode: number | null = null; @@ -744,13 +755,10 @@ export class Context { } else if (c.match(/\W/) && invar.length) { // TODO: Should check for possible spaces before dot if (chars[invarPos - 1] !== "." && RESERVED_WORDS.indexOf(invar) < 0) { - if (!(invar in this.definedVariables)) { - invar = - WORD_REPLACEMENT[invar] || - (invar in this.variables && - this.formatExpression(this.variables[invar])) || - "context['" + invar + "']"; - } + invar = + WORD_REPLACEMENT[invar] || + (this.variables[invar] && (this.variables[invar]).id) || + "context['" + invar + "']"; } r += invar; invar = ""; diff --git a/src/qweb_directives.ts b/src/qweb_directives.ts index 866273a1..386ead28 100644 --- a/src/qweb_directives.ts +++ b/src/qweb_directives.ts @@ -1,4 +1,4 @@ -import { Context, QWeb, UTILS } from "./qweb_core"; +import { Context, QWeb, UTILS, QWebExprVar } from "./qweb_core"; /** * Owl QWeb Directives @@ -14,7 +14,6 @@ import { Context, QWeb, UTILS } from "./qweb_core"; * - t-log */ - //------------------------------------------------------------------------------ // t-esc and t-raw //------------------------------------------------------------------------------ @@ -30,42 +29,43 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Context) { return; } - if (typeof value === "string") { - let exprID = value; - if (!(value in ctx.definedVariables)) { - exprID = `_${ctx.generateID()}`; - ctx.addLine(`var ${exprID} = ${ctx.formatExpression(value)};`); - } - ctx.addIf(`${exprID} || ${exprID} === 0`); - if (!ctx.parentNode) { - throw new Error("Should not have a text node without a parent"); - } - if (ctx.escaping) { - ctx.addLine(`c${ctx.parentNode}.push({text: ${exprID}});`); - } else { - let fragID = ctx.generateID(); - ctx.addLine(`var frag${fragID} = this.utils.getFragment(${exprID})`); - let tempNodeID = ctx.generateID(); - ctx.addLine(`var p${tempNodeID} = {hook: {`); - ctx.addLine( - ` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),` - ); - ctx.addLine(`}};`); - ctx.addLine(`var vn${tempNodeID} = h('div', p${tempNodeID})`); - ctx.addLine(`c${ctx.parentNode}.push(vn${tempNodeID});`); - } - if (node.childNodes.length) { - ctx.addElse(); - qweb._compileChildren(node, ctx); - } - ctx.closeIf(); - return; - } - if (value instanceof NodeList) { - for (let node of Array.from(value)) { + if (value.xml instanceof NodeList) { + for (let node of Array.from(value.xml)) { qweb._compileNode(node, ctx); } + return; } + let exprID: string; + if (typeof value === "string") { + exprID = `_${ctx.generateID()}`; + ctx.addLine(`var ${exprID} = ${ctx.formatExpression(value)};`); + } else { + exprID = value.id; + } + ctx.addIf(`${exprID} || ${exprID} === 0`); + if (!ctx.parentNode) { + throw new Error("Should not have a text node without a parent"); + } + if (ctx.escaping) { + ctx.addLine(`c${ctx.parentNode}.push({text: ${exprID}});`); + } else { + let fragID = ctx.generateID(); + ctx.addLine(`var frag${fragID} = this.utils.getFragment(${exprID})`); + let tempNodeID = ctx.generateID(); + ctx.addLine(`var p${tempNodeID} = {hook: {`); + ctx.addLine( + ` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),` + ); + ctx.addLine(`}};`); + ctx.addLine(`var vn${tempNodeID} = h('div', p${tempNodeID})`); + ctx.addLine(`c${ctx.parentNode}.push(vn${tempNodeID});`); + } + if (node.childNodes.length) { + ctx.addElse(); + qweb._compileChildren(node, ctx); + } + + ctx.closeIf(); } QWeb.addDirective({ @@ -109,15 +109,21 @@ QWeb.addDirective({ if (value) { const formattedValue = ctx.formatExpression(value); if (ctx.variables.hasOwnProperty(variable)) { - ctx.addLine(`${ctx.variables[variable]} = ${formattedValue}`); + ctx.addLine( + `${(ctx.variables[variable]).id} = ${formattedValue}` + ); } else { const varName = `_${ctx.generateID()}`; ctx.addLine(`var ${varName} = ${formattedValue};`); - ctx.definedVariables[varName] = formattedValue; - ctx.variables[variable] = varName; + ctx.variables[variable] = { + id: varName, + expr: formattedValue + }; } } else { - ctx.variables[variable] = node.childNodes; + ctx.variables[variable] = { + xml: node.childNodes + }; } return true; } @@ -189,30 +195,27 @@ QWeb.addDirective({ tempCtx.nextID = ctx.rootContext.nextID; qweb._compileNode(nodeCopy, tempCtx); const vars = Object.assign({}, ctx.variables, tempCtx.variables); - var definedVariables = Object.assign( - {}, - ctx.definedVariables, - tempCtx.definedVariables - ); ctx.rootContext.nextID = tempCtx.nextID; // open new scope, if necessary - const hasNewVariables = Object.keys(definedVariables).length > 0; + const hasNewVariables = Object.keys(tempCtx.variables).length > 0; if (hasNewVariables) { ctx.addLine("{"); ctx.indent(); - } - - // add new variables, if any - for (let key in definedVariables) { - ctx.addLine(`let ${key} = ${definedVariables[key]}`); + // add new variables, if any + for (let key in tempCtx.variables) { + const v = tempCtx.variables[key]; + if ((v).expr) { + ctx.addLine(`let ${(v).id} = ${(v).expr};`); + } + // todo: handle XML variables... + } } // compile sub template const subCtx = ctx .subContext("caller", nodeCopy) - .subContext("variables", Object.create(vars)) - .subContext("definedVariables", Object.create(definedVariables)); + .subContext("variables", Object.create(vars)); qweb._compileNode(nodeTemplate.elem, subCtx); diff --git a/tests/__snapshots__/qweb.test.ts.snap b/tests/__snapshots__/qweb.test.ts.snap index 59eedb80..04689fbb 100644 --- a/tests/__snapshots__/qweb.test.ts.snap +++ b/tests/__snapshots__/qweb.test.ts.snap @@ -585,13 +585,13 @@ exports[`misc global 1`] = ` c5.push({text: _6}); } { - let _11 = 'bbb' + let _11 = 'bbb'; var _13 = 'agüero'; let c14 = [], p14 = {key:14,attrs:{\\"falló\\": _13},on:{}}; var vn14 = h('Año', p14, c14); c1.push(vn14); { - let _15 = 'aaa' + let _15 = 'aaa'; let c16 = [], p16 = {key:16,on:{}}; var vn16 = h('span', p16, c16); c14.push(vn16); @@ -611,16 +611,13 @@ exports[`misc global 1`] = ` c17.push({text: \`foo default\`}); } var _19 = 'bbb'; - { - let _19 = 'bbb' - let c20 = [], p20 = {key:20,on:{}}; - var vn20 = h('span', p20, c20); - c14.push(vn20); - if (_19 || _19 === 0) { - c20.push({text: _19}); - } else { - c20.push({text: \`foo default\`}); - } + let c20 = [], p20 = {key:20,on:{}}; + var vn20 = h('span', p20, c20); + c14.push(vn20); + if (_19 || _19 === 0) { + c20.push({text: _19}); + } else { + c20.push({text: \`foo default\`}); } } } @@ -756,11 +753,8 @@ exports[`t-call (template calling inherit context 1`] = ` let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); var _2 = 1; - { - let _2 = 1 - if (_2 || _2 === 0) { - c1.push({text: _2}); - } + if (_2 || _2 === 0) { + c1.push({text: _2}); } return vn1; }" @@ -773,7 +767,7 @@ exports[`t-call (template calling scoped parameters 1`] = ` let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); { - let _2 = 42 + let _2 = 42; c1.push({text: \`ok\`}); } var _3 = context['foo']; @@ -800,7 +794,7 @@ exports[`t-call (template calling with unused setbody 1`] = ` ) { var h = this.utils.h; { - let _1 = 3 + let _1 = 3; let c2 = [], p2 = {key:2}; var vn2 = h('div', p2, c2); c2.push({text: \`ok\`}); @@ -827,7 +821,7 @@ exports[`t-call (template calling with used set body 1`] = ` let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); { - let _2 = 'ok' + let _2 = 'ok'; if (_2 || _2 === 0) { c1.push({text: _2}); } diff --git a/tests/qweb.test.ts b/tests/qweb.test.ts index 4decaf7a..a555c8db 100644 --- a/tests/qweb.test.ts +++ b/tests/qweb.test.ts @@ -184,7 +184,7 @@ describe("t-set", () => { test("t-set and t-if", () => { qweb.addTemplate( "test", - `
+ `
grimbergen
`