Files
ladybird/Libraries/LibJS/Runtime/FunctionConstructor.cpp
Andreas Kling a8a1aba3ba LibJS: Replace ScopePusher with ScopeCollector
Replace the ScopePusher RAII class (which performed scope analysis
in its destructor chain during parsing) with a two-phase approach:

1. ScopeCollector builds a tree of ScopeRecord nodes during parsing
   via RAII ScopeHandle objects. It records declarations, identifier
   references, and flags, but does not resolve anything.

2. After parsing completes, ScopeCollector::analyze() walks the tree
   bottom-up and performs all resolution: propagate eval/with
   poisoning, resolve identifiers to locals/globals/arguments, hoist
   functions (Annex B.3.3), and build FunctionScopeData.

Key design decisions:
- ScopeRecord::ast_node is a RefPtr<ScopeNode> to prevent
  use-after-free when synthesize_binding_pattern re-parses an
  expression as a binding pattern (the original parse's scope records
  survive with stale AST node pointers).
- Parser::scope_collector() returns the override collector if set
  (for synthesize_binding_pattern's nested parser), ensuring all
  scope operations route to the outer parser's scope tree.
- FunctionNode::local_variables_names() delegates to its body's
  ScopeNode rather than copying at parse time, since analysis runs
  after parsing.
2026-02-10 02:05:20 +01:00

12 KiB