Skip to content

Commit 04daa83

Browse files
committed
compiler: implement function declaration hoisting
Hoist top-level function declarations to make them available before their source position, enabling forward references and mutual recursion without requiring specific declaration order. The implementation uses three compilation phases within each scope: - Phase 1 saves parser state, scans tokens to collect names of top-level function declarations, let/const variable declarations, and import * as locals, skipping over initializer expressions via depth tracking, and restores parser state. - After phase 1, a null-initialized local slot is declared for each collected name (functions, variables, and import locals alike, preserving constness). - Phase 2 saves parser state, re-scans to find function declarations, compiles each function body into the pre-declared slot, and restores parser state. Both scanning phases share a single top-level statement scanner (uc_compiler_scan_toplevel_stmts) that handles brace/alt-block depth tracking and statement boundary detection, with a mode parameter controlling whether to collect names or compile function bodies. - Phase 3 compiles the scope body normally, skipping function declarations that were already compiled in phase 2. Forward references from hoisted function bodies resolve correctly because all local slots exist as upvalue targets during phase 2 function compilation. Pre-declaring variable and import names alongside function names follows ECMAScript semantics: all bindings are created at scope entry before any code runs. Without this, a hoisted function referencing a let/const variable or import * as local declared earlier in source order would fail to resolve it as an upvalue during phase 2. Hoisting applies only to declarations that are direct statements of the scope body. Functions inside control flow or braced blocks are not hoisted. Export declarations reuse compile_declexpr with an export flag to register each variable immediately after declaration. For import * as declarations with pre-declared slots, the import code skips initialize_local and instead stores the module object into the existing slot via I_SLOC. Parser state save/restore preserves the error buffer alongside other state, preventing duplicate error messages when phase 1/2 scanning encounters syntax errors that phase 3 reports again. The last lineinfo byte is also preserved to prevent corruption from uc_source_line_update() modifying the tail entry during scanning. Signed-off-by: Felix Fietkau <nbd@nbd.name>
1 parent d810e57 commit 04daa83

5 files changed

Lines changed: 909 additions & 19 deletions

File tree

0 commit comments

Comments
 (0)