- When working with deeply nested imports, compile errors led to long error messages or complete hangs by compiling the same module over and over again. - Fix for a function expression scope issue. Signed-off-by: Felix Fietkau <nbd@nbd.name>
81 lines
3.1 KiB
Diff
81 lines
3.1 KiB
Diff
From: Felix Fietkau <nbd@nbd.name>
|
|
Date: Mon, 6 Jul 2026 18:23:08 +0200
|
|
Subject: [PATCH] compiler: scope named function expression names to their own
|
|
body
|
|
|
|
A named function expression (e.g. `!function e(){...}()`) shares its parsing
|
|
path with function declarations, and declared its name in the enclosing scope
|
|
just as a declaration does. For an expression this is wrong: the name is only
|
|
meant to be visible inside its own body for self-reference. Binding it in the
|
|
enclosing scope leaked the name and, by pushing an extra local, shifted the
|
|
slot count so the initialize_local for a let/const initialised by the
|
|
expression marked the wrong slot -- the real variable stayed uninitialised and
|
|
its first use raised "Can't access lexical declaration 'x' before
|
|
initialization". Self-recursion was likewise broken.
|
|
|
|
Bind the name in the enclosing scope only for declarations. For a named
|
|
expression, rename the function's own callee slot to the name after
|
|
initialising its compiler, so the body can reference itself while the enclosing
|
|
scope is left untouched.
|
|
|
|
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|
---
|
|
|
|
--- a/compiler.c
|
|
+++ b/compiler.c
|
|
@@ -1949,20 +1949,27 @@ uc_compiler_compile_funcexpr_common(uc_c
|
|
return;
|
|
}
|
|
|
|
- slot = uc_compiler_resolve_funcstub(compiler, name);
|
|
+ /* A function declaration binds its name in the enclosing scope. A named
|
|
+ * function expression must not: its name is only visible inside its own
|
|
+ * body (for self-reference), and declaring it in the enclosing scope
|
|
+ * would both leak it and shift the local slots, corrupting the
|
|
+ * initialisation state of a `let`/`const` the expression initialises. */
|
|
+ if (require_name) {
|
|
+ slot = uc_compiler_resolve_funcstub(compiler, name);
|
|
|
|
- if (slot > -1) {
|
|
- compiler->locals.entries[slot].funcstub = false;
|
|
- }
|
|
- else {
|
|
- slot = uc_compiler_declare_local(compiler, name, false);
|
|
+ if (slot > -1) {
|
|
+ compiler->locals.entries[slot].funcstub = false;
|
|
+ }
|
|
+ else {
|
|
+ slot = uc_compiler_declare_local(compiler, name, false);
|
|
|
|
- if (slot == -1)
|
|
- uc_compiler_initialize_local(compiler);
|
|
- else if (compiler->locals.entries[slot].constant)
|
|
- uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
|
|
- "Redeclaration of constant '%s'",
|
|
- ucv_string_get(name));
|
|
+ if (slot == -1)
|
|
+ uc_compiler_initialize_local(compiler);
|
|
+ else if (compiler->locals.entries[slot].constant)
|
|
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
|
|
+ "Redeclaration of constant '%s'",
|
|
+ ucv_string_get(name));
|
|
+ }
|
|
}
|
|
}
|
|
else if (require_name) {
|
|
@@ -1976,6 +1983,14 @@ uc_compiler_compile_funcexpr_common(uc_c
|
|
compiler->program,
|
|
uc_compiler_is_strict(compiler));
|
|
|
|
+ /* Bind a named function expression's name to its own callee slot so the
|
|
+ * body can reference itself for recursion, without touching the enclosing
|
|
+ * scope. Declarations keep the name in the enclosing scope (handled above). */
|
|
+ if (name && !require_name) {
|
|
+ ucv_put(fncompiler.locals.entries[0].name);
|
|
+ fncompiler.locals.entries[0].name = ucv_get(name);
|
|
+ }
|
|
+
|
|
fncompiler.parent = compiler;
|
|
fncompiler.parser = compiler->parser;
|
|
fncompiler.exprstack = compiler->exprstack;
|