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.
This commit is contained in:
Andreas Kling
2026-02-09 11:27:47 +01:00
committed by Alexander Kalenik
parent beb9186282
commit a8a1aba3ba
Notes: github-actions[bot] 2026-02-10 01:08:13 +00:00
15 changed files with 873 additions and 809 deletions

View File

@@ -78,7 +78,7 @@ Program (script) @4:1
│ │ └─ body
│ │ └─ BlockStatement @22:33
│ └─ ExpressionStatement @23:5
│ └─ Identifier "j" [variable:2] (var) @23:5
│ └─ Identifier "j" [variable:2] @23:5
├─ FunctionDeclaration "destructuring_bindings" @27:1
│ └─ body
│ └─ FunctionBody @28:5