[IMP] qweb: turn handlers into function expressions only

For the sake of consistency with vanilla JS, and to allow some things
that were previously not possible.
This commit is contained in:
Samuel Degueldre
2021-11-12 12:56:07 +01:00
committed by Aaron Bohy
parent 756d32daa0
commit bca6afeb90
33 changed files with 280 additions and 227 deletions
+6 -24
View File
@@ -118,7 +118,6 @@ class BlockDescription {
// -----------------------------------------------------------------------------
// Compiler code
// -----------------------------------------------------------------------------
const FNAMEREGEXP = /^[$A-Z_][0-9A-Z_$]*$/i;
interface Context {
block: BlockDescription | null;
@@ -372,7 +371,7 @@ export class QWebCompiler {
const mapping = new Map<string, string>();
return tokens
.map((tok) => {
if (tok.varName) {
if (tok.varName && !tok.isLocal) {
if (!mapping.has(tok.varName)) {
const varId = this.generateId("v");
mapping.set(tok.varName, varId);
@@ -490,11 +489,6 @@ export class QWebCompiler {
}
generateHandlerCode(rawEvent: string, handler: string): string {
let args: string = "";
const name: string = handler.replace(/\(.*\)/, function (_args) {
args = _args.slice(1, -1);
return "";
});
const modifiers = rawEvent
.split(".")
.slice(1)
@@ -503,22 +497,7 @@ export class QWebCompiler {
if (modifiers.length) {
modifiersCode = `${modifiers.join(",")}, `;
}
const isMethodCall = name.match(FNAMEREGEXP);
if (isMethodCall) {
let handlerFn: string;
if (args) {
const argId = this.generateId("arg");
this.addLine(`const ${argId} = [${compileExpr(args)}];`);
handlerFn = `'${name}', ${argId}`;
} else {
handlerFn = `'${name}'`;
}
return `[${modifiersCode}ctx, ${handlerFn!}]`;
} else {
let code = this.captureExpression(handler);
code = `{const res = (() => { return ${code} })(); if (typeof res === 'function') { res(e) }}`;
return `[${modifiersCode}(e) => ${code}]`;
}
return `[${modifiersCode}${this.captureExpression(handler)}, ctx]`;
}
compileTDomNode(ast: ASTDomNode, ctx: Context) {
@@ -919,7 +898,10 @@ export class QWebCompiler {
const id = this.generateId(`callTemplate_`);
this.staticCalls.push({ id, template: subTemplate });
block = this.createBlock(block, "multi", ctx);
this.insertBlock(`${id}(ctx, node, ${key})`, block!, { ...ctx, forceNewBlock: !block });
this.insertBlock(`${id}.call(this, ctx, node, ${key})`, block!, {
...ctx,
forceNewBlock: !block,
});
}
if (ast.body && !ctx.isLast) {
this.addLine(`ctx = ctx.__proto__;`);
+8
View File
@@ -70,6 +70,7 @@ interface Token {
size?: number;
varName?: string;
replace?: Function;
isLocal?: boolean;
}
const STATIC_TOKEN_MAP: { [key: string]: TKind } = Object.assign(Object.create(null), {
@@ -325,6 +326,13 @@ export function compileExprToArray(expr: string): Token[] {
}
i++;
}
// Mark all variables that have been used locally.
// This assumes the expression has only one scope (incorrect but "good enough for now")
for (const token of tokens) {
if (token.type === "SYMBOL" && localVars.has(token.value)) {
token.isLocal = true;
}
}
return tokens;
}