--- /dev/null
+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;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 7 Jul 2026 09:34:16 +0200
+Subject: [PATCH] lib: avoid allocating print buffer before NULL check in
+ uc_error_message_indent
+
+Move the print buffer allocation after the guard that returns early on a
+NULL message, so no buffer is allocated on that path. This keeps the
+function leak-free when callers invoke it without having produced an error
+string.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/lib.c
++++ b/lib.c
+@@ -220,13 +220,14 @@ uc_error_context_format(uc_stringbuf_t *
+
+ void
+ uc_error_message_indent(char **msg) {
+- uc_stringbuf_t *buf = xprintbuf_new();
++ uc_stringbuf_t *buf;
+ char *s, *p, *nl;
+ size_t len;
+
+ if (!msg || !*msg)
+ return;
+
++ buf = xprintbuf_new();
+ s = *msg;
+ len = strlen(s);
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 6 Jul 2026 11:12:32 +0200
+Subject: [PATCH] compiler: prevent unbounded recompilation on failed
+ module imports
+
+A source's exports.offset of -1 marks a module whose compilation has been
+entered but not completed. When a module failed to compile, uc_compiler_finish
+freed its function and the error cascaded up through the importers via error
+recovery, leaving each involved source at offset -1 with its function gone. The
+re-entry guard only recognised an in-progress module by finding its function, so
+those freed modules were recompiled again on every subsequent import. Across a
+wide import graph this recompilation multiplies and the compiler appears to hang.
+
+Detect the offset -1 marker directly instead of relying on the presence of a
+function: report a circular dependency when the function is still around, and
+propagate the failure otherwise, rather than recursing into the module again.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/compiler.c
++++ b/compiler.c
+@@ -3479,15 +3479,26 @@ uc_compiler_compile_module_source(uc_com
+
+ uc_program_function_foreach(compiler->program, fn) {
+ if (uc_program_function_source(fn) == source) {
+- if (source->exports.offset == (size_t)-1)
+- uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+- "Circular dependency");
+-
+ loaded = true;
+ break;
+ }
+ }
+
++ /* An offset of -1 marks a source whose compilation was entered but not yet
++ * completed. Re-entering it is either a circular dependency (its function is
++ * still present) or a module whose earlier compilation failed and freed its
++ * function. Recompiling in either case recurses without bound across a large
++ * import graph, so report the cycle and propagate the failure instead. */
++ if (source->exports.offset == (size_t)-1) {
++ if (loaded)
++ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
++ "Circular dependency");
++ else if (errp)
++ xasprintf(errp, "Module previously failed to compile\n");
++
++ return false;
++ }
++
+ if (!loaded) {
+ /* We do not yet support linking precompiled modules at compile time,
+ turn static import operation into dynamic load one. */