Skip to main content

bamts_compiler/
lower.rs

1//! Direct lowering from the runtime TypeScript/JavaScript AST to verified
2//! canonical bytecode ([`bamts_bytecode`]).
3//!
4//! # Scope
5//!
6//! This lowering targets the production 36-opcode instruction algebra (`u32`
7//! register/constant/function/pc indices, register-keyed properties, variadic
8//! calls through one arguments array, explicit closures with capture arrays,
9//! the iterator protocol, environment access, module exports, and a
10//! definite-initialization verifier). It expresses the dynamic runtime kernel
11//! the corpus exercises without special-casing syntax: computed and private
12//! property access, non-empty arrays and spread, iteration (`for`/`of`,
13//! `for`/`in`, `for await`), destructuring, template literals, regular
14//! expressions, globals, closures, classes with prototypes/accessors/private
15//! names, `this`/`arguments`/`new.target`, generators and async via
16//! [`Instruction::Suspend`], and module exports.
17//!
18//! No runtime construct that the instruction set can model is silently
19//! approximated. The handful of forms that remain genuinely inexpressible in
20//! this ISA (or that carry no runtime semantics at all) are reported as typed
21//! [`UnsupportedConstruct`] values at their rejection sites; each is documented
22//! there and does not occur in the executable corpus.
23//!
24//! ## Scope and environment model
25//!
26//! Module top-level bindings live in the module environment record, which this
27//! lowering names through [`Instruction::LoadGlobal`]/[`Instruction::StoreGlobal`]
28//! (and [`Instruction::TypeOfGlobal`] for `typeof` of a possibly-undeclared
29//! name). Function-local bindings each own a fixed register *home*;
30//! initialization and assignment copy into that home with [`Instruction::Move`]
31//! so a binding read after a branch or across a loop back-edge is provably
32//! initialized on every path, exactly what the verifier's
33//! definite-initialization fixpoint requires. A nested function that reads a
34//! function-local binding of an enclosing function captures it: the free
35//! variables are computed syntactically, snapshotted into a captures array in
36//! the enclosing function, and bound to the callee's leading capture registers.
37//! Arrow functions additionally capture `this`, `arguments`, and `new.target`
38//! from their lexical enclosing function.
39
40#![allow(clippy::too_many_lines)]
41
42use std::collections::{BTreeSet, HashMap, HashSet};
43use std::error::Error;
44use std::fmt;
45
46use bamts_bytecode::{
47    AccessorKind, BigIntLiteral, BinaryOp, Constant, ConstantId, EcmaString, EcmaStringBuilder,
48    ExceptionHandler, Function, FunctionFlags, FunctionId, Instruction, IteratorKind,
49    MAX_CONSTANTS, MAX_FUNCTIONS, MAX_INSTRUCTIONS, MAX_REGISTERS, Module, NumberBits, Pc,
50    Register, UnaryOp, Verified, VerifyError,
51};
52
53pub use crate::program::{
54    ExecutableModuleProvenance, ExecutableProgram, ProgramLowerError, ProgramLowerErrorKind,
55    ProgramLowerPhase, lower_program,
56};
57
58use crate::source::{ScriptKind, SourceId, TextRange, Utf16Pos};
59use crate::syntax::{
60    ArrayBindingElement, ArrayElement, ArrowFunction, AssignmentArrayElement, AssignmentExpression,
61    AssignmentMemberTarget, AssignmentObjectProperty, AssignmentOperator, AssignmentTarget,
62    AssignmentTargetNode, AwaitExpression, BinaryExpression, BinaryOperator, BindingPattern, Block,
63    BooleanLiteralNode, CallArgument, CallExpression, ClassDeclaration, ClassMember,
64    ConditionalExpression, DoWhileStatement, ExportDeclaration, ExportDefaultValue,
65    ExportNamedDeclaration, ExportSpecifierMode, Expr, Expression, ForBinding, ForInStatement,
66    ForInitializer, ForOfMode, ForOfStatement, ForStatement, FunctionBody, FunctionLike,
67    IdentifierNode, IfStatement, ImportBinding, ImportDeclaration, ImportSpecifierMode, Literal,
68    LogicalExpression, LogicalOperator, MemberExpression, MemberProperty, MetaProperty,
69    ModuleExportName, NewExpression, NodeKind, NumericLiteralNode, ObjectLiteral, ObjectMember,
70    ParameterNode, Pattern, PrivateIdentifierNode, PropertyModifier, PropertyName,
71    RegexLiteralNode, SourceFile, Statement, Stmt, StringLiteralNode, SwitchStatement,
72    TemplateElementNode, TemplateLiteral, TokenKind, UnaryOperator, UpdateExpression,
73    UpdateOperator, VariableDeclaration, VariableKind, WhileStatement, YieldExpression,
74};
75
76/// A degenerate range at the start of the document, used as the diagnostic
77/// anchor for nodes whose own range is absent (missing syntax slots).
78fn zero_range() -> TextRange {
79    match TextRange::new(Utf16Pos::ZERO, Utf16Pos::ZERO) {
80        Ok(range) => range,
81        Err(_) => unreachable!("Utf16Pos::ZERO is never after itself"),
82    }
83}
84
85/// Caller-selected lowering mode.
86#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
87pub struct LowerOptions {
88    /// Accepts JavaScript [`ScriptKind`]s in addition to TypeScript ones.
89    pub javascript_compatibility: bool,
90}
91
92/// The executable artifact this lowerer is producing.
93#[derive(Clone, Copy, Debug, Eq, PartialEq)]
94pub(crate) enum LoweringGoal {
95    /// A standalone module whose imports and exports are bytecode instructions.
96    Module,
97    /// A member of a linked program whose linkage lives in program metadata.
98    ProgramModule,
99    /// An ECMAScript classic script with no module syntax and a completion value.
100    ClassicScript,
101}
102
103/// Structural production ceilings, mirroring the bytecode verifier's limits.
104/// Two instruction slots are always reserved for a function's terminating
105/// epilogue so a body can never leave no room for its own terminator.
106const MAX_BODY_INSTRUCTIONS: usize = MAX_INSTRUCTIONS as usize - 2;
107/// Persisted string constants must fit the deterministic decode ceiling so an
108/// assembled module round-trips through [`bamts_bytecode::decode`].
109const MAX_STRING_UNITS: usize = 1 << 20;
110
111/// A typed lowering failure anchored to one source location.
112#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct LowerError {
114    pub source: SourceId,
115    pub range: TextRange,
116    pub kind: LowerErrorKind,
117}
118
119/// The closed set of lowering failures.
120#[derive(Clone, Debug, Eq, PartialEq)]
121pub enum LowerErrorKind {
122    /// A JavaScript source was lowered without `javascript_compatibility`.
123    JavaScriptSourceNeedsCompatibility { script_kind: ScriptKind },
124    /// JSON sources have no executable statement semantics.
125    JsonSourceNotExecutable,
126    /// A parser recovery node reached lowering.
127    MissingSyntax { expected: NodeKind },
128    /// A numeric literal lexeme did not denote a finite deterministic value.
129    InvalidNumericLiteral,
130    /// A bigint literal lexeme did not denote a canonical integer value.
131    InvalidBigIntLiteral,
132    /// A regular-expression literal lexeme was malformed.
133    InvalidRegexLiteral,
134    /// A module linkage name contained an unpaired UTF-16 surrogate.
135    IllFormedMetadataString,
136    /// A runtime construct the current instruction set cannot express.
137    Unsupported(UnsupportedConstruct),
138    /// A structural production capacity ran out.
139    Capacity(CapacityLimit),
140    /// The assembled module failed bytecode verification. Lowering maintains
141    /// every verifier invariant by construction, so this is defensive.
142    Verify(VerifyError),
143}
144
145/// Runtime syntax this instruction set cannot express faithfully, or that
146/// carries no runtime semantics. Every variant names one rejected construct;
147/// there is no catch-all. None of these occur in the executable corpus.
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149pub enum UnsupportedConstruct {
150    /// `with` opens a dynamic scope the register model cannot represent.
151    WithStatement,
152    /// `using`/`await using` explicit resource management (no disposal opcode).
153    UsingDeclaration,
154    /// A labeled statement (no labeled control-flow target model).
155    LabeledStatement,
156    /// A labeled `break`/`continue`.
157    LabeledJump,
158    /// `debugger` is a host breakpoint request with no bytecode.
159    DebuggerStatement,
160    /// A runtime `enum` (const enums are type-only and already erased).
161    EnumDeclaration,
162    /// A runtime `namespace`/`module` block.
163    NamespaceDeclaration,
164    /// A runtime `import x = require(...)` / `import x = ns` declaration.
165    RuntimeImportEquals,
166    /// A runtime `export * from ...` (no dynamic per-name re-export).
167    RuntimeExportAll,
168    /// An `export =` assignment.
169    ExportAssignment,
170    /// A decorated declaration.
171    DecoratedDeclaration,
172    /// A dynamic `import(expr)` whose specifier is not a string literal.
173    DynamicImportExpression,
174    /// An import declaration is invalid in a classic script.
175    ImportDeclarationInScript,
176    /// An export declaration is invalid in a classic script.
177    ExportDeclarationInScript,
178    /// A dynamic import expression is invalid in a classic script.
179    DynamicImportInScript,
180    /// `import.meta` (no host meta-object primitive).
181    ImportMeta,
182    /// An identifier spelled with unicode escape sequences.
183    EscapedIdentifier,
184    /// A non-decimal (`0x`/`0o`/`0b`) bigint literal.
185    NonDecimalBigInt,
186    /// A `return` at module top level.
187    ReturnOutsideFunction,
188    /// A derived constructor that is not an implicit constructor or a single
189    /// direct top-level `super(...)` call.
190    DerivedConstructorShape,
191    /// A derived constructor references `this` before its direct `super(...)`.
192    ThisBeforeDerivedSuper,
193}
194
195/// The exhausted structural capacity.
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum CapacityLimit {
198    Registers,
199    Constants,
200    Functions,
201    Instructions,
202    StringUnits,
203    Captures,
204}
205
206impl fmt::Display for LowerError {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        write!(
209            f,
210            "lowering failed in source {} at {}..{}: {}",
211            self.source.get(),
212            self.range.start().get(),
213            self.range.end().get(),
214            self.kind
215        )
216    }
217}
218
219impl fmt::Display for LowerErrorKind {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        match self {
222            Self::JavaScriptSourceNeedsCompatibility { script_kind } => write!(
223                f,
224                "{script_kind:?} source requires LowerOptions::javascript_compatibility"
225            ),
226            Self::JsonSourceNotExecutable => f.write_str("JSON sources are not executable"),
227            Self::MissingSyntax { expected } => {
228                write!(f, "parser recovery produced a missing {expected:?}")
229            }
230            Self::InvalidNumericLiteral => f.write_str("numeric literal has no cooked value"),
231            Self::InvalidBigIntLiteral => f.write_str("bigint literal has no canonical value"),
232            Self::InvalidRegexLiteral => f.write_str("regular-expression literal is malformed"),
233            Self::IllFormedMetadataString => {
234                f.write_str("module metadata string is not well-formed UTF-16")
235            }
236            Self::Unsupported(construct) => {
237                write!(f, "unsupported runtime semantics: {construct}")
238            }
239            Self::Capacity(limit) => write!(f, "bytecode capacity exhausted: {limit}"),
240            Self::Verify(error) => write!(f, "assembled module failed verification: {error}"),
241        }
242    }
243}
244
245impl fmt::Display for UnsupportedConstruct {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        let text = match self {
248            Self::WithStatement => "`with` statement",
249            Self::UsingDeclaration => "`using` declaration",
250            Self::LabeledStatement => "labeled statement",
251            Self::LabeledJump => "labeled `break`/`continue`",
252            Self::DebuggerStatement => "`debugger` statement",
253            Self::EnumDeclaration => "runtime `enum` declaration",
254            Self::NamespaceDeclaration => "runtime `namespace` declaration",
255            Self::RuntimeImportEquals => "runtime `import =` declaration",
256            Self::RuntimeExportAll => "runtime `export *` declaration",
257            Self::ExportAssignment => "`export =` assignment",
258            Self::DecoratedDeclaration => "decorated declaration",
259            Self::DynamicImportExpression => "dynamic `import()` with a non-literal specifier",
260            Self::ImportDeclarationInScript => "`import` declaration in a classic script",
261            Self::ExportDeclarationInScript => "`export` declaration in a classic script",
262            Self::DynamicImportInScript => "dynamic `import()` in a classic script",
263            Self::ImportMeta => "`import.meta` meta property",
264            Self::EscapedIdentifier => "identifier containing escape sequences",
265            Self::NonDecimalBigInt => "non-decimal bigint literal",
266            Self::ReturnOutsideFunction => "top-level `return`",
267            Self::DerivedConstructorShape => {
268                "derived constructor without one direct `super(...)` call"
269            }
270            Self::ThisBeforeDerivedSuper => "`this` before `super(...)` in a derived constructor",
271        };
272        f.write_str(text)
273    }
274}
275
276impl fmt::Display for CapacityLimit {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        let text = match self {
279            Self::Registers => "too many registers in one function",
280            Self::Constants => "too many pooled constants",
281            Self::Functions => "too many functions",
282            Self::Instructions => "too many instructions in one function",
283            Self::StringUnits => "string constant exceeds the deterministic pool code-unit ceiling",
284            Self::Captures => "too many captured variables in one closure",
285        };
286        f.write_str(text)
287    }
288}
289
290impl Error for LowerError {
291    fn source(&self) -> Option<&(dyn Error + 'static)> {
292        match &self.kind {
293            LowerErrorKind::Verify(error) => Some(error),
294            _ => None,
295        }
296    }
297}
298
299/// Lowers a parsed source file directly to a verified bytecode module.
300///
301/// Top-level statements become the entry function; every nested function
302/// becomes one additional module function. The returned module has passed
303/// [`Module::verify`].
304///
305/// # Errors
306/// Returns a typed [`LowerError`] for an unsupported source kind, a parser
307/// recovery node, an inexpressible runtime construct, an exhausted capacity, or
308/// (defensively) a verification failure.
309pub fn lower(file: &SourceFile, options: LowerOptions) -> Result<Module<Verified>, LowerError> {
310    let module = assemble(file, options)?;
311    module.verify().map_err(|error| LowerError {
312        source: file.source_id(),
313        range: file.range(),
314        kind: LowerErrorKind::Verify(error),
315    })
316}
317
318/// Assembles the module without the final verification pass. Exposed for tests
319/// that need to inspect assembled-but-unverified bytecode.
320pub(crate) fn assemble(
321    file: &SourceFile,
322    options: LowerOptions,
323) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
324    assemble_with_linkage_strings(file, options, &[], LoweringGoal::Module)
325}
326
327pub(crate) fn assemble_program_module(
328    file: &SourceFile,
329    options: LowerOptions,
330    linkage_strings: &[String],
331) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
332    assemble_with_linkage_strings(file, options, linkage_strings, LoweringGoal::ProgramModule)
333}
334
335/// Assembles a classic script without the final verification pass.
336pub(crate) fn assemble_classic_script(
337    file: &SourceFile,
338    options: LowerOptions,
339) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
340    assemble_classic_script_named(file, options, "evalmachine.<anonymous>")
341}
342
343pub(crate) fn assemble_classic_script_named(
344    file: &SourceFile,
345    options: LowerOptions,
346    module_name: &str,
347) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
348    assemble_with_linkage_strings(
349        file,
350        options,
351        &[module_name.to_owned()],
352        LoweringGoal::ClassicScript,
353    )
354}
355
356fn assemble_with_linkage_strings(
357    file: &SourceFile,
358    options: LowerOptions,
359    linkage_strings: &[String],
360    goal: LoweringGoal,
361) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
362    validate_script_kind(file, options)?;
363
364    let mut builder = ModuleBuilder {
365        source: file.source_id(),
366        constants: Vec::new(),
367        functions: Vec::new(),
368    };
369    for value in linkage_strings {
370        builder.intern(Constant::String(EcmaString::from_utf8(value)), file.range())?;
371    }
372    let entry = builder.reserve_function(file.range())?;
373
374    let mut context = FunctionContext::new_top_level(file, goal);
375    let completion = if goal == LoweringGoal::ClassicScript {
376        let completion = context.alloc_register(file.range())?;
377        let undefined = context.undefined(&mut builder, file.range())?;
378        context.move_to(file.range(), completion, undefined)?;
379        context.completion = Some(completion);
380        Some(completion)
381    } else {
382        None
383    };
384    context.lower_top_level(&mut builder, file.statements())?;
385    match completion {
386        Some(value) => context.emit(file.range(), Instruction::Return { value })?,
387        None => context.emit(file.range(), Instruction::Halt)?,
388    };
389    let assembled = context.into_function(None, FunctionFlags::default());
390    builder.fill_function(entry, assembled);
391
392    let functions = builder
393        .functions
394        .into_iter()
395        .map(|slot| slot.expect("every reserved function slot is filled before assembly"))
396        .collect();
397    Ok(Module::new(builder.constants, functions, entry))
398}
399
400fn validate_script_kind(file: &SourceFile, options: LowerOptions) -> Result<(), LowerError> {
401    let kind = match file.script_kind() {
402        ScriptKind::TypeScript | ScriptKind::TypeScriptReact => return Ok(()),
403        ScriptKind::JavaScript | ScriptKind::JavaScriptReact => {
404            if options.javascript_compatibility {
405                return Ok(());
406            }
407            LowerErrorKind::JavaScriptSourceNeedsCompatibility {
408                script_kind: file.script_kind(),
409            }
410        }
411        ScriptKind::Json => LowerErrorKind::JsonSourceNotExecutable,
412    };
413    Err(LowerError {
414        source: file.source_id(),
415        range: file.range(),
416        kind,
417    })
418}
419
420/// Module-wide constant pool and function table.
421struct ModuleBuilder {
422    source: SourceId,
423    constants: Vec<Constant>,
424    functions: Vec<Option<Function>>,
425}
426
427impl ModuleBuilder {
428    fn error(&self, range: TextRange, kind: LowerErrorKind) -> LowerError {
429        LowerError {
430            source: self.source,
431            range,
432            kind,
433        }
434    }
435
436    /// Interns one constant, deduplicated, in deterministic first-use order.
437    fn intern(&mut self, constant: Constant, range: TextRange) -> Result<ConstantId, LowerError> {
438        if let Constant::String(value) = &constant
439            && value.len_units() > MAX_STRING_UNITS
440        {
441            return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::StringUnits)));
442        }
443        if let Some(position) = self
444            .constants
445            .iter()
446            .position(|existing| *existing == constant)
447        {
448            return Ok(ConstantId::new(position as u32));
449        }
450        if self.constants.len() >= MAX_CONSTANTS as usize {
451            return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Constants)));
452        }
453        let id = ConstantId::new(self.constants.len() as u32);
454        self.constants.push(constant);
455        Ok(id)
456    }
457
458    fn reserve_function(&mut self, range: TextRange) -> Result<FunctionId, LowerError> {
459        if self.functions.len() >= MAX_FUNCTIONS as usize {
460            return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Functions)));
461        }
462        let id = FunctionId::new(self.functions.len() as u32);
463        self.functions.push(None);
464        Ok(id)
465    }
466
467    fn fill_function(&mut self, id: FunctionId, function: Function) {
468        self.functions[id.get() as usize] = Some(function);
469    }
470}
471
472/// What a resolved name denotes inside the current function. Its storage kind is
473/// fixed at declaration and never changes.
474#[derive(Clone, Copy)]
475enum Binding {
476    /// A value living in a fixed register (the binding's home).
477    Local(Register),
478    /// A one-element array shared by every closure over this binding.
479    Cell(Register),
480}
481
482type BindingSite = (usize, usize);
483
484#[derive(Clone, Debug, Eq, Hash, PartialEq)]
485enum BindingIdentity {
486    Function(String),
487    Lexical(BindingSite),
488}
489
490#[derive(Default)]
491struct CapturePlan {
492    captured: HashSet<BindingIdentity>,
493    runtime_cells: HashSet<BindingIdentity>,
494}
495
496impl CapturePlan {
497    fn captures(&self, name: &str, site: BindingSite, declaration_scope: DeclarationScope) -> bool {
498        self.captured
499            .contains(&binding_identity(name, site, declaration_scope))
500    }
501
502    fn requires_cell(
503        &self,
504        name: &str,
505        site: BindingSite,
506        declaration_scope: DeclarationScope,
507    ) -> bool {
508        let identity = binding_identity(name, site, declaration_scope);
509        self.captured.contains(&identity) || self.runtime_cells.contains(&identity)
510    }
511}
512
513#[derive(Clone)]
514struct ImmediateDeclaration<'a> {
515    name: String,
516    site: BindingSite,
517    range: TextRange,
518    kind: ImmediateDeclarationKind<'a>,
519}
520
521#[derive(Clone, Copy)]
522enum ImmediateDeclarationKind<'a> {
523    Lexical,
524    Function(&'a FunctionLike),
525}
526
527fn binding_identity(
528    name: &str,
529    site: BindingSite,
530    declaration_scope: DeclarationScope,
531) -> BindingIdentity {
532    match declaration_scope {
533        DeclarationScope::Function => BindingIdentity::Function(name.to_owned()),
534        DeclarationScope::Lexical | DeclarationScope::Iteration => BindingIdentity::Lexical(site),
535    }
536}
537
538fn binding_site(range: TextRange) -> BindingSite {
539    (range.start().get(), range.end().get())
540}
541
542#[derive(Clone, Copy)]
543enum DeclarationScope {
544    Function,
545    Lexical,
546    Iteration,
547}
548
549/// One captured value of a nested function, in deterministic capture order.
550#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
551enum CaptureKey {
552    /// A named function-local binding of the enclosing function.
553    Name(String),
554    /// The enclosing function's `this` (arrow capture).
555    This,
556    /// The enclosing function's `arguments` (arrow capture).
557    Arguments,
558    /// The enclosing function's `new.target` (arrow capture).
559    NewTarget,
560    /// The parent constructor captured by a derived class constructor.
561    Parent(Register),
562}
563
564/// How the current function observes its `arguments` binding.
565#[derive(Clone, Copy)]
566enum ArgumentsSource {
567    /// A regular function: `arguments` is the activation's own exotic object.
568    Own,
569    /// An arrow (or nested arrow): `arguments` is a captured register.
570    Captured(Register),
571    /// Module top level or an arrow with no enclosing function: `arguments` is
572    /// an ordinary free name resolved against the environment.
573    None,
574}
575
576/// A live loop's break/continue placeholder jumps, patched when the loop ends.
577struct LoopFrame {
578    breaks: Vec<Pc>,
579    continues: Vec<Pc>,
580    is_loop: bool,
581}
582
583/// Completion kinds routed through a `finally` block.
584const COMPLETION_NORMAL: i32 = 0;
585const COMPLETION_RETURN: i32 = 1;
586const COMPLETION_THROW: i32 = 2;
587const COMPLETION_BREAK: i32 = 3;
588const COMPLETION_CONTINUE: i32 = 4;
589
590/// A live `finally` block: the completion state registers and the pending
591/// jumps into the finally entry that must be patched once its PC is known.
592struct FinallyFrame {
593    kind_reg: Register,
594    value_reg: Register,
595    pending: Vec<Pc>,
596    /// Loop-stack depth when this finally was entered; a `break`/`continue`
597    /// whose target loop predates the finally must route through it.
598    loop_depth: usize,
599}
600
601/// Per-function lowering state: code, register allocator, and lexical scopes.
602struct FunctionContext<'a> {
603    file: &'a SourceFile,
604    code: Vec<Instruction>,
605    registers: u32,
606    capture_count: u32,
607    parameter_count: u32,
608    scopes: Vec<HashMap<String, Binding>>,
609    /// Cells allocated before a declaration-owned initializer or body is built,
610    /// indexed by the scanner's exact binding identity.
611    predeclared_cells: HashMap<BindingIdentity, Register>,
612    capture_plan: CapturePlan,
613    loops: Vec<LoopFrame>,
614    handlers: Vec<ExceptionHandler>,
615    finally_stack: Vec<FinallyFrame>,
616    /// `true` for the module entry function, whose bindings are the module
617    /// environment (named globals) rather than register homes.
618    top_level: bool,
619    goal: LoweringGoal,
620    /// Innermost statement-value target, present only for a classic script entry.
621    completion: Option<Register>,
622    /// Reusable statement-value registers indexed by normalizing-statement depth.
623    completion_pool: Vec<Register>,
624    completion_depth: usize,
625    /// `Some(reg)` when `this` is captured by value by an arrow; `None` when
626    /// the activation owns `this` (`LoadThis`).
627    this_capture: Option<Register>,
628    /// `Some(reg)` when `new.target` is captured by value by an arrow; `None`
629    /// when owned (`LoadNewTarget`).
630    new_target_capture: Option<Register>,
631    /// The parent constructor captured by a derived constructor.
632    parent_constructor_capture: Option<Register>,
633    arguments_source: ArgumentsSource,
634}
635
636impl<'a> FunctionContext<'a> {
637    fn new_top_level(file: &'a SourceFile, goal: LoweringGoal) -> Self {
638        let capture_plan = CapturePlan::for_statements(file, file.statements());
639        Self {
640            file,
641            code: Vec::new(),
642            registers: 0,
643            capture_count: 0,
644            parameter_count: 0,
645            scopes: vec![HashMap::new()],
646            predeclared_cells: HashMap::new(),
647            capture_plan,
648            loops: Vec::new(),
649            handlers: Vec::new(),
650            finally_stack: Vec::new(),
651            top_level: true,
652            goal,
653            completion: None,
654            completion_pool: Vec::new(),
655            completion_depth: 0,
656            this_capture: None,
657            new_target_capture: None,
658            parent_constructor_capture: None,
659            arguments_source: ArgumentsSource::None,
660        }
661    }
662
663    fn into_function(self, name: Option<ConstantId>, flags: FunctionFlags) -> Function {
664        Function::new(
665            name,
666            self.capture_count,
667            self.parameter_count,
668            self.registers,
669            flags,
670            self.code,
671            self.handlers,
672        )
673    }
674
675    fn error(&self, range: TextRange, kind: LowerErrorKind) -> LowerError {
676        LowerError {
677            source: self.file.source_id(),
678            range,
679            kind,
680        }
681    }
682
683    fn unsupported(&self, range: TextRange, construct: UnsupportedConstruct) -> LowerError {
684        self.error(range, LowerErrorKind::Unsupported(construct))
685    }
686
687    fn missing(&self, range: TextRange, expected: NodeKind) -> LowerError {
688        self.error(range, LowerErrorKind::MissingSyntax { expected })
689    }
690
691    // ------------------------------------------------------------------
692    // Emission primitives
693    // ------------------------------------------------------------------
694
695    fn emit(&mut self, range: TextRange, instruction: Instruction) -> Result<Pc, LowerError> {
696        if self.code.len() >= MAX_BODY_INSTRUCTIONS {
697            return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Instructions)));
698        }
699        let pc = Pc::new(self.code.len() as u32);
700        self.code.push(instruction);
701        Ok(pc)
702    }
703
704    fn next_pc(&self) -> Pc {
705        Pc::new(self.code.len() as u32)
706    }
707
708    fn patch_jump(&mut self, at: Pc, target: Pc) {
709        match &mut self.code[at.get() as usize] {
710            Instruction::Jump { target: slot }
711            | Instruction::JumpIfTrue { target: slot, .. }
712            | Instruction::JumpIfFalse { target: slot, .. } => *slot = target,
713            other => unreachable!("patch target of non-jump instruction: {other:?}"),
714        }
715    }
716
717    fn alloc_register(&mut self, range: TextRange) -> Result<Register, LowerError> {
718        if self.registers >= MAX_REGISTERS {
719            return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Registers)));
720        }
721        let register = Register::new(self.registers);
722        self.registers += 1;
723        Ok(register)
724    }
725
726    fn load_constant(
727        &mut self,
728        builder: &mut ModuleBuilder,
729        constant: Constant,
730        range: TextRange,
731    ) -> Result<Register, LowerError> {
732        let id = builder.intern(constant, range)?;
733        let dst = self.alloc_register(range)?;
734        self.emit(range, Instruction::LoadConst { dst, constant: id })?;
735        Ok(dst)
736    }
737
738    fn undefined(
739        &mut self,
740        builder: &mut ModuleBuilder,
741        range: TextRange,
742    ) -> Result<Register, LowerError> {
743        self.load_constant(builder, Constant::Undefined, range)
744    }
745
746    /// Loads a string constant into a fresh register (used for property keys,
747    /// global names, and string values).
748    fn string_reg(
749        &mut self,
750        builder: &mut ModuleBuilder,
751        value: EcmaString,
752        range: TextRange,
753    ) -> Result<Register, LowerError> {
754        self.load_constant(builder, Constant::String(value), range)
755    }
756
757    fn move_to(
758        &mut self,
759        range: TextRange,
760        dst: Register,
761        src: Register,
762    ) -> Result<(), LowerError> {
763        self.emit(range, Instruction::Move { dst, src })?;
764        Ok(())
765    }
766
767    fn lower_normalizing_statement(
768        &mut self,
769        builder: &mut ModuleBuilder,
770        range: TextRange,
771        lower: impl FnOnce(&mut Self, &mut ModuleBuilder) -> Result<(), LowerError>,
772    ) -> Result<(), LowerError> {
773        let Some(outer) = self.completion else {
774            return lower(self, builder);
775        };
776        let depth = self.completion_depth;
777        let inner = match self.completion_pool.get(depth).copied() {
778            Some(register) => register,
779            None => {
780                let register = self.alloc_register(range)?;
781                self.completion_pool.push(register);
782                register
783            }
784        };
785        let undefined = self.undefined(builder, range)?;
786        self.move_to(range, inner, undefined)?;
787        self.completion = Some(inner);
788        self.completion_depth += 1;
789        let result = lower(self, builder);
790        self.completion_depth -= 1;
791        self.completion = Some(outer);
792        result?;
793        self.move_to(range, outer, inner)
794    }
795
796    fn lower_without_completion(
797        &mut self,
798        builder: &mut ModuleBuilder,
799        lower: impl FnOnce(&mut Self, &mut ModuleBuilder) -> Result<(), LowerError>,
800    ) -> Result<(), LowerError> {
801        let completion = self.completion.take();
802        let result = lower(self, builder);
803        self.completion = completion;
804        result
805    }
806
807    // ------------------------------------------------------------------
808    // Scopes and names
809    // ------------------------------------------------------------------
810
811    fn push_scope(&mut self) {
812        self.scopes.push(HashMap::new());
813    }
814
815    fn pop_scope(&mut self) {
816        self.scopes.pop();
817    }
818
819    fn resolve(&self, name: &str) -> Option<Binding> {
820        self.scopes
821            .iter()
822            .rev()
823            .find_map(|scope| scope.get(name).copied())
824    }
825
826    fn declare(&mut self, name: String, binding: Binding, declaration_scope: DeclarationScope) {
827        let scope = match declaration_scope {
828            DeclarationScope::Function => self
829                .scopes
830                .first_mut()
831                .expect("a function context always holds its root scope"),
832            DeclarationScope::Lexical | DeclarationScope::Iteration => self
833                .scopes
834                .last_mut()
835                .expect("a function context always holds at least one scope"),
836        };
837        scope.insert(name, binding);
838    }
839
840    fn identifier_text(&self, identifier: &IdentifierNode) -> Result<String, LowerError> {
841        let token = identifier.data().token();
842        if token.is_missing() {
843            return Err(self.missing(identifier.range(), NodeKind::Identifier));
844        }
845        let Some(text) = self.file.token_text(token) else {
846            return Err(self.missing(identifier.range(), NodeKind::Identifier));
847        };
848        if text.contains('\\') {
849            return Err(
850                self.unsupported(identifier.range(), UnsupportedConstruct::EscapedIdentifier)
851            );
852        }
853        Ok(text.to_owned())
854    }
855
856    fn private_text(&self, private: &PrivateIdentifierNode) -> Result<String, LowerError> {
857        let token = private.data().token();
858        if token.is_missing() {
859            return Err(self.missing(private.range(), NodeKind::PrivateIdentifier));
860        }
861        let Some(text) = self.file.token_text(token) else {
862            return Err(self.missing(private.range(), NodeKind::PrivateIdentifier));
863        };
864        Ok(text.to_owned())
865    }
866
867    /// Declares an initialized function-local binding or module global.
868    fn declare_initialized(
869        &mut self,
870        builder: &mut ModuleBuilder,
871        name: &str,
872        value: Register,
873        range: TextRange,
874        site: BindingSite,
875        declaration_scope: DeclarationScope,
876    ) -> Result<(), LowerError> {
877        if self.top_level && !matches!(declaration_scope, DeclarationScope::Iteration) {
878            let id = builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
879            self.emit(range, Instruction::StoreGlobal { name: id, value })?;
880            return Ok(());
881        }
882        if self.capture_plan.captures(name, site, declaration_scope) {
883            let cell = self.alloc_register(range)?;
884            self.emit(range, Instruction::CreateArray { dst: cell })?;
885            self.emit(range, Instruction::ArrayPush { array: cell, value })?;
886            self.declare(name.to_owned(), Binding::Cell(cell), declaration_scope);
887        } else {
888            let home = self.alloc_register(range)?;
889            self.move_to(range, home, value)?;
890            self.declare(name.to_owned(), Binding::Local(home), declaration_scope);
891        }
892        Ok(())
893    }
894
895    /// Seeds the cell a declaration-owned closure needs before its initializer
896    /// or body can materialize that closure. Module globals stay environment-backed.
897    fn predeclare_captured_binding(
898        &mut self,
899        name: &str,
900        range: TextRange,
901        site: BindingSite,
902        declaration_scope: DeclarationScope,
903    ) -> Result<(), LowerError> {
904        if self.top_level
905            || !self
906                .capture_plan
907                .requires_cell(name, site, declaration_scope)
908        {
909            return Ok(());
910        }
911        let identity = binding_identity(name, site, declaration_scope);
912        if self.predeclared_cells.contains_key(&identity) {
913            return Ok(());
914        }
915        if matches!(declaration_scope, DeclarationScope::Function)
916            && let Some(Binding::Cell(cell)) = self
917                .scopes
918                .first()
919                .and_then(|scope| scope.get(name).copied())
920        {
921            self.predeclared_cells.insert(identity, cell);
922            return Ok(());
923        }
924        let cell = self.alloc_register(range)?;
925        self.emit(range, Instruction::CreateCell { dst: cell })?;
926        self.declare(name.to_owned(), Binding::Cell(cell), declaration_scope);
927        self.predeclared_cells.insert(identity, cell);
928        Ok(())
929    }
930
931    /// Predeclares just the planned captured leaves of a lexical pattern.
932    fn predeclare_captured_pattern(
933        &mut self,
934        pattern: &Pattern,
935        declaration_scope: DeclarationScope,
936    ) -> Result<(), LowerError> {
937        match pattern.data() {
938            BindingPattern::Identifier(identifier) => {
939                let name = self.identifier_text(identifier)?;
940                self.predeclare_captured_binding(
941                    &name,
942                    identifier.range(),
943                    binding_site(identifier.range()),
944                    declaration_scope,
945                )
946            }
947            BindingPattern::Object(object) => {
948                for property in &object.properties {
949                    self.predeclare_captured_pattern(&property.binding, declaration_scope)?;
950                }
951                Ok(())
952            }
953            BindingPattern::Array(array) => {
954                for element in &array.elements {
955                    if let ArrayBindingElement::Binding(pattern) = element {
956                        self.predeclare_captured_pattern(pattern, declaration_scope)?;
957                    }
958                }
959                Ok(())
960            }
961            BindingPattern::Assignment(assignment) => {
962                self.predeclare_captured_pattern(&assignment.left, declaration_scope)
963            }
964            BindingPattern::Rest(rest) => {
965                self.predeclare_captured_pattern(&rest.argument, declaration_scope)
966            }
967            BindingPattern::Missing(_) => Ok(()),
968        }
969    }
970
971    /// Creates the class expression's own TDZ cell, keyed by its declaration
972    /// site so a caller that already predeclared it shares the same binding.
973    fn predeclare_class_expression_binding(
974        &mut self,
975        name: &str,
976        range: TextRange,
977        site: BindingSite,
978    ) -> Result<Register, LowerError> {
979        let identity = binding_identity(name, site, DeclarationScope::Lexical);
980        if let Some(cell) = self.predeclared_cells.get(&identity).copied() {
981            return Ok(cell);
982        }
983        let cell = self.alloc_register(range)?;
984        self.emit(range, Instruction::CreateCell { dst: cell })?;
985        self.declare(
986            name.to_owned(),
987            Binding::Cell(cell),
988            DeclarationScope::Lexical,
989        );
990        self.predeclared_cells.insert(identity, cell);
991        Ok(cell)
992    }
993
994    fn cell_value(
995        &mut self,
996        builder: &mut ModuleBuilder,
997        cell: Register,
998        range: TextRange,
999    ) -> Result<Register, LowerError> {
1000        let key = self.load_constant(builder, Constant::Int32(0), range)?;
1001        let dst = self.alloc_register(range)?;
1002        self.emit(
1003            range,
1004            Instruction::GetProperty {
1005                dst,
1006                object: cell,
1007                key,
1008            },
1009        )?;
1010        Ok(dst)
1011    }
1012
1013    fn store_cell(
1014        &mut self,
1015        builder: &mut ModuleBuilder,
1016        cell: Register,
1017        value: Register,
1018        range: TextRange,
1019    ) -> Result<(), LowerError> {
1020        let key = self.load_constant(builder, Constant::Int32(0), range)?;
1021        self.emit(
1022            range,
1023            Instruction::SetProperty {
1024                object: cell,
1025                key,
1026                value,
1027            },
1028        )?;
1029        Ok(())
1030    }
1031
1032    fn rebind_iteration_cell(
1033        &mut self,
1034        builder: &mut ModuleBuilder,
1035        name: &str,
1036        range: TextRange,
1037    ) -> Result<(), LowerError> {
1038        let Some(Binding::Cell(cell)) = self.resolve(name) else {
1039            return Ok(());
1040        };
1041        let value = self.cell_value(builder, cell, range)?;
1042        self.emit(range, Instruction::CreateArray { dst: cell })?;
1043        self.emit(range, Instruction::ArrayPush { array: cell, value })?;
1044        Ok(())
1045    }
1046
1047    fn declaration_names(&self, declaration: &VariableDeclaration) -> Vec<String> {
1048        let mut names = Vec::new();
1049        for declarator in &declaration.declarations {
1050            collect_pattern_names(self.file, &declarator.data().binding, &mut names);
1051        }
1052        names
1053    }
1054
1055    fn rebind_iteration_cells(
1056        &mut self,
1057        builder: &mut ModuleBuilder,
1058        names: &[String],
1059        range: TextRange,
1060    ) -> Result<(), LowerError> {
1061        for name in names {
1062            self.rebind_iteration_cell(builder, name, range)?;
1063        }
1064        Ok(())
1065    }
1066
1067    /// Stores a produced value into a binding named `name`, reusing a hoisted
1068    /// function binding or declaring a fresh binding with immutable storage.
1069    fn store_binding(
1070        &mut self,
1071        builder: &mut ModuleBuilder,
1072        name: &str,
1073        value: Register,
1074        range: TextRange,
1075        site: BindingSite,
1076        declaration_scope: DeclarationScope,
1077    ) -> Result<(), LowerError> {
1078        let identity = binding_identity(name, site, declaration_scope);
1079        if let Some(cell) = self.predeclared_cells.get(&identity).copied() {
1080            return self.store_cell(builder, cell, value, range);
1081        }
1082        if matches!(declaration_scope, DeclarationScope::Function)
1083            && let Some(binding) = self
1084                .scopes
1085                .first()
1086                .and_then(|scope| scope.get(name).copied())
1087        {
1088            return match binding {
1089                Binding::Local(home) => self.move_to(range, home, value),
1090                Binding::Cell(cell) => self.store_cell(builder, cell, value, range),
1091            };
1092        }
1093        self.declare_initialized(builder, name, value, range, site, declaration_scope)
1094    }
1095
1096    /// Pre-allocates and zero-initializes storage for every `var`-scoped binding.
1097    fn hoist_vars(
1098        &mut self,
1099        builder: &mut ModuleBuilder,
1100        statements: &[Stmt],
1101        range: TextRange,
1102    ) -> Result<(), LowerError> {
1103        if self.top_level {
1104            return Ok(());
1105        }
1106        let mut names = Vec::new();
1107        collect_var_names(self.file, statements, &mut names);
1108        let mut seen = HashSet::new();
1109        for name in names {
1110            if !seen.insert(name.clone()) {
1111                continue;
1112            }
1113            if self
1114                .scopes
1115                .first()
1116                .is_some_and(|scope| scope.contains_key(&name))
1117            {
1118                continue;
1119            }
1120            let id = builder.intern(Constant::Undefined, range)?;
1121            if self
1122                .capture_plan
1123                .captures(&name, binding_site(range), DeclarationScope::Function)
1124            {
1125                let value = self.alloc_register(range)?;
1126                self.emit(
1127                    range,
1128                    Instruction::LoadConst {
1129                        dst: value,
1130                        constant: id,
1131                    },
1132                )?;
1133                let cell = self.alloc_register(range)?;
1134                self.emit(range, Instruction::CreateArray { dst: cell })?;
1135                self.emit(range, Instruction::ArrayPush { array: cell, value })?;
1136                self.declare(name, Binding::Cell(cell), DeclarationScope::Function);
1137            } else {
1138                let home = self.alloc_register(range)?;
1139                self.emit(
1140                    range,
1141                    Instruction::LoadConst {
1142                        dst: home,
1143                        constant: id,
1144                    },
1145                )?;
1146                self.declare(name, Binding::Local(home), DeclarationScope::Function);
1147            }
1148        }
1149        Ok(())
1150    }
1151
1152    // ------------------------------------------------------------------
1153    // Reads and assignments of names
1154    // ------------------------------------------------------------------
1155
1156    fn read_name(
1157        &mut self,
1158        builder: &mut ModuleBuilder,
1159        name: &str,
1160        range: TextRange,
1161    ) -> Result<Register, LowerError> {
1162        if let Some(binding) = self.resolve(name) {
1163            return match binding {
1164                Binding::Local(register) => Ok(register),
1165                Binding::Cell(cell) => self.cell_value(builder, cell, range),
1166            };
1167        }
1168        if name == "arguments"
1169            && let Some(register) = self.arguments_value(builder, range)?
1170        {
1171            return Ok(register);
1172        }
1173        if name == "undefined" {
1174            return self.undefined(builder, range);
1175        }
1176        // Free name: read from the environment.
1177        let id = builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
1178        let dst = self.alloc_register(range)?;
1179        self.emit(range, Instruction::LoadGlobal { dst, name: id })?;
1180        Ok(dst)
1181    }
1182
1183    /// Stores `value` into the binding named `name` (already existing), or the
1184    /// environment if the name is free.
1185    fn assign_name(
1186        &mut self,
1187        builder: &mut ModuleBuilder,
1188        name: &str,
1189        value: Register,
1190        range: TextRange,
1191    ) -> Result<(), LowerError> {
1192        if let Some(binding) = self.resolve(name) {
1193            return match binding {
1194                Binding::Local(home) => self.move_to(range, home, value),
1195                Binding::Cell(cell) => {
1196                    let _ = self.cell_value(builder, cell, range)?;
1197                    self.store_cell(builder, cell, value, range)
1198                }
1199            };
1200        }
1201        let id = builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
1202        self.emit(range, Instruction::StoreGlobal { name: id, value })?;
1203        Ok(())
1204    }
1205
1206    /// The current value of a name for read-modify-write (compound assignment
1207    /// and update). Returns the register holding the current value.
1208    fn read_name_value(
1209        &mut self,
1210        builder: &mut ModuleBuilder,
1211        name: &str,
1212        range: TextRange,
1213    ) -> Result<Register, LowerError> {
1214        self.read_name(builder, name, range)
1215    }
1216
1217    // ------------------------------------------------------------------
1218    // this / arguments / new.target
1219    // ------------------------------------------------------------------
1220
1221    fn this_value(&mut self, range: TextRange) -> Result<Register, LowerError> {
1222        if let Some(register) = self.this_capture {
1223            return Ok(register);
1224        }
1225        let dst = self.alloc_register(range)?;
1226        self.emit(range, Instruction::LoadThis { dst })?;
1227        Ok(dst)
1228    }
1229
1230    fn new_target_value(&mut self, range: TextRange) -> Result<Register, LowerError> {
1231        if let Some(register) = self.new_target_capture {
1232            return Ok(register);
1233        }
1234        let dst = self.alloc_register(range)?;
1235        self.emit(range, Instruction::LoadNewTarget { dst })?;
1236        Ok(dst)
1237    }
1238
1239    /// Returns the `arguments` register if the current function provides one,
1240    /// or `None` if `arguments` should be treated as a free name.
1241    fn arguments_value(
1242        &mut self,
1243        _builder: &mut ModuleBuilder,
1244        range: TextRange,
1245    ) -> Result<Option<Register>, LowerError> {
1246        match self.arguments_source {
1247            ArgumentsSource::Own => {
1248                let dst = self.alloc_register(range)?;
1249                self.emit(range, Instruction::LoadArguments { dst })?;
1250                Ok(Some(dst))
1251            }
1252            ArgumentsSource::Captured(register) => Ok(Some(register)),
1253            ArgumentsSource::None => Ok(None),
1254        }
1255    }
1256
1257    // ------------------------------------------------------------------
1258    // Top-level lowering
1259    // ------------------------------------------------------------------
1260
1261    fn lower_top_level(
1262        &mut self,
1263        builder: &mut ModuleBuilder,
1264        statements: &[Stmt],
1265    ) -> Result<(), LowerError> {
1266        self.instantiate_declarations(builder, statements, false)?;
1267        for statement in statements {
1268            self.lower_statement(builder, statement)?;
1269        }
1270        Ok(())
1271    }
1272
1273    fn instantiate_declarations(
1274        &mut self,
1275        builder: &mut ModuleBuilder,
1276        statements: &[Stmt],
1277        switch_scope: bool,
1278    ) -> Result<(), LowerError> {
1279        let declarations = collect_immediate_declarations(self.file, statements);
1280        for declaration in &declarations {
1281            if matches!(&declaration.kind, ImmediateDeclarationKind::Lexical)
1282                && (switch_scope
1283                    || self.capture_plan.requires_cell(
1284                        &declaration.name,
1285                        declaration.site,
1286                        DeclarationScope::Lexical,
1287                    ))
1288            {
1289                self.predeclare_captured_binding(
1290                    &declaration.name,
1291                    declaration.range,
1292                    declaration.site,
1293                    DeclarationScope::Lexical,
1294                )?;
1295            }
1296        }
1297        for declaration in declarations {
1298            if let ImmediateDeclarationKind::Function(function) = declaration.kind {
1299                self.instantiate_function_declaration(
1300                    builder,
1301                    declaration.range,
1302                    &declaration.name,
1303                    declaration.site,
1304                    function,
1305                )?;
1306            }
1307        }
1308        Ok(())
1309    }
1310
1311    // ------------------------------------------------------------------
1312    // Statements
1313    // ------------------------------------------------------------------
1314
1315    fn lower_statement(
1316        &mut self,
1317        builder: &mut ModuleBuilder,
1318        statement: &Stmt,
1319    ) -> Result<(), LowerError> {
1320        let range = statement.range();
1321        match statement.data() {
1322            Statement::Interface(_) | Statement::TypeAlias(_) | Statement::Declare(_) => Ok(()),
1323            Statement::Import(import) => self.lower_import(builder, range, import),
1324            Statement::ImportEquals(import) => {
1325                if self.goal == LoweringGoal::ClassicScript {
1326                    Err(self.unsupported(range, UnsupportedConstruct::ImportDeclarationInScript))
1327                } else if import.is_type_only {
1328                    Ok(())
1329                } else {
1330                    Err(self.unsupported(range, UnsupportedConstruct::RuntimeImportEquals))
1331                }
1332            }
1333            Statement::Export(export) => self.lower_export(builder, range, export),
1334            Statement::Variable(declaration) => {
1335                self.lower_variable_declaration(builder, declaration)
1336            }
1337            Statement::Function(_) => Ok(()),
1338            Statement::Class(class) => self.lower_class_declaration(builder, range, class, None),
1339            Statement::Enum(_) => {
1340                Err(self.unsupported(range, UnsupportedConstruct::EnumDeclaration))
1341            }
1342            Statement::Namespace(_) => {
1343                Err(self.unsupported(range, UnsupportedConstruct::NamespaceDeclaration))
1344            }
1345            Statement::Block(block) => {
1346                self.push_scope();
1347                let result = self.lower_block(builder, block.data());
1348                self.pop_scope();
1349                result
1350            }
1351            Statement::Empty => Ok(()),
1352            Statement::Expression(expression) => {
1353                let value = self.lower_expression(builder, &expression.expression)?;
1354                if let Some(completion) = self.completion {
1355                    self.move_to(range, completion, value)?;
1356                }
1357                Ok(())
1358            }
1359            Statement::If(if_statement) => {
1360                self.lower_normalizing_statement(builder, range, |this, builder| {
1361                    this.lower_if(builder, if_statement)
1362                })
1363            }
1364            Statement::Switch(switch) => {
1365                self.lower_normalizing_statement(builder, range, |this, builder| {
1366                    this.lower_switch(builder, range, switch)
1367                })
1368            }
1369            Statement::For(for_statement) => {
1370                self.lower_normalizing_statement(builder, range, |this, builder| {
1371                    this.lower_for(builder, for_statement)
1372                })
1373            }
1374            Statement::ForIn(for_in) => {
1375                self.lower_normalizing_statement(builder, range, |this, builder| {
1376                    this.lower_for_in(builder, range, for_in)
1377                })
1378            }
1379            Statement::ForOf(for_of) => {
1380                self.lower_normalizing_statement(builder, range, |this, builder| {
1381                    this.lower_for_of(builder, range, for_of)
1382                })
1383            }
1384            Statement::While(while_statement) => {
1385                self.lower_normalizing_statement(builder, range, |this, builder| {
1386                    this.lower_while(builder, while_statement)
1387                })
1388            }
1389            Statement::DoWhile(do_while) => {
1390                self.lower_normalizing_statement(builder, range, |this, builder| {
1391                    this.lower_do_while(builder, do_while)
1392                })
1393            }
1394            Statement::Try(try_statement) => {
1395                self.lower_normalizing_statement(builder, range, |this, builder| {
1396                    this.lower_try(builder, range, try_statement)
1397                })
1398            }
1399            Statement::With(_) => Err(self.unsupported(range, UnsupportedConstruct::WithStatement)),
1400            Statement::Labeled(_) => {
1401                Err(self.unsupported(range, UnsupportedConstruct::LabeledStatement))
1402            }
1403            Statement::Break(jump) => self.lower_break(builder, range, jump.label.is_some()),
1404            Statement::Continue(jump) => self.lower_continue(builder, range, jump.label.is_some()),
1405            Statement::Return(return_statement) => {
1406                if self.top_level {
1407                    return Err(
1408                        self.unsupported(range, UnsupportedConstruct::ReturnOutsideFunction)
1409                    );
1410                }
1411                let value = match &return_statement.argument {
1412                    Some(expression) => self.lower_expression(builder, expression)?,
1413                    None => self.undefined(builder, range)?,
1414                };
1415                if self.route_through_finally(builder, range, COMPLETION_RETURN, Some(value))? {
1416                    return Ok(());
1417                }
1418                self.emit(range, Instruction::Return { value })?;
1419                Ok(())
1420            }
1421            Statement::Throw(throw) => {
1422                let value = self.lower_expression(builder, &throw.argument)?;
1423                self.emit(range, Instruction::Throw { value })?;
1424                Ok(())
1425            }
1426            Statement::Debugger => {
1427                Err(self.unsupported(range, UnsupportedConstruct::DebuggerStatement))
1428            }
1429            Statement::Missing(missing) => Err(self.missing(range, missing.expected())),
1430        }
1431    }
1432
1433    fn lower_block(
1434        &mut self,
1435        builder: &mut ModuleBuilder,
1436        block: &Block,
1437    ) -> Result<(), LowerError> {
1438        self.instantiate_declarations(builder, &block.statements, false)?;
1439        for statement in &block.statements {
1440            self.lower_statement(builder, statement)?;
1441        }
1442        Ok(())
1443    }
1444
1445    fn lower_nested(&mut self, builder: &mut ModuleBuilder, body: &Stmt) -> Result<(), LowerError> {
1446        self.push_scope();
1447        let result = self.lower_statement(builder, body);
1448        self.pop_scope();
1449        result
1450    }
1451
1452    fn lower_if(
1453        &mut self,
1454        builder: &mut ModuleBuilder,
1455        if_statement: &IfStatement,
1456    ) -> Result<(), LowerError> {
1457        let range = if_statement.test.range();
1458        let condition = self.lower_expression(builder, &if_statement.test)?;
1459        let to_else = self.emit(
1460            range,
1461            Instruction::JumpIfFalse {
1462                condition,
1463                target: Pc::new(0),
1464            },
1465        )?;
1466        self.lower_nested(builder, &if_statement.consequent)?;
1467        match &if_statement.alternate {
1468            Some(alternate) => {
1469                let to_end = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1470                let else_pc = self.next_pc();
1471                self.patch_jump(to_else, else_pc);
1472                self.lower_nested(builder, alternate)?;
1473                let end_pc = self.next_pc();
1474                self.patch_jump(to_end, end_pc);
1475            }
1476            None => {
1477                let end_pc = self.next_pc();
1478                self.patch_jump(to_else, end_pc);
1479            }
1480        }
1481        Ok(())
1482    }
1483
1484    fn lower_while(
1485        &mut self,
1486        builder: &mut ModuleBuilder,
1487        while_statement: &WhileStatement,
1488    ) -> Result<(), LowerError> {
1489        let range = while_statement.test.range();
1490        let head = self.next_pc();
1491        let condition = self.lower_expression(builder, &while_statement.test)?;
1492        let exit_jump = self.emit(
1493            range,
1494            Instruction::JumpIfFalse {
1495                condition,
1496                target: Pc::new(0),
1497            },
1498        )?;
1499        self.loops.push(LoopFrame {
1500            breaks: Vec::new(),
1501            continues: Vec::new(),
1502            is_loop: true,
1503        });
1504        self.lower_nested(builder, &while_statement.body)?;
1505        self.emit(range, Instruction::Jump { target: head })?;
1506        let exit = self.next_pc();
1507        self.patch_jump(exit_jump, exit);
1508        let frame = self.loops.pop().expect("loop frame is balanced");
1509        for jump in frame.breaks {
1510            self.patch_jump(jump, exit);
1511        }
1512        for jump in frame.continues {
1513            self.patch_jump(jump, head);
1514        }
1515        Ok(())
1516    }
1517
1518    fn lower_do_while(
1519        &mut self,
1520        builder: &mut ModuleBuilder,
1521        do_while: &DoWhileStatement,
1522    ) -> Result<(), LowerError> {
1523        let range = do_while.test.range();
1524        let head = self.next_pc();
1525        self.loops.push(LoopFrame {
1526            breaks: Vec::new(),
1527            continues: Vec::new(),
1528            is_loop: true,
1529        });
1530        self.lower_nested(builder, &do_while.body)?;
1531        let test_pc = self.next_pc();
1532        let condition = self.lower_expression(builder, &do_while.test)?;
1533        self.emit(
1534            range,
1535            Instruction::JumpIfTrue {
1536                condition,
1537                target: head,
1538            },
1539        )?;
1540        let exit = self.next_pc();
1541        let frame = self.loops.pop().expect("loop frame is balanced");
1542        for jump in frame.breaks {
1543            self.patch_jump(jump, exit);
1544        }
1545        for jump in frame.continues {
1546            self.patch_jump(jump, test_pc);
1547        }
1548        Ok(())
1549    }
1550
1551    fn lower_for(
1552        &mut self,
1553        builder: &mut ModuleBuilder,
1554        for_statement: &ForStatement,
1555    ) -> Result<(), LowerError> {
1556        self.push_scope();
1557        let result = self.lower_for_inner(builder, for_statement);
1558        self.pop_scope();
1559        result
1560    }
1561
1562    fn lower_for_inner(
1563        &mut self,
1564        builder: &mut ModuleBuilder,
1565        for_statement: &ForStatement,
1566    ) -> Result<(), LowerError> {
1567        let per_iteration_names = match &for_statement.initializer {
1568            Some(ForInitializer::Variable(declaration))
1569                if matches!(declaration.kind, VariableKind::Let | VariableKind::Const) =>
1570            {
1571                self.declaration_names(declaration)
1572            }
1573            _ => Vec::new(),
1574        };
1575        if let Some(initializer) = &for_statement.initializer {
1576            match initializer {
1577                ForInitializer::Variable(declaration) => {
1578                    if matches!(declaration.kind, VariableKind::Let | VariableKind::Const) {
1579                        self.lower_iteration_variable_declaration(builder, declaration)?;
1580                    } else {
1581                        self.lower_variable_declaration(builder, declaration)?;
1582                    }
1583                }
1584                ForInitializer::Expression(expression) => {
1585                    self.lower_expression(builder, expression)?;
1586                }
1587            }
1588        }
1589        let head = self.next_pc();
1590        let exit_jump = match &for_statement.test {
1591            Some(test) => {
1592                let condition = self.lower_expression(builder, test)?;
1593                Some(self.emit(
1594                    test.range(),
1595                    Instruction::JumpIfFalse {
1596                        condition,
1597                        target: Pc::new(0),
1598                    },
1599                )?)
1600            }
1601            None => None,
1602        };
1603        self.loops.push(LoopFrame {
1604            breaks: Vec::new(),
1605            continues: Vec::new(),
1606            is_loop: true,
1607        });
1608        self.lower_nested(builder, &for_statement.body)?;
1609        let update_pc = self.next_pc();
1610        self.rebind_iteration_cells(builder, &per_iteration_names, head_range(for_statement))?;
1611        if let Some(update) = &for_statement.update {
1612            self.lower_expression(builder, update)?;
1613        }
1614        self.emit(
1615            head_range(for_statement),
1616            Instruction::Jump { target: head },
1617        )?;
1618        let exit = self.next_pc();
1619        if let Some(jump) = exit_jump {
1620            self.patch_jump(jump, exit);
1621        }
1622        let frame = self.loops.pop().expect("loop frame is balanced");
1623        for jump in frame.breaks {
1624            self.patch_jump(jump, exit);
1625        }
1626        for jump in frame.continues {
1627            self.patch_jump(jump, update_pc);
1628        }
1629        Ok(())
1630    }
1631
1632    /// Lowers `for (binding in object)` via the enumerate-keys iterator.
1633    fn lower_for_in(
1634        &mut self,
1635        builder: &mut ModuleBuilder,
1636        range: TextRange,
1637        for_in: &ForInStatement,
1638    ) -> Result<(), LowerError> {
1639        let subject = self.lower_expression(builder, &for_in.object)?;
1640        self.lower_iteration(
1641            builder,
1642            range,
1643            subject,
1644            IteratorKind::Keys,
1645            &for_in.binding,
1646            &for_in.body,
1647        )
1648    }
1649
1650    /// Lowers `for (binding of iterable)` and `for await (binding of iterable)`.
1651    fn lower_for_of(
1652        &mut self,
1653        builder: &mut ModuleBuilder,
1654        range: TextRange,
1655        for_of: &ForOfStatement,
1656    ) -> Result<(), LowerError> {
1657        let subject = self.lower_expression(builder, &for_of.iterable)?;
1658        let kind = match for_of.mode {
1659            ForOfMode::Sync => IteratorKind::Sync,
1660            ForOfMode::Async => IteratorKind::Async,
1661        };
1662        self.lower_iteration(builder, range, subject, kind, &for_of.binding, &for_of.body)
1663    }
1664
1665    /// Shared iterator-driven loop for `for`/`of`, `for`/`in`, and
1666    /// `for await`/`of`.
1667    fn lower_iteration(
1668        &mut self,
1669        builder: &mut ModuleBuilder,
1670        range: TextRange,
1671        subject: Register,
1672        kind: IteratorKind,
1673        binding: &ForBinding,
1674        body: &Stmt,
1675    ) -> Result<(), LowerError> {
1676        self.push_scope();
1677        match binding {
1678            ForBinding::Variable(declaration)
1679                if matches!(
1680                    declaration.kind,
1681                    VariableKind::Using | VariableKind::AwaitUsing
1682                ) =>
1683            {
1684                self.pop_scope();
1685                return Err(self.unsupported(range, UnsupportedConstruct::UsingDeclaration));
1686            }
1687            _ => {}
1688        }
1689        let iterator = self.alloc_register(range)?;
1690        self.emit(
1691            range,
1692            Instruction::GetIterator {
1693                dst: iterator,
1694                src: subject,
1695                kind,
1696            },
1697        )?;
1698        let done = self.alloc_register(range)?;
1699        let value = self.alloc_register(range)?;
1700        let head = self.next_pc();
1701        self.emit(
1702            range,
1703            Instruction::IteratorNext {
1704                done,
1705                value,
1706                iterator,
1707            },
1708        )?;
1709        let exit_jump = self.emit(
1710            range,
1711            Instruction::JumpIfTrue {
1712                condition: done,
1713                target: Pc::new(0),
1714            },
1715        )?;
1716        self.loops.push(LoopFrame {
1717            breaks: Vec::new(),
1718            continues: Vec::new(),
1719            is_loop: true,
1720        });
1721        // Fresh per-iteration scope for the loop binding.
1722        self.push_scope();
1723        self.bind_for_binding(builder, binding, value, range)?;
1724        let body_result = self.lower_statement(builder, body);
1725        self.pop_scope();
1726        body_result?;
1727        self.emit(range, Instruction::Jump { target: head })?;
1728        let exit = self.next_pc();
1729        self.patch_jump(exit_jump, exit);
1730        let frame = self.loops.pop().expect("loop frame is balanced");
1731        for jump in frame.breaks {
1732            self.patch_jump(jump, exit);
1733        }
1734        for jump in frame.continues {
1735            self.patch_jump(jump, head);
1736        }
1737        self.pop_scope();
1738        Ok(())
1739    }
1740
1741    fn bind_for_binding(
1742        &mut self,
1743        builder: &mut ModuleBuilder,
1744        binding: &ForBinding,
1745        value: Register,
1746        range: TextRange,
1747    ) -> Result<(), LowerError> {
1748        match binding {
1749            ForBinding::Variable(declaration) => {
1750                let declaration_scope = match declaration.kind {
1751                    VariableKind::Var => DeclarationScope::Function,
1752                    VariableKind::Let | VariableKind::Const => DeclarationScope::Iteration,
1753                    VariableKind::Using | VariableKind::AwaitUsing => {
1754                        return Err(self.unsupported(range, UnsupportedConstruct::UsingDeclaration));
1755                    }
1756                };
1757                let declarator = declaration
1758                    .declarations
1759                    .first()
1760                    .ok_or_else(|| self.missing(range, NodeKind::VariableDeclarator))?;
1761                self.bind_pattern(
1762                    builder,
1763                    &declarator.data().binding,
1764                    value,
1765                    declaration_scope,
1766                )
1767            }
1768            ForBinding::Target(target) => self.assign_target(builder, target, value),
1769        }
1770    }
1771
1772    fn lower_break(
1773        &mut self,
1774        builder: &mut ModuleBuilder,
1775        range: TextRange,
1776        labeled: bool,
1777    ) -> Result<(), LowerError> {
1778        if labeled {
1779            return Err(self.unsupported(range, UnsupportedConstruct::LabeledJump));
1780        }
1781        if self.route_through_finally(builder, range, COMPLETION_BREAK, None)? {
1782            return Ok(());
1783        }
1784        let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1785        let frame = self.loops.last_mut().ok_or_else(|| LowerError {
1786            source: self.file.source_id(),
1787            range,
1788            kind: LowerErrorKind::MissingSyntax {
1789                expected: NodeKind::BreakStatement,
1790            },
1791        })?;
1792        frame.breaks.push(jump);
1793        Ok(())
1794    }
1795
1796    fn lower_continue(
1797        &mut self,
1798        builder: &mut ModuleBuilder,
1799        range: TextRange,
1800        labeled: bool,
1801    ) -> Result<(), LowerError> {
1802        if labeled {
1803            return Err(self.unsupported(range, UnsupportedConstruct::LabeledJump));
1804        }
1805        if self.route_through_finally(builder, range, COMPLETION_CONTINUE, None)? {
1806            return Ok(());
1807        }
1808        let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1809        let index = self.nearest_loop_index().ok_or_else(|| LowerError {
1810            source: self.file.source_id(),
1811            range,
1812            kind: LowerErrorKind::MissingSyntax {
1813                expected: NodeKind::ContinueStatement,
1814            },
1815        })?;
1816        self.loops[index].continues.push(jump);
1817        Ok(())
1818    }
1819
1820    /// Routes an abrupt completion (`return`/`break`/`continue`) through the
1821    /// innermost enclosing `finally`, if one is live and the completion crosses
1822    /// it. Returns `true` when the completion was routed.
1823    fn route_through_finally(
1824        &mut self,
1825        builder: &mut ModuleBuilder,
1826        range: TextRange,
1827        kind: i32,
1828        value: Option<Register>,
1829    ) -> Result<bool, LowerError> {
1830        let Some((kind_reg, value_reg, depth)) = self
1831            .finally_stack
1832            .last()
1833            .map(|frame| (frame.kind_reg, frame.value_reg, frame.loop_depth))
1834        else {
1835            return Ok(false);
1836        };
1837        // Determine the frame this completion targets; if that frame predates
1838        // the finally, the completion crosses it and routes through it.
1839        let target = match kind {
1840            COMPLETION_BREAK => {
1841                if self.loops.is_empty() {
1842                    return Ok(false);
1843                }
1844                Some(self.loops.len() - 1)
1845            }
1846            COMPLETION_CONTINUE => match self.nearest_loop_index() {
1847                Some(index) => Some(index),
1848                None => return Ok(false),
1849            },
1850            _ => None,
1851        };
1852        if let Some(target) = target
1853            && target >= depth
1854        {
1855            return Ok(false);
1856        }
1857        let marker = self.load_constant(builder, Constant::Int32(kind), range)?;
1858        self.move_to(range, kind_reg, marker)?;
1859        if let Some(value) = value {
1860            self.move_to(range, value_reg, value)?;
1861        }
1862        let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1863        self.finally_stack
1864            .last_mut()
1865            .expect("finally frame present")
1866            .pending
1867            .push(jump);
1868        Ok(true)
1869    }
1870
1871    /// The index of the innermost enclosing real loop (skipping `switch`
1872    /// break-scopes), which is where a `continue` transfers.
1873    fn nearest_loop_index(&self) -> Option<usize> {
1874        self.loops.iter().rposition(|frame| frame.is_loop)
1875    }
1876
1877    fn lower_switch(
1878        &mut self,
1879        builder: &mut ModuleBuilder,
1880        range: TextRange,
1881        switch: &SwitchStatement,
1882    ) -> Result<(), LowerError> {
1883        let discriminant = self.lower_expression(builder, &switch.discriminant)?;
1884        self.push_scope();
1885        let switch_statements = switch
1886            .cases
1887            .iter()
1888            .flat_map(|case| case.data().consequent.iter().cloned())
1889            .collect::<Vec<_>>();
1890        self.instantiate_declarations(builder, &switch_statements, true)?;
1891        self.loops.push(LoopFrame {
1892            breaks: Vec::new(),
1893            continues: Vec::new(),
1894            is_loop: false,
1895        });
1896        let mut case_jumps: Vec<Option<Pc>> = Vec::with_capacity(switch.cases.len());
1897        let mut default_index = None;
1898        for (index, case) in switch.cases.iter().enumerate() {
1899            match &case.data().test {
1900                Some(test) => {
1901                    let value = self.lower_expression(builder, test)?;
1902                    let matched = self.alloc_register(range)?;
1903                    self.emit(
1904                        range,
1905                        Instruction::Binary {
1906                            dst: matched,
1907                            op: BinaryOp::StrictEqual,
1908                            left: discriminant,
1909                            right: value,
1910                        },
1911                    )?;
1912                    let jump = self.emit(
1913                        range,
1914                        Instruction::JumpIfTrue {
1915                            condition: matched,
1916                            target: Pc::new(0),
1917                        },
1918                    )?;
1919                    case_jumps.push(Some(jump));
1920                }
1921                None => {
1922                    default_index = Some(index);
1923                    case_jumps.push(None);
1924                }
1925            }
1926        }
1927        let no_match_jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1928        let mut body_starts: Vec<Pc> = Vec::with_capacity(switch.cases.len());
1929        for case in &switch.cases {
1930            let start = self.next_pc();
1931            body_starts.push(start);
1932            for statement in &case.data().consequent {
1933                self.lower_statement(builder, statement)?;
1934            }
1935        }
1936        let exit = self.next_pc();
1937        for (jump, start) in case_jumps.iter().zip(body_starts.iter()) {
1938            if let Some(jump) = jump {
1939                self.patch_jump(*jump, *start);
1940            }
1941        }
1942        match default_index {
1943            Some(index) => self.patch_jump(no_match_jump, body_starts[index]),
1944            None => self.patch_jump(no_match_jump, exit),
1945        }
1946        let frame = self.loops.pop().expect("switch break frame is balanced");
1947
1948        for jump in frame.breaks {
1949            self.patch_jump(jump, exit);
1950        }
1951        self.pop_scope();
1952        Ok(())
1953    }
1954
1955    fn lower_try(
1956        &mut self,
1957        builder: &mut ModuleBuilder,
1958        range: TextRange,
1959        try_statement: &crate::syntax::TryStatement,
1960    ) -> Result<(), LowerError> {
1961        let has_finally = try_statement
1962            .finalizer
1963            .as_ref()
1964            .is_some_and(|finalizer| !finalizer.data().statements.is_empty());
1965        if has_finally {
1966            return self.lower_try_finally(builder, range, try_statement);
1967        }
1968        let Some(handler_clause) = &try_statement.handler else {
1969            self.push_scope();
1970            let result = self.lower_block(builder, try_statement.block.data());
1971            self.pop_scope();
1972            return result;
1973        };
1974
1975        let start = self.next_pc();
1976        self.push_scope();
1977        let block_result = self.lower_block(builder, try_statement.block.data());
1978        self.pop_scope();
1979        block_result?;
1980        let end = self.next_pc();
1981        if end.get() == start.get() {
1982            return Ok(());
1983        }
1984        let over_catch = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1985
1986        let catch_register = self.alloc_register(range)?;
1987        let handler_pc = self.next_pc();
1988        if let Some(completion) = self.completion {
1989            let undefined = self.undefined(builder, range)?;
1990            self.move_to(range, completion, undefined)?;
1991        }
1992        self.push_scope();
1993        let clause = handler_clause.data();
1994        if let Some(binding) = &clause.binding {
1995            let bind_result =
1996                self.bind_pattern(builder, binding, catch_register, DeclarationScope::Lexical);
1997            if let Err(error) = bind_result {
1998                self.pop_scope();
1999                return Err(error);
2000            }
2001        }
2002        let catch_result = self.lower_block(builder, clause.body.data());
2003        self.pop_scope();
2004        catch_result?;
2005        let after = self.next_pc();
2006        self.patch_jump(over_catch, after);
2007
2008        self.handlers.push(ExceptionHandler {
2009            start,
2010            end,
2011            handler: handler_pc,
2012            catch_register,
2013        });
2014        Ok(())
2015    }
2016
2017    /// Lowers `try`/`finally` (with an optional `catch`) by routing every
2018    /// completion of the protected body through the finally block, then
2019    /// dispatching the recorded completion after it runs.
2020    fn lower_try_finally(
2021        &mut self,
2022        builder: &mut ModuleBuilder,
2023        range: TextRange,
2024        try_statement: &crate::syntax::TryStatement,
2025    ) -> Result<(), LowerError> {
2026        let finalizer = try_statement
2027            .finalizer
2028            .as_ref()
2029            .expect("lower_try_finally is only called with a non-empty finalizer");
2030        let kind_reg = self.alloc_register(range)?;
2031        let value_reg = self.alloc_register(range)?;
2032        let normal = self.load_constant(builder, Constant::Int32(COMPLETION_NORMAL), range)?;
2033        self.move_to(range, kind_reg, normal)?;
2034        let undefined = self.undefined(builder, range)?;
2035        self.move_to(range, value_reg, undefined)?;
2036        self.finally_stack.push(FinallyFrame {
2037            kind_reg,
2038            value_reg,
2039            pending: Vec::new(),
2040            loop_depth: self.loops.len(),
2041        });
2042
2043        let start = self.next_pc();
2044        self.push_scope();
2045        let body_result = self.lower_block(builder, try_statement.block.data());
2046        self.pop_scope();
2047        if let Err(error) = body_result {
2048            self.finally_stack.pop();
2049            return Err(error);
2050        }
2051        let end = self.next_pc();
2052        // Normal completion of the try body routes to the finally.
2053        self.push_finally_completion(builder, range, COMPLETION_NORMAL, None)?;
2054
2055        let handler = if end.get() == start.get() {
2056            None
2057        } else {
2058            let catch_register = self.alloc_register(range)?;
2059            let handler_pc = self.next_pc();
2060            if let Some(handler_clause) = &try_statement.handler {
2061                if let Some(completion) = self.completion {
2062                    let undefined = self.undefined(builder, range)?;
2063                    self.move_to(range, completion, undefined)?;
2064                }
2065                self.push_scope();
2066                let clause = handler_clause.data();
2067                if let Some(binding) = &clause.binding {
2068                    let bind_result = self.bind_pattern(
2069                        builder,
2070                        binding,
2071                        catch_register,
2072                        DeclarationScope::Lexical,
2073                    );
2074                    if let Err(error) = bind_result {
2075                        self.pop_scope();
2076                        self.finally_stack.pop();
2077                        return Err(error);
2078                    }
2079                }
2080                let catch_result = self.lower_block(builder, clause.body.data());
2081                self.pop_scope();
2082                if let Err(error) = catch_result {
2083                    self.finally_stack.pop();
2084                    return Err(error);
2085                }
2086                self.push_finally_completion(builder, range, COMPLETION_NORMAL, None)?;
2087            } else {
2088                // No catch: record the thrown value and re-raise after finally.
2089                self.push_finally_completion(
2090                    builder,
2091                    range,
2092                    COMPLETION_THROW,
2093                    Some(catch_register),
2094                )?;
2095            }
2096            Some((catch_register, handler_pc))
2097        };
2098
2099        let frame = self.finally_stack.pop().expect("finally frame present");
2100        let finally_pc = self.next_pc();
2101        for jump in &frame.pending {
2102            self.patch_jump(*jump, finally_pc);
2103        }
2104        if let Some((catch_register, handler_pc)) = handler {
2105            self.handlers.push(ExceptionHandler {
2106                start,
2107                end,
2108                handler: handler_pc,
2109                catch_register,
2110            });
2111        }
2112        self.push_scope();
2113        let finally_result = self.lower_without_completion(builder, |this, builder| {
2114            this.lower_block(builder, finalizer.data())
2115        });
2116        self.pop_scope();
2117        finally_result?;
2118        self.emit_finally_dispatch(builder, range, kind_reg, value_reg)
2119    }
2120
2121    /// Records a completion (sets the state registers) and jumps to the pending
2122    /// finally entry.
2123    fn push_finally_completion(
2124        &mut self,
2125        builder: &mut ModuleBuilder,
2126        range: TextRange,
2127        kind: i32,
2128        value: Option<Register>,
2129    ) -> Result<(), LowerError> {
2130        let (kind_reg, value_reg) = {
2131            let frame = self.finally_stack.last().expect("finally frame present");
2132            (frame.kind_reg, frame.value_reg)
2133        };
2134        let marker = self.load_constant(builder, Constant::Int32(kind), range)?;
2135        self.move_to(range, kind_reg, marker)?;
2136        if let Some(value) = value {
2137            self.move_to(range, value_reg, value)?;
2138        }
2139        let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2140        self.finally_stack
2141            .last_mut()
2142            .expect("finally frame present")
2143            .pending
2144            .push(jump);
2145        Ok(())
2146    }
2147
2148    /// After a finally block runs, resumes the recorded completion.
2149    fn emit_finally_dispatch(
2150        &mut self,
2151        builder: &mut ModuleBuilder,
2152        range: TextRange,
2153        kind_reg: Register,
2154        value_reg: Register,
2155    ) -> Result<(), LowerError> {
2156        // return
2157        let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_RETURN)?;
2158        self.emit(range, Instruction::Return { value: value_reg })?;
2159        let after = self.next_pc();
2160        self.patch_jump(skip, after);
2161        // throw
2162        let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_THROW)?;
2163        self.emit(range, Instruction::Throw { value: value_reg })?;
2164        let after = self.next_pc();
2165        self.patch_jump(skip, after);
2166        // break
2167        let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_BREAK)?;
2168        let break_jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2169        match self.loops.last_mut() {
2170            Some(frame) => frame.breaks.push(break_jump),
2171            None => {
2172                let target = self.next_pc();
2173                self.patch_jump(break_jump, target);
2174            }
2175        }
2176        let after = self.next_pc();
2177        self.patch_jump(skip, after);
2178        // continue
2179        let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_CONTINUE)?;
2180        let continue_jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2181        match self.nearest_loop_index() {
2182            Some(index) => self.loops[index].continues.push(continue_jump),
2183            None => {
2184                let target = self.next_pc();
2185                self.patch_jump(continue_jump, target);
2186            }
2187        }
2188        let after = self.next_pc();
2189        self.patch_jump(skip, after);
2190        Ok(())
2191    }
2192
2193    /// Emits `if kind_reg != kind { jump skip }`, returning the skip jump to
2194    /// patch past the guarded completion.
2195    fn emit_kind_guard(
2196        &mut self,
2197        builder: &mut ModuleBuilder,
2198        range: TextRange,
2199        kind_reg: Register,
2200        kind: i32,
2201    ) -> Result<Pc, LowerError> {
2202        let marker = self.load_constant(builder, Constant::Int32(kind), range)?;
2203        let matched = self.alloc_register(range)?;
2204        self.emit(
2205            range,
2206            Instruction::Binary {
2207                dst: matched,
2208                op: BinaryOp::StrictEqual,
2209                left: kind_reg,
2210                right: marker,
2211            },
2212        )?;
2213        self.emit(
2214            range,
2215            Instruction::JumpIfFalse {
2216                condition: matched,
2217                target: Pc::new(0),
2218            },
2219        )
2220    }
2221
2222    fn lower_variable_declaration(
2223        &mut self,
2224        builder: &mut ModuleBuilder,
2225        declaration: &VariableDeclaration,
2226    ) -> Result<(), LowerError> {
2227        let declaration_scope = match declaration.kind {
2228            VariableKind::Var => DeclarationScope::Function,
2229            VariableKind::Let | VariableKind::Const => DeclarationScope::Lexical,
2230            VariableKind::Using | VariableKind::AwaitUsing => {
2231                let range = declaration
2232                    .declarations
2233                    .first()
2234                    .map_or_else(zero_range, |declarator| declarator.range());
2235                return Err(self.unsupported(range, UnsupportedConstruct::UsingDeclaration));
2236            }
2237        };
2238        for declarator in &declaration.declarations {
2239            let range = declarator.range();
2240            let data = declarator.data();
2241            self.predeclare_captured_pattern(&data.binding, declaration_scope)?;
2242            let value = match &data.initializer {
2243                Some(initializer) => self.lower_expression(builder, initializer)?,
2244                None => {
2245                    // A bare declaration (`let x;`) binds undefined.
2246                    self.undefined(builder, range)?
2247                }
2248            };
2249            self.bind_pattern(builder, &data.binding, value, declaration_scope)?;
2250        }
2251        Ok(())
2252    }
2253
2254    fn lower_iteration_variable_declaration(
2255        &mut self,
2256        builder: &mut ModuleBuilder,
2257        declaration: &VariableDeclaration,
2258    ) -> Result<(), LowerError> {
2259        debug_assert!(matches!(
2260            declaration.kind,
2261            VariableKind::Let | VariableKind::Const
2262        ));
2263        for declarator in &declaration.declarations {
2264            let range = declarator.range();
2265            let data = declarator.data();
2266            self.predeclare_captured_pattern(&data.binding, DeclarationScope::Iteration)?;
2267            let value = match &data.initializer {
2268                Some(initializer) => self.lower_expression(builder, initializer)?,
2269                None => self.undefined(builder, range)?,
2270            };
2271            self.bind_pattern(builder, &data.binding, value, DeclarationScope::Iteration)?;
2272        }
2273        Ok(())
2274    }
2275
2276    fn instantiate_function_declaration(
2277        &mut self,
2278        builder: &mut ModuleBuilder,
2279        range: TextRange,
2280        name: &str,
2281        site: BindingSite,
2282        function: &FunctionLike,
2283    ) -> Result<(), LowerError> {
2284        if function.body.is_none() {
2285            return Ok(());
2286        }
2287        self.predeclare_captured_binding(name, range, site, DeclarationScope::Function)?;
2288        let closure = self.build_constructible_function_value(
2289            builder,
2290            range,
2291            Some(name.to_owned()),
2292            function,
2293        )?;
2294        self.store_binding(
2295            builder,
2296            name,
2297            closure,
2298            range,
2299            site,
2300            DeclarationScope::Function,
2301        )
2302    }
2303
2304    // ------------------------------------------------------------------
2305    // Expressions
2306    // ------------------------------------------------------------------
2307
2308    fn lower_expression(
2309        &mut self,
2310        builder: &mut ModuleBuilder,
2311        expression: &Expr,
2312    ) -> Result<Register, LowerError> {
2313        let range = expression.range();
2314        match expression.data() {
2315            Expression::Identifier(identifier) => {
2316                let name = self.identifier_text(identifier)?;
2317                self.read_name(builder, &name, range)
2318            }
2319            Expression::This => self.this_value(range),
2320            Expression::Super => {
2321                // Derived constructors handle their one supported `super(...)`
2322                // call directly so it uses the captured parent and existing receiver.
2323                return Err(self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape));
2324            }
2325            Expression::Literal(literal) => self.lower_literal(builder, range, literal),
2326            Expression::Template(template) => self.lower_template(builder, range, template),
2327            Expression::TaggedTemplate(tagged) => {
2328                self.lower_tagged_template(builder, range, tagged)
2329            }
2330            Expression::Array(array) => self.lower_array(builder, range, array),
2331            Expression::Object(object) => self.lower_object(builder, range, object),
2332            Expression::Function(function) => {
2333                self.build_constructible_function_value(builder, range, None, &function.function)
2334            }
2335            Expression::Class(class) => {
2336                let name = class
2337                    .class
2338                    .name
2339                    .as_ref()
2340                    .map(|identifier| {
2341                        self.identifier_text(identifier)
2342                            .map(|name| (name, binding_site(identifier.range())))
2343                    })
2344                    .transpose()?;
2345                self.lower_class_value(
2346                    builder,
2347                    range,
2348                    &class.class,
2349                    None,
2350                    name.as_ref().map(|(name, site)| (name.as_str(), *site)),
2351                )
2352            }
2353            Expression::Arrow(arrow) => self.lower_arrow(builder, range, arrow),
2354            Expression::Call(call) => self.lower_call(builder, range, call),
2355            Expression::Member(member) => {
2356                let (_, value) = self.lower_member(builder, range, member)?;
2357                Ok(value)
2358            }
2359            Expression::New(new) => self.lower_new(builder, range, new),
2360            Expression::Await(await_expression) => {
2361                self.lower_await(builder, range, await_expression)
2362            }
2363            Expression::Yield(yield_expression) => {
2364                self.lower_yield(builder, range, yield_expression)
2365            }
2366            Expression::Unary(unary) => self.lower_unary(builder, range, unary),
2367            Expression::Update(update) => self.lower_update(builder, range, update),
2368            Expression::Binary(binary) => self.lower_binary(builder, range, binary),
2369            Expression::Logical(logical) => self.lower_logical(builder, range, logical),
2370            Expression::Conditional(conditional) => {
2371                self.lower_conditional(builder, range, conditional)
2372            }
2373            Expression::Assignment(assignment) => self.lower_assignment(builder, range, assignment),
2374            Expression::Sequence(sequence) => {
2375                let mut last = None;
2376                for expression in &sequence.expressions {
2377                    last = Some(self.lower_expression(builder, expression)?);
2378                }
2379                match last {
2380                    Some(register) => Ok(register),
2381                    None => self.undefined(builder, range),
2382                }
2383            }
2384            Expression::Parenthesized(inner) => self.lower_expression(builder, inner),
2385            Expression::As(as_expression) => {
2386                self.lower_expression(builder, &as_expression.expression)
2387            }
2388            Expression::Satisfies(satisfies) => {
2389                self.lower_expression(builder, &satisfies.expression)
2390            }
2391            Expression::TypeAssertion(assertion) => {
2392                self.lower_expression(builder, &assertion.expression)
2393            }
2394            Expression::NonNull(non_null) => self.lower_expression(builder, &non_null.expression),
2395            Expression::Import(import) => self.lower_import_expression(builder, range, import),
2396            Expression::Meta(meta) => match meta {
2397                MetaProperty::NewTarget => self.new_target_value(range),
2398                MetaProperty::ImportMeta => {
2399                    Err(self.unsupported(range, UnsupportedConstruct::ImportMeta))
2400                }
2401            },
2402            Expression::Missing(missing) => Err(self.missing(range, missing.expected())),
2403        }
2404    }
2405
2406    fn lower_unary(
2407        &mut self,
2408        builder: &mut ModuleBuilder,
2409        range: TextRange,
2410        unary: &crate::syntax::UnaryExpression,
2411    ) -> Result<Register, LowerError> {
2412        let op = match unary.operator {
2413            UnaryOperator::Void => {
2414                self.lower_expression(builder, &unary.argument)?;
2415                return self.undefined(builder, range);
2416            }
2417            UnaryOperator::Delete => return self.lower_delete(builder, range, &unary.argument),
2418            UnaryOperator::Typeof => return self.lower_typeof(builder, range, &unary.argument),
2419            UnaryOperator::Plus => UnaryOp::Plus,
2420            UnaryOperator::Minus => UnaryOp::Negate,
2421            UnaryOperator::Not => UnaryOp::LogicalNot,
2422            UnaryOperator::BitNot => UnaryOp::BitwiseNot,
2423        };
2424        let operand = self.lower_expression(builder, &unary.argument)?;
2425        let dst = self.alloc_register(range)?;
2426        self.emit(range, Instruction::Unary { dst, op, operand })?;
2427        Ok(dst)
2428    }
2429
2430    /// `typeof x` never throws for a free/undeclared name, so a bare-identifier
2431    /// operand that resolves to the environment uses [`Instruction::TypeOfGlobal`].
2432    fn lower_typeof(
2433        &mut self,
2434        builder: &mut ModuleBuilder,
2435        range: TextRange,
2436        argument: &Expr,
2437    ) -> Result<Register, LowerError> {
2438        if let Expression::Identifier(identifier) = argument.data() {
2439            let name = self.identifier_text(identifier)?;
2440            let resolved = self.resolve(&name).is_some()
2441                || (name == "arguments" && !matches!(self.arguments_source, ArgumentsSource::None))
2442                || name == "undefined";
2443            if !resolved {
2444                let id = builder.intern(Constant::String(EcmaString::from_utf8(&name)), range)?;
2445                let dst = self.alloc_register(range)?;
2446                self.emit(range, Instruction::TypeOfGlobal { dst, name: id })?;
2447                return Ok(dst);
2448            }
2449        }
2450        let operand = self.lower_expression(builder, argument)?;
2451        let dst = self.alloc_register(range)?;
2452        self.emit(
2453            range,
2454            Instruction::Unary {
2455                dst,
2456                op: UnaryOp::TypeOf,
2457                operand,
2458            },
2459        )?;
2460        Ok(dst)
2461    }
2462
2463    fn lower_delete(
2464        &mut self,
2465        builder: &mut ModuleBuilder,
2466        range: TextRange,
2467        argument: &Expr,
2468    ) -> Result<Register, LowerError> {
2469        match argument.data() {
2470            Expression::Member(member) => {
2471                if member.optional {
2472                    return self.lower_optional_delete(builder, range, member);
2473                }
2474                let object = self.lower_expression(builder, &member.object)?;
2475                let key = self.member_key(builder, &member.property)?;
2476                let dst = self.alloc_register(range)?;
2477                self.emit(range, Instruction::DeleteProperty { dst, object, key })?;
2478                Ok(dst)
2479            }
2480            Expression::Parenthesized(inner) => self.lower_delete(builder, range, inner),
2481            // `delete x` of a binding is a no-op returning false in strict/module
2482            // code (bindings are non-configurable).
2483            _ => self.load_constant(builder, Constant::Boolean(false), range),
2484        }
2485    }
2486
2487    fn lower_optional_delete(
2488        &mut self,
2489        builder: &mut ModuleBuilder,
2490        range: TextRange,
2491        member: &MemberExpression,
2492    ) -> Result<Register, LowerError> {
2493        let result = self.alloc_register(range)?;
2494        let truthy = self.load_constant(builder, Constant::Boolean(true), range)?;
2495        self.move_to(range, result, truthy)?;
2496        let object = self.lower_expression(builder, &member.object)?;
2497        let is_nullish = self.compute_nullish(builder, range, object)?;
2498        let skip = self.emit(
2499            range,
2500            Instruction::JumpIfTrue {
2501                condition: is_nullish,
2502                target: Pc::new(0),
2503            },
2504        )?;
2505        let key = self.member_key(builder, &member.property)?;
2506        let deleted = self.alloc_register(range)?;
2507        self.emit(
2508            range,
2509            Instruction::DeleteProperty {
2510                dst: deleted,
2511                object,
2512                key,
2513            },
2514        )?;
2515        self.move_to(range, result, deleted)?;
2516        let end = self.next_pc();
2517        self.patch_jump(skip, end);
2518        Ok(result)
2519    }
2520
2521    fn lower_binary(
2522        &mut self,
2523        builder: &mut ModuleBuilder,
2524        range: TextRange,
2525        binary: &BinaryExpression,
2526    ) -> Result<Register, LowerError> {
2527        let op = map_binary_operator(binary.operator);
2528        let left = self.lower_expression(builder, &binary.left)?;
2529        let right = self.lower_expression(builder, &binary.right)?;
2530        let dst = self.alloc_register(range)?;
2531        self.emit(
2532            range,
2533            Instruction::Binary {
2534                dst,
2535                op,
2536                left,
2537                right,
2538            },
2539        )?;
2540        Ok(dst)
2541    }
2542
2543    fn lower_logical(
2544        &mut self,
2545        builder: &mut ModuleBuilder,
2546        range: TextRange,
2547        logical: &LogicalExpression,
2548    ) -> Result<Register, LowerError> {
2549        let result = self.alloc_register(range)?;
2550        let left = self.lower_expression(builder, &logical.left)?;
2551        self.move_to(range, result, left)?;
2552        let short_circuit = self.branch_on_short_circuit(builder, range, logical.operator, left)?;
2553        let right = self.lower_expression(builder, &logical.right)?;
2554        self.move_to(range, result, right)?;
2555        let end = self.next_pc();
2556        self.patch_jump(short_circuit, end);
2557        Ok(result)
2558    }
2559
2560    /// Emits the branch that keeps the left operand of a short-circuit
2561    /// operator, returning the placeholder jump to patch to the merge.
2562    fn branch_on_short_circuit(
2563        &mut self,
2564        builder: &mut ModuleBuilder,
2565        range: TextRange,
2566        operator: LogicalOperator,
2567        left: Register,
2568    ) -> Result<Pc, LowerError> {
2569        match operator {
2570            LogicalOperator::And => self.emit(
2571                range,
2572                Instruction::JumpIfFalse {
2573                    condition: left,
2574                    target: Pc::new(0),
2575                },
2576            ),
2577            LogicalOperator::Or => self.emit(
2578                range,
2579                Instruction::JumpIfTrue {
2580                    condition: left,
2581                    target: Pc::new(0),
2582                },
2583            ),
2584            LogicalOperator::Nullish => {
2585                let is_nullish = self.compute_nullish(builder, range, left)?;
2586                self.emit(
2587                    range,
2588                    Instruction::JumpIfFalse {
2589                        condition: is_nullish,
2590                        target: Pc::new(0),
2591                    },
2592                )
2593            }
2594        }
2595    }
2596
2597    /// Computes whether `value` is `null` or `undefined` (`value == null`).
2598    fn compute_nullish(
2599        &mut self,
2600        builder: &mut ModuleBuilder,
2601        range: TextRange,
2602        value: Register,
2603    ) -> Result<Register, LowerError> {
2604        let null = self.load_constant(builder, Constant::Null, range)?;
2605        let is_null = self.alloc_register(range)?;
2606        self.emit(
2607            range,
2608            Instruction::Binary {
2609                dst: is_null,
2610                op: BinaryOp::Equal,
2611                left: value,
2612                right: null,
2613            },
2614        )?;
2615        Ok(is_null)
2616    }
2617
2618    fn lower_conditional(
2619        &mut self,
2620        builder: &mut ModuleBuilder,
2621        range: TextRange,
2622        conditional: &ConditionalExpression,
2623    ) -> Result<Register, LowerError> {
2624        let result = self.alloc_register(range)?;
2625        let condition = self.lower_expression(builder, &conditional.test)?;
2626        let to_alternate = self.emit(
2627            range,
2628            Instruction::JumpIfFalse {
2629                condition,
2630                target: Pc::new(0),
2631            },
2632        )?;
2633        let consequent = self.lower_expression(builder, &conditional.consequent)?;
2634        self.move_to(range, result, consequent)?;
2635        let to_end = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2636        let alternate_pc = self.next_pc();
2637        self.patch_jump(to_alternate, alternate_pc);
2638        let alternate = self.lower_expression(builder, &conditional.alternate)?;
2639        self.move_to(range, result, alternate)?;
2640        let end = self.next_pc();
2641        self.patch_jump(to_end, end);
2642        Ok(result)
2643    }
2644
2645    // ------------------------------------------------------------------
2646    // Assignment and update
2647    // ------------------------------------------------------------------
2648
2649    fn lower_assignment(
2650        &mut self,
2651        builder: &mut ModuleBuilder,
2652        range: TextRange,
2653        assignment: &AssignmentExpression,
2654    ) -> Result<Register, LowerError> {
2655        match assignment.left.data() {
2656            AssignmentTarget::Identifier(identifier) => {
2657                let name = self.identifier_text(identifier)?;
2658                self.lower_identifier_assignment(builder, range, &name, assignment)
2659            }
2660            AssignmentTarget::Member(member) => {
2661                self.lower_member_assignment(builder, range, member, assignment)
2662            }
2663            AssignmentTarget::Object(_) | AssignmentTarget::Array(_) => {
2664                // Destructuring assignment applies only for `=`.
2665                if compound_operator(assignment.operator).is_some() {
2666                    return Err(
2667                        self.missing(assignment.left.range(), NodeKind::AssignmentExpression)
2668                    );
2669                }
2670                let value = self.lower_expression(builder, &assignment.right)?;
2671                self.assign_target(builder, &assignment.left, value)?;
2672                Ok(value)
2673            }
2674            AssignmentTarget::Missing(missing) => {
2675                Err(self.missing(assignment.left.range(), missing.expected()))
2676            }
2677        }
2678    }
2679
2680    fn lower_identifier_assignment(
2681        &mut self,
2682        builder: &mut ModuleBuilder,
2683        range: TextRange,
2684        name: &str,
2685        assignment: &AssignmentExpression,
2686    ) -> Result<Register, LowerError> {
2687        match compound_operator(assignment.operator) {
2688            None => {
2689                let value = self.lower_expression(builder, &assignment.right)?;
2690                self.assign_name(builder, name, value, range)?;
2691                Ok(value)
2692            }
2693            Some(CompoundOp::Arithmetic(op)) => {
2694                let current = self.read_name_value(builder, name, range)?;
2695                let right = self.lower_expression(builder, &assignment.right)?;
2696                let result = self.alloc_register(range)?;
2697                self.emit(
2698                    range,
2699                    Instruction::Binary {
2700                        dst: result,
2701                        op,
2702                        left: current,
2703                        right,
2704                    },
2705                )?;
2706                self.assign_name(builder, name, result, range)?;
2707                Ok(result)
2708            }
2709            Some(CompoundOp::Logical(op)) => {
2710                let result = self.alloc_register(range)?;
2711                let current = self.read_name_value(builder, name, range)?;
2712                self.move_to(range, result, current)?;
2713                let skip = self.branch_on_short_circuit(builder, range, op, current)?;
2714                let value = self.lower_expression(builder, &assignment.right)?;
2715                self.assign_name(builder, name, value, range)?;
2716                self.move_to(range, result, value)?;
2717                let end = self.next_pc();
2718                self.patch_jump(skip, end);
2719                Ok(result)
2720            }
2721        }
2722    }
2723
2724    fn lower_member_assignment(
2725        &mut self,
2726        builder: &mut ModuleBuilder,
2727        range: TextRange,
2728        member: &AssignmentMemberTarget,
2729        assignment: &AssignmentExpression,
2730    ) -> Result<Register, LowerError> {
2731        let object = self.lower_expression(builder, &member.object)?;
2732        let key = self.member_key(builder, &member.property)?;
2733        match compound_operator(assignment.operator) {
2734            None => {
2735                let value = self.lower_expression(builder, &assignment.right)?;
2736                self.emit(range, Instruction::SetProperty { object, key, value })?;
2737                Ok(value)
2738            }
2739            Some(CompoundOp::Arithmetic(op)) => {
2740                let current = self.alloc_register(range)?;
2741                self.emit(
2742                    range,
2743                    Instruction::GetProperty {
2744                        dst: current,
2745                        object,
2746                        key,
2747                    },
2748                )?;
2749                let right = self.lower_expression(builder, &assignment.right)?;
2750                let result = self.alloc_register(range)?;
2751                self.emit(
2752                    range,
2753                    Instruction::Binary {
2754                        dst: result,
2755                        op,
2756                        left: current,
2757                        right,
2758                    },
2759                )?;
2760                self.emit(
2761                    range,
2762                    Instruction::SetProperty {
2763                        object,
2764                        key,
2765                        value: result,
2766                    },
2767                )?;
2768                Ok(result)
2769            }
2770            Some(CompoundOp::Logical(op)) => {
2771                let result = self.alloc_register(range)?;
2772                let current = self.alloc_register(range)?;
2773                self.emit(
2774                    range,
2775                    Instruction::GetProperty {
2776                        dst: current,
2777                        object,
2778                        key,
2779                    },
2780                )?;
2781                self.move_to(range, result, current)?;
2782                let skip = self.branch_on_short_circuit(builder, range, op, current)?;
2783                let value = self.lower_expression(builder, &assignment.right)?;
2784                self.emit(range, Instruction::SetProperty { object, key, value })?;
2785                self.move_to(range, result, value)?;
2786                let end = self.next_pc();
2787                self.patch_jump(skip, end);
2788                Ok(result)
2789            }
2790        }
2791    }
2792
2793    fn lower_update(
2794        &mut self,
2795        builder: &mut ModuleBuilder,
2796        range: TextRange,
2797        update: &UpdateExpression,
2798    ) -> Result<Register, LowerError> {
2799        let op = match update.operator {
2800            UpdateOperator::Increment => BinaryOp::Add,
2801            UpdateOperator::Decrement => BinaryOp::Subtract,
2802        };
2803        match update.argument.data() {
2804            AssignmentTarget::Identifier(identifier) => {
2805                let name = self.identifier_text(identifier)?;
2806                let current = self.read_name_value(builder, &name, range)?;
2807                let old = self.alloc_register(range)?;
2808                self.move_to(range, old, current)?;
2809                let one = self.load_constant(builder, Constant::Int32(1), range)?;
2810                let updated = self.alloc_register(range)?;
2811                self.emit(
2812                    range,
2813                    Instruction::Binary {
2814                        dst: updated,
2815                        op,
2816                        left: old,
2817                        right: one,
2818                    },
2819                )?;
2820                self.assign_name(builder, &name, updated, range)?;
2821                Ok(if update.prefix { updated } else { old })
2822            }
2823            AssignmentTarget::Member(member) => {
2824                let object = self.lower_expression(builder, &member.object)?;
2825                let key = self.member_key(builder, &member.property)?;
2826                let old = self.alloc_register(range)?;
2827                self.emit(
2828                    range,
2829                    Instruction::GetProperty {
2830                        dst: old,
2831                        object,
2832                        key,
2833                    },
2834                )?;
2835                let one = self.load_constant(builder, Constant::Int32(1), range)?;
2836                let updated = self.alloc_register(range)?;
2837                self.emit(
2838                    range,
2839                    Instruction::Binary {
2840                        dst: updated,
2841                        op,
2842                        left: old,
2843                        right: one,
2844                    },
2845                )?;
2846                self.emit(
2847                    range,
2848                    Instruction::SetProperty {
2849                        object,
2850                        key,
2851                        value: updated,
2852                    },
2853                )?;
2854                Ok(if update.prefix { updated } else { old })
2855            }
2856            AssignmentTarget::Object(_) | AssignmentTarget::Array(_) => {
2857                Err(self.missing(update.argument.range(), NodeKind::UpdateExpression))
2858            }
2859            AssignmentTarget::Missing(missing) => {
2860                Err(self.missing(update.argument.range(), missing.expected()))
2861            }
2862        }
2863    }
2864
2865    /// `await x` suspends on `x` and resumes with the settled value in `dst`.
2866    fn lower_await(
2867        &mut self,
2868        builder: &mut ModuleBuilder,
2869        range: TextRange,
2870        await_expression: &AwaitExpression,
2871    ) -> Result<Register, LowerError> {
2872        let src = self.lower_expression(builder, &await_expression.argument)?;
2873        self.emit_suspend(range, src)
2874    }
2875
2876    /// `yield x` yields `x` and resumes with the `.next(v)` value in `dst`;
2877    /// `yield* x` delegates to the iterator of `x`.
2878    fn lower_yield(
2879        &mut self,
2880        builder: &mut ModuleBuilder,
2881        range: TextRange,
2882        yield_expression: &YieldExpression,
2883    ) -> Result<Register, LowerError> {
2884        if yield_expression.delegate {
2885            return self.lower_yield_delegate(builder, range, yield_expression);
2886        }
2887        let src = match &yield_expression.argument {
2888            Some(expression) => self.lower_expression(builder, expression)?,
2889            None => self.undefined(builder, range)?,
2890        };
2891        self.emit_suspend(range, src)
2892    }
2893
2894    /// `yield* x`: iterate `x`, yielding each produced value; the expression's
2895    /// value is the delegate iterator's completion value.
2896    fn lower_yield_delegate(
2897        &mut self,
2898        builder: &mut ModuleBuilder,
2899        range: TextRange,
2900        yield_expression: &YieldExpression,
2901    ) -> Result<Register, LowerError> {
2902        let subject = match &yield_expression.argument {
2903            Some(expression) => self.lower_expression(builder, expression)?,
2904            None => self.undefined(builder, range)?,
2905        };
2906        let iterator = self.alloc_register(range)?;
2907        self.emit(
2908            range,
2909            Instruction::GetIterator {
2910                dst: iterator,
2911                src: subject,
2912                kind: IteratorKind::Sync,
2913            },
2914        )?;
2915        let done = self.alloc_register(range)?;
2916        let value = self.alloc_register(range)?;
2917        let result = self.alloc_register(range)?;
2918        let undefined = self.undefined(builder, range)?;
2919        self.move_to(range, result, undefined)?;
2920        let head = self.next_pc();
2921        self.emit(
2922            range,
2923            Instruction::IteratorNext {
2924                done,
2925                value,
2926                iterator,
2927            },
2928        )?;
2929        let exit_jump = self.emit(
2930            range,
2931            Instruction::JumpIfTrue {
2932                condition: done,
2933                target: Pc::new(0),
2934            },
2935        )?;
2936        let resumed = self.emit_suspend(range, value)?;
2937        self.move_to(range, result, resumed)?;
2938        self.emit(range, Instruction::Jump { target: head })?;
2939        let exit = self.next_pc();
2940        self.patch_jump(exit_jump, exit);
2941        // On completion the delegate's final value is `value`.
2942        self.move_to(range, result, value)?;
2943        Ok(result)
2944    }
2945
2946    /// Emits a suspension yielding `src`, returning the register that receives
2947    /// the resumed value.
2948    fn emit_suspend(&mut self, range: TextRange, src: Register) -> Result<Register, LowerError> {
2949        let dst = self.alloc_register(range)?;
2950        let resume = Pc::new(self.code.len() as u32 + 1);
2951        self.emit(range, Instruction::Suspend { dst, src, resume })?;
2952        Ok(dst)
2953    }
2954
2955    // ------------------------------------------------------------------
2956    // Member access and calls
2957    // ------------------------------------------------------------------
2958
2959    /// Lowers a member expression, returning `(object, value)` where `object`
2960    /// is the base register (the receiver for a following call) and `value` is
2961    /// the read property.
2962    fn lower_member(
2963        &mut self,
2964        builder: &mut ModuleBuilder,
2965        range: TextRange,
2966        member: &MemberExpression,
2967    ) -> Result<(Register, Register), LowerError> {
2968        if member.optional {
2969            let value = self.lower_optional_chain(builder, range, member)?;
2970            // The receiver of an optional member read is the (possibly nullish)
2971            // base; a call through it re-evaluates, so return undefined here.
2972            let object = self.undefined(builder, range)?;
2973            return Ok((object, value));
2974        }
2975        let object = self.lower_expression(builder, &member.object)?;
2976        let key = self.member_key(builder, &member.property)?;
2977        let dst = self.alloc_register(range)?;
2978        self.emit(range, Instruction::GetProperty { dst, object, key })?;
2979        Ok((object, dst))
2980    }
2981
2982    /// Lowers an optional member/call chain node `a?.b` to a short-circuiting
2983    /// read whose result is `undefined` when the base is nullish.
2984    fn lower_optional_chain(
2985        &mut self,
2986        builder: &mut ModuleBuilder,
2987        range: TextRange,
2988        member: &MemberExpression,
2989    ) -> Result<Register, LowerError> {
2990        let result = self.alloc_register(range)?;
2991        let undefined = self.undefined(builder, range)?;
2992        self.move_to(range, result, undefined)?;
2993        let object = self.lower_expression(builder, &member.object)?;
2994        let is_nullish = self.compute_nullish(builder, range, object)?;
2995        let skip = self.emit(
2996            range,
2997            Instruction::JumpIfTrue {
2998                condition: is_nullish,
2999                target: Pc::new(0),
3000            },
3001        )?;
3002        let key = self.member_key(builder, &member.property)?;
3003        let value = self.alloc_register(range)?;
3004        self.emit(
3005            range,
3006            Instruction::GetProperty {
3007                dst: value,
3008                object,
3009                key,
3010            },
3011        )?;
3012        self.move_to(range, result, value)?;
3013        let end = self.next_pc();
3014        self.patch_jump(skip, end);
3015        Ok(result)
3016    }
3017
3018    fn lower_call(
3019        &mut self,
3020        builder: &mut ModuleBuilder,
3021        range: TextRange,
3022        call: &CallExpression,
3023    ) -> Result<Register, LowerError> {
3024        if let Expression::Member(member) = call.callee.data()
3025            && member.optional
3026        {
3027            return self.lower_optional_member_call(builder, range, call, member);
3028        }
3029        if call.optional {
3030            return self.lower_optional_call(builder, range, call);
3031        }
3032        let (callee, this_value) = self.lower_callee(builder, range, &call.callee)?;
3033        let arguments = self.build_arguments(builder, range, &call.arguments)?;
3034        let dst = self.alloc_register(range)?;
3035        self.emit(
3036            range,
3037            Instruction::Call {
3038                dst,
3039                callee,
3040                this_value,
3041                arguments,
3042            },
3043        )?;
3044        Ok(dst)
3045    }
3046
3047    fn lower_optional_member_call(
3048        &mut self,
3049        builder: &mut ModuleBuilder,
3050        range: TextRange,
3051        call: &CallExpression,
3052        member: &MemberExpression,
3053    ) -> Result<Register, LowerError> {
3054        let result = self.alloc_register(range)?;
3055        let undefined = self.undefined(builder, range)?;
3056        self.move_to(range, result, undefined)?;
3057
3058        let object = self.lower_expression(builder, &member.object)?;
3059        let object_is_nullish = self.compute_nullish(builder, range, object)?;
3060        let object_skip = self.emit(
3061            range,
3062            Instruction::JumpIfTrue {
3063                condition: object_is_nullish,
3064                target: Pc::new(0),
3065            },
3066        )?;
3067
3068        let key = self.member_key(builder, &member.property)?;
3069        let callee = self.alloc_register(range)?;
3070        self.emit(
3071            range,
3072            Instruction::GetProperty {
3073                dst: callee,
3074                object,
3075                key,
3076            },
3077        )?;
3078
3079        let callee_skip = if call.optional {
3080            let callee_is_nullish = self.compute_nullish(builder, range, callee)?;
3081            Some(self.emit(
3082                range,
3083                Instruction::JumpIfTrue {
3084                    condition: callee_is_nullish,
3085                    target: Pc::new(0),
3086                },
3087            )?)
3088        } else {
3089            None
3090        };
3091
3092        let arguments = self.build_arguments(builder, range, &call.arguments)?;
3093        let value = self.alloc_register(range)?;
3094        self.emit(
3095            range,
3096            Instruction::Call {
3097                dst: value,
3098                callee,
3099                this_value: object,
3100                arguments,
3101            },
3102        )?;
3103        self.move_to(range, result, value)?;
3104
3105        let end = self.next_pc();
3106        self.patch_jump(object_skip, end);
3107        if let Some(callee_skip) = callee_skip {
3108            self.patch_jump(callee_skip, end);
3109        }
3110        Ok(result)
3111    }
3112
3113    /// Evaluates a call's callee, returning `(callee, this_value)`. A member
3114    /// callee `obj.m()` uses `obj` as the receiver; a `super.m()` uses `this`.
3115    fn lower_callee(
3116        &mut self,
3117        builder: &mut ModuleBuilder,
3118        range: TextRange,
3119        callee: &Expr,
3120    ) -> Result<(Register, Register), LowerError> {
3121        match callee.data() {
3122            Expression::Member(member) if !member.optional => {
3123                if matches!(member.object.data(), Expression::Super) {
3124                    // `super.m()`: read from the prototype chain, call with `this`.
3125                    let this_value = self.this_value(range)?;
3126                    let object = this_value;
3127                    let key = self.member_key(builder, &member.property)?;
3128                    let value = self.alloc_register(range)?;
3129                    self.emit(
3130                        range,
3131                        Instruction::GetProperty {
3132                            dst: value,
3133                            object,
3134                            key,
3135                        },
3136                    )?;
3137                    return Ok((value, this_value));
3138                }
3139                let (object, value) = self.lower_member(builder, callee.range(), member)?;
3140                Ok((value, object))
3141            }
3142            Expression::Super => {
3143                // `super(...)`: invoke the parent constructor with the current
3144                // `this`. The parent constructor is read from the environment
3145                // binding created for the class's `extends` clause is not
3146                // available here, so route through the receiver's prototype
3147                // constructor via `this`.
3148                let this_value = self.this_value(range)?;
3149                Ok((this_value, this_value))
3150            }
3151            _ => {
3152                let callee = self.lower_expression(builder, callee)?;
3153                let this_value = self.undefined(builder, range)?;
3154                Ok((callee, this_value))
3155            }
3156        }
3157    }
3158
3159    fn lower_optional_call(
3160        &mut self,
3161        builder: &mut ModuleBuilder,
3162        range: TextRange,
3163        call: &CallExpression,
3164    ) -> Result<Register, LowerError> {
3165        let result = self.alloc_register(range)?;
3166        let undefined = self.undefined(builder, range)?;
3167        self.move_to(range, result, undefined)?;
3168        let (callee, this_value) = self.lower_callee(builder, range, &call.callee)?;
3169        let is_nullish = self.compute_nullish(builder, range, callee)?;
3170        let skip = self.emit(
3171            range,
3172            Instruction::JumpIfTrue {
3173                condition: is_nullish,
3174                target: Pc::new(0),
3175            },
3176        )?;
3177        let arguments = self.build_arguments(builder, range, &call.arguments)?;
3178        let value = self.alloc_register(range)?;
3179        self.emit(
3180            range,
3181            Instruction::Call {
3182                dst: value,
3183                callee,
3184                this_value,
3185                arguments,
3186            },
3187        )?;
3188        self.move_to(range, result, value)?;
3189        let end = self.next_pc();
3190        self.patch_jump(skip, end);
3191        Ok(result)
3192    }
3193
3194    fn lower_new(
3195        &mut self,
3196        builder: &mut ModuleBuilder,
3197        range: TextRange,
3198        new: &NewExpression,
3199    ) -> Result<Register, LowerError> {
3200        let callee = self.lower_expression(builder, &new.callee)?;
3201        let arguments = self.build_arguments(builder, range, &new.arguments)?;
3202        let dst = self.alloc_register(range)?;
3203        self.emit(
3204            range,
3205            Instruction::Construct {
3206                dst,
3207                callee,
3208                arguments,
3209            },
3210        )?;
3211        Ok(dst)
3212    }
3213
3214    /// Builds one dynamic arguments array from positional arguments and spreads.
3215    fn build_arguments(
3216        &mut self,
3217        builder: &mut ModuleBuilder,
3218        range: TextRange,
3219        arguments: &[CallArgument],
3220    ) -> Result<Register, LowerError> {
3221        let array = self.alloc_register(range)?;
3222        self.emit(range, Instruction::CreateArray { dst: array })?;
3223        for argument in arguments {
3224            match argument {
3225                CallArgument::Expression(expression) => {
3226                    let value = self.lower_expression(builder, expression)?;
3227                    self.emit(range, Instruction::ArrayPush { array, value })?;
3228                }
3229                CallArgument::Spread(spread) => {
3230                    let iterable = self.lower_expression(builder, &spread.argument)?;
3231                    self.emit(range, Instruction::ArrayExtend { array, iterable })?;
3232                }
3233                CallArgument::Missing(missing) => {
3234                    return Err(self.error(
3235                        zero_range(),
3236                        LowerErrorKind::MissingSyntax {
3237                            expected: missing.expected(),
3238                        },
3239                    ));
3240                }
3241            }
3242        }
3243        Ok(array)
3244    }
3245
3246    // ------------------------------------------------------------------
3247    // Arrays and objects
3248    // ------------------------------------------------------------------
3249
3250    fn lower_array(
3251        &mut self,
3252        builder: &mut ModuleBuilder,
3253        range: TextRange,
3254        array: &crate::syntax::ArrayLiteral,
3255    ) -> Result<Register, LowerError> {
3256        let dst = self.alloc_register(range)?;
3257        self.emit(range, Instruction::CreateArray { dst })?;
3258        for element in &array.elements {
3259            match element {
3260                ArrayElement::Expression(expression) => {
3261                    let value = self.lower_expression(builder, expression)?;
3262                    self.emit(range, Instruction::ArrayPush { array: dst, value })?;
3263                }
3264                ArrayElement::Spread(spread) => {
3265                    let iterable = self.lower_expression(builder, &spread.argument)?;
3266                    self.emit(
3267                        range,
3268                        Instruction::ArrayExtend {
3269                            array: dst,
3270                            iterable,
3271                        },
3272                    )?;
3273                }
3274                ArrayElement::Elision => {
3275                    let hole = self.undefined(builder, range)?;
3276                    self.emit(
3277                        range,
3278                        Instruction::ArrayPush {
3279                            array: dst,
3280                            value: hole,
3281                        },
3282                    )?;
3283                }
3284                ArrayElement::Missing(missing) => {
3285                    return Err(self.missing(range, missing.expected()));
3286                }
3287            }
3288        }
3289        Ok(dst)
3290    }
3291
3292    fn lower_object(
3293        &mut self,
3294        builder: &mut ModuleBuilder,
3295        range: TextRange,
3296        object: &ObjectLiteral,
3297    ) -> Result<Register, LowerError> {
3298        let dst = self.alloc_register(range)?;
3299        self.emit(range, Instruction::CreateObject { dst })?;
3300        for member in &object.members {
3301            match member.data() {
3302                ObjectMember::Property(property) => {
3303                    let key = self.property_key(builder, &property.name)?;
3304                    let value = self.lower_expression(builder, &property.value)?;
3305                    self.install_property(
3306                        builder,
3307                        member.range(),
3308                        dst,
3309                        key,
3310                        value,
3311                        property.modifier,
3312                    )?;
3313                }
3314                ObjectMember::Method(method) => {
3315                    let key = self.property_key(builder, &method.name)?;
3316                    let value =
3317                        self.build_function_value(builder, member.range(), None, &method.function)?;
3318                    self.install_property(
3319                        builder,
3320                        member.range(),
3321                        dst,
3322                        key,
3323                        value,
3324                        method.modifier,
3325                    )?;
3326                }
3327                ObjectMember::Spread(spread) => {
3328                    let source = self.lower_expression(builder, &spread.argument)?;
3329                    self.emit(
3330                        member.range(),
3331                        Instruction::ObjectSpread {
3332                            target: dst,
3333                            source,
3334                        },
3335                    )?;
3336                }
3337                ObjectMember::Missing(missing) => {
3338                    return Err(self.missing(member.range(), missing.expected()));
3339                }
3340            }
3341        }
3342        Ok(dst)
3343    }
3344
3345    /// Installs a data property, getter, or setter under `key` on `object`.
3346    fn install_property(
3347        &mut self,
3348        _builder: &mut ModuleBuilder,
3349        range: TextRange,
3350        object: Register,
3351        key: Register,
3352        value: Register,
3353        modifier: PropertyModifier,
3354    ) -> Result<(), LowerError> {
3355        match modifier {
3356            PropertyModifier::None => {
3357                self.emit(range, Instruction::SetProperty { object, key, value })?;
3358            }
3359            PropertyModifier::Get => {
3360                self.emit(
3361                    range,
3362                    Instruction::DefineAccessor {
3363                        object,
3364                        key,
3365                        accessor: value,
3366                        kind: AccessorKind::Getter,
3367                    },
3368                )?;
3369            }
3370            PropertyModifier::Set => {
3371                self.emit(
3372                    range,
3373                    Instruction::DefineAccessor {
3374                        object,
3375                        key,
3376                        accessor: value,
3377                        kind: AccessorKind::Setter,
3378                    },
3379                )?;
3380            }
3381        }
3382        Ok(())
3383    }
3384
3385    // ------------------------------------------------------------------
3386    // Literals, templates, regex
3387    // ------------------------------------------------------------------
3388
3389    fn lower_literal(
3390        &mut self,
3391        builder: &mut ModuleBuilder,
3392        range: TextRange,
3393        literal: &Literal,
3394    ) -> Result<Register, LowerError> {
3395        match literal {
3396            Literal::Number(number) => self.lower_numeric_literal(builder, number),
3397            Literal::String(string) => {
3398                let value = self.string_literal_value(string)?;
3399                self.string_reg(builder, value, range)
3400            }
3401            Literal::Boolean(boolean) => {
3402                let value = self.boolean_literal_value(boolean)?;
3403                self.load_constant(builder, Constant::Boolean(value), range)
3404            }
3405            Literal::Null(_) => self.load_constant(builder, Constant::Null, range),
3406            Literal::BigInt(_) => self.lower_bigint_literal(builder, range, literal),
3407            Literal::Regex(regex) => self.lower_regex_literal(builder, range, regex),
3408        }
3409    }
3410
3411    fn lower_template(
3412        &mut self,
3413        builder: &mut ModuleBuilder,
3414        range: TextRange,
3415        template: &TemplateLiteral,
3416    ) -> Result<Register, LowerError> {
3417        // `` `a${x}b` `` == "a" + x + "b": the leading string operand forces
3418        // the whole chain to string concatenation.
3419        let cooked = self.cooked_template_parts(template)?;
3420        let first = cooked.first().cloned().unwrap_or_default();
3421        let mut acc = self.string_reg(builder, first, range)?;
3422        for (index, expression) in template.expressions.iter().enumerate() {
3423            let value = self.lower_expression(builder, expression)?;
3424            let joined = self.alloc_register(range)?;
3425            self.emit(
3426                range,
3427                Instruction::Binary {
3428                    dst: joined,
3429                    op: BinaryOp::Add,
3430                    left: acc,
3431                    right: value,
3432                },
3433            )?;
3434            acc = joined;
3435            let chunk = cooked.get(index + 1).cloned().unwrap_or_default();
3436            let chunk_reg = self.string_reg(builder, chunk, range)?;
3437            let joined = self.alloc_register(range)?;
3438            self.emit(
3439                range,
3440                Instruction::Binary {
3441                    dst: joined,
3442                    op: BinaryOp::Add,
3443                    left: acc,
3444                    right: chunk_reg,
3445                },
3446            )?;
3447            acc = joined;
3448        }
3449        Ok(acc)
3450    }
3451
3452    /// `tag`...`` calls `tag` with a cooked strings array (carrying a `raw`
3453    /// property) followed by the substitution values.
3454    fn lower_tagged_template(
3455        &mut self,
3456        builder: &mut ModuleBuilder,
3457        range: TextRange,
3458        tagged: &crate::syntax::TaggedTemplateExpression,
3459    ) -> Result<Register, LowerError> {
3460        let (callee, this_value) = self.lower_callee(builder, range, &tagged.tag)?;
3461        let cooked = self.cooked_template_parts(&tagged.template)?;
3462        let raw = self.raw_template_parts(&tagged.template)?;
3463        // strings array (cooked) with a `.raw` array.
3464        let strings = self.alloc_register(range)?;
3465        self.emit(range, Instruction::CreateArray { dst: strings })?;
3466        for part in &cooked {
3467            let value = self.string_reg(builder, part.clone(), range)?;
3468            self.emit(
3469                range,
3470                Instruction::ArrayPush {
3471                    array: strings,
3472                    value,
3473                },
3474            )?;
3475        }
3476        let raw_array = self.alloc_register(range)?;
3477        self.emit(range, Instruction::CreateArray { dst: raw_array })?;
3478        for part in &raw {
3479            let value = self.string_reg(builder, part.clone(), range)?;
3480            self.emit(
3481                range,
3482                Instruction::ArrayPush {
3483                    array: raw_array,
3484                    value,
3485                },
3486            )?;
3487        }
3488        let raw_key = self.string_reg(builder, EcmaString::from_utf8("raw"), range)?;
3489        self.emit(
3490            range,
3491            Instruction::SetProperty {
3492                object: strings,
3493                key: raw_key,
3494                value: raw_array,
3495            },
3496        )?;
3497        let arguments = self.alloc_register(range)?;
3498        self.emit(range, Instruction::CreateArray { dst: arguments })?;
3499        self.emit(
3500            range,
3501            Instruction::ArrayPush {
3502                array: arguments,
3503                value: strings,
3504            },
3505        )?;
3506        for expression in &tagged.template.expressions {
3507            let value = self.lower_expression(builder, expression)?;
3508            self.emit(
3509                range,
3510                Instruction::ArrayPush {
3511                    array: arguments,
3512                    value,
3513                },
3514            )?;
3515        }
3516        let dst = self.alloc_register(range)?;
3517        self.emit(
3518            range,
3519            Instruction::Call {
3520                dst,
3521                callee,
3522                this_value,
3523                arguments,
3524            },
3525        )?;
3526        Ok(dst)
3527    }
3528
3529    fn cooked_template_parts(
3530        &self,
3531        template: &TemplateLiteral,
3532    ) -> Result<Vec<EcmaString>, LowerError> {
3533        template
3534            .elements
3535            .iter()
3536            .map(|element| self.template_element_text(element, true))
3537            .collect()
3538    }
3539
3540    fn raw_template_parts(
3541        &self,
3542        template: &TemplateLiteral,
3543    ) -> Result<Vec<EcmaString>, LowerError> {
3544        template
3545            .elements
3546            .iter()
3547            .map(|element| self.template_element_text(element, false))
3548            .collect()
3549    }
3550
3551    /// Extracts the interior text of a template element, cooking escapes when
3552    /// `cook` is set.
3553    fn template_element_text(
3554        &self,
3555        element: &TemplateElementNode,
3556        cook: bool,
3557    ) -> Result<EcmaString, LowerError> {
3558        let token = element.data().token();
3559        if token.is_missing() {
3560            return Ok(EcmaString::default());
3561        }
3562        let Some(text) = self.file.token_text(token) else {
3563            return Ok(EcmaString::default());
3564        };
3565        let interior = trim_template_delimiters(text, token.kind());
3566        if cook {
3567            Ok(cook_escapes(interior))
3568        } else {
3569            Ok(EcmaString::from_utf8(interior))
3570        }
3571    }
3572
3573    fn lower_regex_literal(
3574        &mut self,
3575        builder: &mut ModuleBuilder,
3576        range: TextRange,
3577        regex: &RegexLiteralNode,
3578    ) -> Result<Register, LowerError> {
3579        let token = regex.data().token();
3580        if token.is_missing() {
3581            return Err(self.missing(range, NodeKind::RegexLiteral));
3582        }
3583        let lexeme = self
3584            .file
3585            .token_text(token)
3586            .ok_or_else(|| self.error(range, LowerErrorKind::InvalidRegexLiteral))?;
3587        let (pattern, flags) = split_regex(lexeme)
3588            .ok_or_else(|| self.error(range, LowerErrorKind::InvalidRegexLiteral))?;
3589        let pattern_id =
3590            builder.intern(Constant::String(EcmaString::from_utf8(&pattern)), range)?;
3591        let flags_id = builder.intern(Constant::String(EcmaString::from_utf8(&flags)), range)?;
3592        let dst = self.alloc_register(range)?;
3593        self.emit(
3594            range,
3595            Instruction::CreateRegExp {
3596                dst,
3597                pattern: pattern_id,
3598                flags: flags_id,
3599            },
3600        )?;
3601        Ok(dst)
3602    }
3603
3604    fn lower_bigint_literal(
3605        &mut self,
3606        builder: &mut ModuleBuilder,
3607        range: TextRange,
3608        literal: &Literal,
3609    ) -> Result<Register, LowerError> {
3610        let Literal::BigInt(node) = literal else {
3611            unreachable!("lower_bigint_literal only handles bigint literals");
3612        };
3613        let token = node.data().token();
3614        if token.is_missing() {
3615            return Err(self.missing(range, NodeKind::BigIntLiteral));
3616        }
3617        let lexeme = self
3618            .file
3619            .token_text(token)
3620            .ok_or_else(|| self.error(range, LowerErrorKind::InvalidBigIntLiteral))?;
3621        let canonical = canonical_bigint_text(lexeme)
3622            .ok_or_else(|| self.unsupported(range, UnsupportedConstruct::NonDecimalBigInt))?;
3623        let value = BigIntLiteral::new(canonical)
3624            .ok_or_else(|| self.error(range, LowerErrorKind::InvalidBigIntLiteral))?;
3625        self.load_constant(builder, Constant::BigInt(value), range)
3626    }
3627
3628    fn lower_numeric_literal(
3629        &mut self,
3630        builder: &mut ModuleBuilder,
3631        number: &NumericLiteralNode,
3632    ) -> Result<Register, LowerError> {
3633        let range = number.range();
3634        let token = number.data().token();
3635        if token.is_missing() {
3636            return Err(self.missing(range, NodeKind::NumericLiteral));
3637        }
3638        let lexeme = self
3639            .file
3640            .token_text(token)
3641            .filter(|text| !text.is_empty())
3642            .ok_or_else(|| self.error(range, LowerErrorKind::InvalidNumericLiteral))?;
3643        let value = cook_number(lexeme)
3644            .ok_or_else(|| self.error(range, LowerErrorKind::InvalidNumericLiteral))?;
3645        self.load_constant(builder, number_constant(value), range)
3646    }
3647
3648    fn string_literal_value(&self, string: &StringLiteralNode) -> Result<EcmaString, LowerError> {
3649        let range = string.range();
3650        let token = string.data().token();
3651        let missing = || self.missing(range, NodeKind::StringLiteral);
3652        if token.is_missing() {
3653            return Err(missing());
3654        }
3655        let text = self.file.token_text(token).ok_or_else(missing)?;
3656        if text.len() < 2 {
3657            return Err(missing());
3658        }
3659        let interior = &text[1..text.len() - 1];
3660        Ok(cook_escapes(interior))
3661    }
3662
3663    fn boolean_literal_value(&self, boolean: &BooleanLiteralNode) -> Result<bool, LowerError> {
3664        let token = boolean.data().token();
3665        match token.kind() {
3666            TokenKind::KwTrue if !token.is_missing() => Ok(true),
3667            TokenKind::KwFalse if !token.is_missing() => Ok(false),
3668            _ => Err(self.missing(boolean.range(), NodeKind::BooleanLiteral)),
3669        }
3670    }
3671
3672    // ------------------------------------------------------------------
3673    // Property keys
3674    // ------------------------------------------------------------------
3675
3676    /// A member-access key as a register (string, computed value, or private
3677    /// name binding).
3678    fn member_key(
3679        &mut self,
3680        builder: &mut ModuleBuilder,
3681        property: &MemberProperty,
3682    ) -> Result<Register, LowerError> {
3683        match property {
3684            MemberProperty::Named(identifier) => {
3685                let name = self.identifier_text(identifier)?;
3686                self.string_reg(builder, EcmaString::from_utf8(&name), identifier.range())
3687            }
3688            MemberProperty::Computed(expression) => self.lower_expression(builder, expression),
3689            MemberProperty::Private(private) => {
3690                let name = self.private_text(private)?;
3691                self.read_name(builder, &name, private.range())
3692            }
3693        }
3694    }
3695
3696    /// An object-literal / class-member property key as a register.
3697    fn property_key(
3698        &mut self,
3699        builder: &mut ModuleBuilder,
3700        name: &PropertyName,
3701    ) -> Result<Register, LowerError> {
3702        match name {
3703            PropertyName::Identifier(identifier) => {
3704                let text = self.identifier_text(identifier)?;
3705                self.string_reg(builder, EcmaString::from_utf8(&text), identifier.range())
3706            }
3707            PropertyName::String(string) => {
3708                let value = self.string_literal_value(string)?;
3709                self.string_reg(builder, value, string.range())
3710            }
3711            PropertyName::Number(number) => {
3712                let key = numeric_key_text(self, number)?;
3713                self.string_reg(builder, EcmaString::from_utf8(&key), number.range())
3714            }
3715            PropertyName::Computed(expression) => self.lower_expression(builder, expression),
3716            PropertyName::Private(private) => {
3717                let name = self.private_text(private)?;
3718                self.read_name(builder, &name, private.range())
3719            }
3720            PropertyName::Missing(missing) => Err(self.error(
3721                zero_range(),
3722                LowerErrorKind::MissingSyntax {
3723                    expected: missing.expected(),
3724                },
3725            )),
3726        }
3727    }
3728
3729    // ------------------------------------------------------------------
3730    // Modules
3731    // ------------------------------------------------------------------
3732
3733    fn lower_import(
3734        &mut self,
3735        builder: &mut ModuleBuilder,
3736        range: TextRange,
3737        import: &ImportDeclaration,
3738    ) -> Result<(), LowerError> {
3739        if self.goal == LoweringGoal::ClassicScript {
3740            return Err(self.unsupported(range, UnsupportedConstruct::ImportDeclarationInScript));
3741        }
3742        if import.type_only || self.goal == LoweringGoal::ProgramModule {
3743            return Ok(());
3744        }
3745        let specifier = self.string_literal_value(&import.source)?;
3746        let specifier_id = builder.intern(Constant::String(specifier), range)?;
3747        let module = self.alloc_register(range)?;
3748        self.emit(
3749            range,
3750            Instruction::Import {
3751                dst: module,
3752                specifier: specifier_id,
3753            },
3754        )?;
3755        let Some(clause) = &import.clause else {
3756            return Ok(());
3757        };
3758        if let Some(default) = &clause.default {
3759            let name = self.identifier_text(default)?;
3760            let value = self.get_named(builder, range, module, "default")?;
3761            self.store_binding(
3762                builder,
3763                &name,
3764                value,
3765                range,
3766                binding_site(default.range()),
3767                DeclarationScope::Function,
3768            )?;
3769        }
3770        match &clause.binding {
3771            Some(ImportBinding::Namespace(identifier)) => {
3772                let name = self.identifier_text(identifier)?;
3773                self.store_binding(
3774                    builder,
3775                    &name,
3776                    module,
3777                    range,
3778                    binding_site(identifier.range()),
3779                    DeclarationScope::Function,
3780                )?;
3781            }
3782            Some(ImportBinding::Named(specifiers)) => {
3783                for specifier in specifiers {
3784                    let data = specifier.data();
3785                    if matches!(data.mode, ImportSpecifierMode::TypeOnly) {
3786                        continue;
3787                    }
3788                    let local = self.identifier_text(&data.local)?;
3789                    let imported = self.module_export_name(&data.imported)?;
3790                    let value = self.get_named(builder, range, module, &imported)?;
3791                    self.store_binding(
3792                        builder,
3793                        &local,
3794                        value,
3795                        range,
3796                        binding_site(data.local.range()),
3797                        DeclarationScope::Function,
3798                    )?;
3799                }
3800            }
3801            None => {}
3802        }
3803        Ok(())
3804    }
3805
3806    fn lower_import_expression(
3807        &mut self,
3808        builder: &mut ModuleBuilder,
3809        range: TextRange,
3810        import: &crate::syntax::ImportExpression,
3811    ) -> Result<Register, LowerError> {
3812        if self.goal == LoweringGoal::ClassicScript {
3813            return Err(self.unsupported(range, UnsupportedConstruct::DynamicImportInScript));
3814        }
3815        if let Expression::Literal(Literal::String(string)) = import.source.data() {
3816            let specifier = self.string_literal_value(string)?;
3817            let specifier_id = builder.intern(Constant::String(specifier), range)?;
3818            let dst = self.alloc_register(range)?;
3819            self.emit(
3820                range,
3821                Instruction::Import {
3822                    dst,
3823                    specifier: specifier_id,
3824                },
3825            )?;
3826            Ok(dst)
3827        } else {
3828            Err(self.unsupported(range, UnsupportedConstruct::DynamicImportExpression))
3829        }
3830    }
3831
3832    fn get_named(
3833        &mut self,
3834        builder: &mut ModuleBuilder,
3835        range: TextRange,
3836        object: Register,
3837        name: &str,
3838    ) -> Result<Register, LowerError> {
3839        let key = self.string_reg(builder, EcmaString::from_utf8(name), range)?;
3840        let dst = self.alloc_register(range)?;
3841        self.emit(range, Instruction::GetProperty { dst, object, key })?;
3842        Ok(dst)
3843    }
3844
3845    /// Emits a runtime export of the local binding `name` under `exported`.
3846    fn export_binding(
3847        &mut self,
3848        builder: &mut ModuleBuilder,
3849        range: TextRange,
3850        local: &str,
3851        exported: &str,
3852    ) -> Result<(), LowerError> {
3853        if self.goal == LoweringGoal::ProgramModule {
3854            return Ok(());
3855        }
3856        let src = self.read_name(builder, local, range)?;
3857        let name = builder.intern(Constant::String(EcmaString::from_utf8(exported)), range)?;
3858        self.emit(range, Instruction::Export { name, src })?;
3859        Ok(())
3860    }
3861
3862    fn export_value(
3863        &mut self,
3864        builder: &mut ModuleBuilder,
3865        range: TextRange,
3866        exported: &str,
3867        src: Register,
3868    ) -> Result<(), LowerError> {
3869        if self.goal == LoweringGoal::ProgramModule {
3870            debug_assert_eq!(exported, "default");
3871            return self.store_binding(
3872                builder,
3873                "*default*",
3874                src,
3875                range,
3876                binding_site(range),
3877                DeclarationScope::Lexical,
3878            );
3879        }
3880        let name = builder.intern(Constant::String(EcmaString::from_utf8(exported)), range)?;
3881        self.emit(range, Instruction::Export { name, src })?;
3882        Ok(())
3883    }
3884
3885    fn lower_export(
3886        &mut self,
3887        builder: &mut ModuleBuilder,
3888        range: TextRange,
3889        export: &ExportDeclaration,
3890    ) -> Result<(), LowerError> {
3891        if self.goal == LoweringGoal::ClassicScript {
3892            return Err(self.unsupported(range, UnsupportedConstruct::ExportDeclarationInScript));
3893        }
3894
3895        match export {
3896            ExportDeclaration::Named(ExportNamedDeclaration::Declaration(statement)) => {
3897                self.lower_statement(builder, statement)?;
3898                for name in declared_names(self.file, statement) {
3899                    self.export_binding(builder, range, &name, &name)?;
3900                }
3901                Ok(())
3902            }
3903            ExportDeclaration::Named(ExportNamedDeclaration::Specifiers {
3904                type_only,
3905                specifiers,
3906                source,
3907                ..
3908            }) => {
3909                if *type_only || self.goal == LoweringGoal::ProgramModule {
3910                    return Ok(());
3911                }
3912                if let Some(source) = source {
3913                    // Re-export from another module.
3914                    let specifier = self.string_literal_value(source)?;
3915                    let specifier_id = builder.intern(Constant::String(specifier), range)?;
3916                    let module = self.alloc_register(range)?;
3917                    self.emit(
3918                        range,
3919                        Instruction::Import {
3920                            dst: module,
3921                            specifier: specifier_id,
3922                        },
3923                    )?;
3924                    for specifier in specifiers {
3925                        let data = specifier.data();
3926                        if matches!(data.mode, ExportSpecifierMode::TypeOnly) {
3927                            continue;
3928                        }
3929                        let local = self.module_export_name(&data.local)?;
3930                        let exported = self.module_export_name(&data.exported)?;
3931                        let value = self.get_named(builder, range, module, &local)?;
3932                        self.export_value(builder, range, &exported, value)?;
3933                    }
3934                    return Ok(());
3935                }
3936                for specifier in specifiers {
3937                    let data = specifier.data();
3938                    if matches!(data.mode, ExportSpecifierMode::TypeOnly) {
3939                        continue;
3940                    }
3941                    let local = self.module_export_name(&data.local)?;
3942                    let exported = self.module_export_name(&data.exported)?;
3943                    self.export_binding(builder, range, &local, &exported)?;
3944                }
3945                Ok(())
3946            }
3947            ExportDeclaration::All(all) => {
3948                if all.type_only || self.goal == LoweringGoal::ProgramModule {
3949                    Ok(())
3950                } else {
3951                    Err(self.unsupported(range, UnsupportedConstruct::RuntimeExportAll))
3952                }
3953            }
3954            ExportDeclaration::Default(default) => match &default.value {
3955                ExportDefaultValue::Expression(expression) => {
3956                    let value = self.lower_expression(builder, expression)?;
3957                    self.export_value(builder, range, "default", value)
3958                }
3959                ExportDefaultValue::Function(function) => {
3960                    if function.body.is_none() {
3961                        return Ok(());
3962                    }
3963                    if let Some(identifier) = &function.name {
3964                        let name = self.identifier_text(identifier)?;
3965                        let closure = self.read_name(builder, &name, range)?;
3966                        self.export_value(builder, range, "default", closure)
3967                    } else {
3968                        let closure = self
3969                            .build_constructible_function_value(builder, range, None, function)?;
3970                        self.export_value(builder, range, "default", closure)
3971                    }
3972                }
3973                ExportDefaultValue::Class(class) => {
3974                    let value = self.lower_class_value(builder, range, class, None, None)?;
3975                    if let Some(identifier) = &class.name {
3976                        let name = self.identifier_text(identifier)?;
3977                        self.store_binding(
3978                            builder,
3979                            &name,
3980                            value,
3981                            range,
3982                            binding_site(identifier.range()),
3983                            DeclarationScope::Lexical,
3984                        )?;
3985                    }
3986                    self.export_value(builder, range, "default", value)
3987                }
3988                ExportDefaultValue::Missing(missing) => {
3989                    Err(self.missing(range, missing.expected()))
3990                }
3991            },
3992            ExportDeclaration::Assignment(_) => {
3993                Err(self.unsupported(range, UnsupportedConstruct::ExportAssignment))
3994            }
3995        }
3996    }
3997
3998    fn module_export_name(&self, name: &ModuleExportName) -> Result<String, LowerError> {
3999        match name {
4000            ModuleExportName::Identifier(identifier) => self.identifier_text(identifier),
4001            ModuleExportName::String(string) => self
4002                .string_literal_value(string)?
4003                .to_utf8_strict()
4004                .map_err(|_| self.error(string.range(), LowerErrorKind::IllFormedMetadataString)),
4005            ModuleExportName::Missing(missing) => Err(self.error(
4006                zero_range(),
4007                LowerErrorKind::MissingSyntax {
4008                    expected: missing.expected(),
4009                },
4010            )),
4011        }
4012    }
4013
4014    // ------------------------------------------------------------------
4015    // Destructuring (binding and assignment)
4016    // ------------------------------------------------------------------
4017
4018    /// Binds a binding pattern to `value`, declaring each identifier binding.
4019    fn bind_pattern(
4020        &mut self,
4021        builder: &mut ModuleBuilder,
4022        pattern: &Pattern,
4023        value: Register,
4024        declaration_scope: DeclarationScope,
4025    ) -> Result<(), LowerError> {
4026        let range = pattern.range();
4027        match pattern.data() {
4028            BindingPattern::Identifier(identifier) => {
4029                let name = self.identifier_text(identifier)?;
4030                self.store_binding(
4031                    builder,
4032                    &name,
4033                    value,
4034                    range,
4035                    binding_site(identifier.range()),
4036                    declaration_scope,
4037                )
4038            }
4039            BindingPattern::Object(object) => {
4040                let mut taken: Vec<Register> = Vec::new();
4041                for property in &object.properties {
4042                    if let BindingPattern::Rest(rest) = property.binding.data() {
4043                        let rest_value = self.rest_object(builder, range, value, &taken)?;
4044                        self.bind_pattern(builder, &rest.argument, rest_value, declaration_scope)?;
4045                        continue;
4046                    }
4047                    let key = self.property_key(builder, &property.name)?;
4048                    taken.push(key);
4049                    let element = self.alloc_register(range)?;
4050                    self.emit(
4051                        range,
4052                        Instruction::GetProperty {
4053                            dst: element,
4054                            object: value,
4055                            key,
4056                        },
4057                    )?;
4058                    let element = match &property.initializer {
4059                        Some(default) => self.apply_default(builder, range, element, default)?,
4060                        None => element,
4061                    };
4062                    self.bind_pattern(builder, &property.binding, element, declaration_scope)?;
4063                }
4064                Ok(())
4065            }
4066            BindingPattern::Array(array) => {
4067                let iterator = self.alloc_register(range)?;
4068                self.emit(
4069                    range,
4070                    Instruction::GetIterator {
4071                        dst: iterator,
4072                        src: value,
4073                        kind: IteratorKind::Sync,
4074                    },
4075                )?;
4076                for element in &array.elements {
4077                    match element {
4078                        ArrayBindingElement::Elision => {
4079                            self.iterator_step_discard(range, iterator)?;
4080                        }
4081                        ArrayBindingElement::Binding(inner) => {
4082                            if let BindingPattern::Rest(rest) = inner.data() {
4083                                let rest_value = self.rest_array(builder, range, iterator)?;
4084                                self.bind_pattern(
4085                                    builder,
4086                                    &rest.argument,
4087                                    rest_value,
4088                                    declaration_scope,
4089                                )?;
4090                            } else {
4091                                let (element_value, default) = self.destructure_element(inner);
4092                                let value = self.iterator_step_value(builder, range, iterator)?;
4093                                let value = match default {
4094                                    Some(default) => {
4095                                        self.apply_default(builder, range, value, default)?
4096                                    }
4097                                    None => value,
4098                                };
4099                                let _ = element_value;
4100                                self.bind_pattern(builder, inner, value, declaration_scope)?;
4101                            }
4102                        }
4103                        ArrayBindingElement::Missing(missing) => {
4104                            return Err(self.missing(range, missing.expected()));
4105                        }
4106                    }
4107                }
4108                Ok(())
4109            }
4110            BindingPattern::Assignment(assignment) => {
4111                let value = self.apply_default(builder, range, value, &assignment.right)?;
4112                self.bind_pattern(builder, &assignment.left, value, declaration_scope)
4113            }
4114            BindingPattern::Rest(rest) => {
4115                // A bare rest at the top level binds the whole value.
4116                self.bind_pattern(builder, &rest.argument, value, declaration_scope)
4117            }
4118            BindingPattern::Missing(missing) => Err(self.missing(range, missing.expected())),
4119        }
4120    }
4121
4122    /// Splits an assignment binding element into (inner pattern, default).
4123    fn destructure_element<'p>(&self, pattern: &'p Pattern) -> (&'p Pattern, Option<&'p Expr>) {
4124        if let BindingPattern::Assignment(assignment) = pattern.data() {
4125            (&assignment.left, Some(&assignment.right))
4126        } else {
4127            (pattern, None)
4128        }
4129    }
4130
4131    /// `value === undefined ? default : value`.
4132    fn apply_default(
4133        &mut self,
4134        builder: &mut ModuleBuilder,
4135        range: TextRange,
4136        value: Register,
4137        default: &Expr,
4138    ) -> Result<Register, LowerError> {
4139        let result = self.alloc_register(range)?;
4140        self.move_to(range, result, value)?;
4141        let undefined = self.undefined(builder, range)?;
4142        let is_undefined = self.alloc_register(range)?;
4143        self.emit(
4144            range,
4145            Instruction::Binary {
4146                dst: is_undefined,
4147                op: BinaryOp::StrictEqual,
4148                left: value,
4149                right: undefined,
4150            },
4151        )?;
4152        let skip = self.emit(
4153            range,
4154            Instruction::JumpIfFalse {
4155                condition: is_undefined,
4156                target: Pc::new(0),
4157            },
4158        )?;
4159        let default_value = self.lower_expression(builder, default)?;
4160        self.move_to(range, result, default_value)?;
4161        let end = self.next_pc();
4162        self.patch_jump(skip, end);
4163        Ok(result)
4164    }
4165
4166    /// Steps an iterator once, discarding the produced value.
4167    fn iterator_step_discard(
4168        &mut self,
4169        range: TextRange,
4170        iterator: Register,
4171    ) -> Result<(), LowerError> {
4172        let done = self.alloc_register(range)?;
4173        let value = self.alloc_register(range)?;
4174        self.emit(
4175            range,
4176            Instruction::IteratorNext {
4177                done,
4178                value,
4179                iterator,
4180            },
4181        )?;
4182        Ok(())
4183    }
4184
4185    /// Steps an iterator, returning a register that holds the produced value
4186    /// (or `undefined` when the iterator is exhausted).
4187    fn iterator_step_value(
4188        &mut self,
4189        builder: &mut ModuleBuilder,
4190        range: TextRange,
4191        iterator: Register,
4192    ) -> Result<Register, LowerError> {
4193        let done = self.alloc_register(range)?;
4194        let value = self.alloc_register(range)?;
4195        let result = self.alloc_register(range)?;
4196        let undefined = self.undefined(builder, range)?;
4197        self.move_to(range, result, undefined)?;
4198        self.emit(
4199            range,
4200            Instruction::IteratorNext {
4201                done,
4202                value,
4203                iterator,
4204            },
4205        )?;
4206        let skip = self.emit(
4207            range,
4208            Instruction::JumpIfTrue {
4209                condition: done,
4210                target: Pc::new(0),
4211            },
4212        )?;
4213        self.move_to(range, result, value)?;
4214        let end = self.next_pc();
4215        self.patch_jump(skip, end);
4216        Ok(result)
4217    }
4218
4219    /// Collects the remaining iterator elements into a fresh array.
4220    fn rest_array(
4221        &mut self,
4222        _builder: &mut ModuleBuilder,
4223        range: TextRange,
4224        iterator: Register,
4225    ) -> Result<Register, LowerError> {
4226        let array = self.alloc_register(range)?;
4227        self.emit(range, Instruction::CreateArray { dst: array })?;
4228        let done = self.alloc_register(range)?;
4229        let value = self.alloc_register(range)?;
4230        let head = self.next_pc();
4231        self.emit(
4232            range,
4233            Instruction::IteratorNext {
4234                done,
4235                value,
4236                iterator,
4237            },
4238        )?;
4239        let exit = self.emit(
4240            range,
4241            Instruction::JumpIfTrue {
4242                condition: done,
4243                target: Pc::new(0),
4244            },
4245        )?;
4246        self.emit(range, Instruction::ArrayPush { array, value })?;
4247        self.emit(range, Instruction::Jump { target: head })?;
4248        let exit_pc = self.next_pc();
4249        self.patch_jump(exit, exit_pc);
4250        Ok(array)
4251    }
4252
4253    /// Builds `{ ...object }` minus the already-taken keys, for object rest.
4254    fn rest_object(
4255        &mut self,
4256        _builder: &mut ModuleBuilder,
4257        range: TextRange,
4258        object: Register,
4259        taken: &[Register],
4260    ) -> Result<Register, LowerError> {
4261        let rest = self.alloc_register(range)?;
4262        self.emit(range, Instruction::CreateObject { dst: rest })?;
4263        self.emit(
4264            range,
4265            Instruction::ObjectSpread {
4266                target: rest,
4267                source: object,
4268            },
4269        )?;
4270        for key in taken {
4271            let discarded = self.alloc_register(range)?;
4272            self.emit(
4273                range,
4274                Instruction::DeleteProperty {
4275                    dst: discarded,
4276                    object: rest,
4277                    key: *key,
4278                },
4279            )?;
4280        }
4281        Ok(rest)
4282    }
4283
4284    /// Assigns `value` into an existing assignment target (identifier, member,
4285    /// or nested destructuring pattern).
4286    fn assign_target(
4287        &mut self,
4288        builder: &mut ModuleBuilder,
4289        target: &AssignmentTargetNode,
4290        value: Register,
4291    ) -> Result<(), LowerError> {
4292        let range = target.range();
4293        match target.data() {
4294            AssignmentTarget::Identifier(identifier) => {
4295                let name = self.identifier_text(identifier)?;
4296                self.assign_name(builder, &name, value, range)
4297            }
4298            AssignmentTarget::Member(member) => {
4299                let object = self.lower_expression(builder, &member.object)?;
4300                let key = self.member_key(builder, &member.property)?;
4301                self.emit(range, Instruction::SetProperty { object, key, value })?;
4302                Ok(())
4303            }
4304            AssignmentTarget::Object(object) => {
4305                let mut taken: Vec<Register> = Vec::new();
4306                for property in &object.properties {
4307                    self.assign_object_property(builder, range, value, property, &mut taken)?;
4308                }
4309                Ok(())
4310            }
4311            AssignmentTarget::Array(array) => {
4312                let iterator = self.alloc_register(range)?;
4313                self.emit(
4314                    range,
4315                    Instruction::GetIterator {
4316                        dst: iterator,
4317                        src: value,
4318                        kind: IteratorKind::Sync,
4319                    },
4320                )?;
4321                for element in &array.elements {
4322                    match element {
4323                        AssignmentArrayElement::Elision => {
4324                            self.iterator_step_discard(range, iterator)?;
4325                        }
4326                        AssignmentArrayElement::Target(inner) => {
4327                            let element = self.iterator_step_value(builder, range, iterator)?;
4328                            self.assign_target(builder, inner, element)?;
4329                        }
4330                        AssignmentArrayElement::Missing(missing) => {
4331                            return Err(self.missing(range, missing.expected()));
4332                        }
4333                    }
4334                }
4335                Ok(())
4336            }
4337            AssignmentTarget::Missing(missing) => Err(self.missing(range, missing.expected())),
4338        }
4339    }
4340
4341    fn assign_object_property(
4342        &mut self,
4343        builder: &mut ModuleBuilder,
4344        range: TextRange,
4345        source: Register,
4346        property: &AssignmentObjectProperty,
4347        taken: &mut Vec<Register>,
4348    ) -> Result<(), LowerError> {
4349        let key = self.property_key(builder, &property.name)?;
4350        taken.push(key);
4351        let element = self.alloc_register(range)?;
4352        self.emit(
4353            range,
4354            Instruction::GetProperty {
4355                dst: element,
4356                object: source,
4357                key,
4358            },
4359        )?;
4360        let element = match &property.initializer {
4361            Some(default) => self.apply_default(builder, range, element, default)?,
4362            None => element,
4363        };
4364        self.assign_target(builder, &property.target, element)
4365    }
4366
4367    // ------------------------------------------------------------------
4368    // Functions and closures
4369    // ------------------------------------------------------------------
4370
4371    fn lower_arrow(
4372        &mut self,
4373        builder: &mut ModuleBuilder,
4374        range: TextRange,
4375        arrow: &ArrowFunction,
4376    ) -> Result<Register, LowerError> {
4377        let flags = FunctionFlags {
4378            is_async: arrow.is_async,
4379            is_generator: false,
4380        };
4381        let captures =
4382            self.compute_captures(&arrow.parameters, ArrowBody::Arrow(&arrow.body), true);
4383        let id = builder.reserve_function(range)?;
4384        self.build_function_into(
4385            builder,
4386            id,
4387            range,
4388            None,
4389            &arrow.parameters,
4390            ArrowBody::Arrow(&arrow.body),
4391            flags,
4392            &captures,
4393            true,
4394        )?;
4395        self.materialize_closure(builder, range, id, &captures)
4396    }
4397
4398    fn build_constructible_function_value(
4399        &mut self,
4400        builder: &mut ModuleBuilder,
4401        range: TextRange,
4402        name: Option<String>,
4403        function: &FunctionLike,
4404    ) -> Result<Register, LowerError> {
4405        let closure = self.build_function_value(builder, range, name, function)?;
4406        if !function.is_async && !function.is_generator {
4407            let prototype = self.alloc_register(range)?;
4408            self.emit(range, Instruction::CreateObject { dst: prototype })?;
4409            let constructor_key =
4410                self.string_reg(builder, EcmaString::from_utf8("constructor"), range)?;
4411            self.emit(
4412                range,
4413                Instruction::SetProperty {
4414                    object: prototype,
4415                    key: constructor_key,
4416                    value: closure,
4417                },
4418            )?;
4419            let prototype_key =
4420                self.string_reg(builder, EcmaString::from_utf8("prototype"), range)?;
4421            self.emit(
4422                range,
4423                Instruction::SetProperty {
4424                    object: closure,
4425                    key: prototype_key,
4426                    value: prototype,
4427                },
4428            )?;
4429        }
4430        Ok(closure)
4431    }
4432
4433    /// Builds a function/method value (a closure) from a [`FunctionLike`].
4434    fn build_function_value(
4435        &mut self,
4436        builder: &mut ModuleBuilder,
4437        range: TextRange,
4438        name: Option<String>,
4439        function: &FunctionLike,
4440    ) -> Result<Register, LowerError> {
4441        if let Some(decorator) = function.decorators.first() {
4442            return Err(self.unsupported(
4443                decorator.range(),
4444                UnsupportedConstruct::DecoratedDeclaration,
4445            ));
4446        }
4447        let flags = FunctionFlags {
4448            is_async: function.is_async,
4449            is_generator: function.is_generator,
4450        };
4451        let body = function
4452            .body
4453            .as_ref()
4454            .ok_or_else(|| self.missing(range, NodeKind::BlockStatement))?;
4455        let captures =
4456            self.compute_captures(&function.parameters, ArrowBody::Function(body), false);
4457        let id = builder.reserve_function(range)?;
4458        self.build_function_into(
4459            builder,
4460            id,
4461            range,
4462            name,
4463            &function.parameters,
4464            ArrowBody::Function(body),
4465            flags,
4466            &captures,
4467            false,
4468        )?;
4469        self.materialize_closure(builder, range, id, &captures)
4470    }
4471
4472    /// Lowers a function body into a reserved module function slot.
4473    #[allow(clippy::too_many_arguments)]
4474    fn build_function_into(
4475        &mut self,
4476        builder: &mut ModuleBuilder,
4477        id: FunctionId,
4478        range: TextRange,
4479        name: Option<String>,
4480        parameters: &[ParameterNode],
4481        body: ArrowBody<'_>,
4482        flags: FunctionFlags,
4483        captures: &[CaptureKey],
4484        is_arrow: bool,
4485    ) -> Result<(), LowerError> {
4486        let capture_plan = CapturePlan::for_function(self.file, parameters, body);
4487        let mut inner = FunctionContext {
4488            file: self.file,
4489            code: Vec::new(),
4490            registers: 0,
4491            capture_count: 0,
4492            parameter_count: 0,
4493            scopes: vec![HashMap::new()],
4494            predeclared_cells: HashMap::new(),
4495            capture_plan,
4496            loops: Vec::new(),
4497            handlers: Vec::new(),
4498            finally_stack: Vec::new(),
4499            top_level: false,
4500            goal: self.goal,
4501            completion: None,
4502            completion_pool: Vec::new(),
4503            completion_depth: 0,
4504            this_capture: None,
4505            new_target_capture: None,
4506            parent_constructor_capture: None,
4507            arguments_source: if is_arrow {
4508                ArgumentsSource::None
4509            } else {
4510                ArgumentsSource::Own
4511            },
4512        };
4513        // Leading capture registers.
4514        for capture in captures {
4515            let register = inner.alloc_register(range)?;
4516            inner.capture_count += 1;
4517            match capture {
4518                CaptureKey::Name(name) => {
4519                    inner.declare(
4520                        name.clone(),
4521                        Binding::Cell(register),
4522                        DeclarationScope::Function,
4523                    );
4524                }
4525                CaptureKey::This => inner.this_capture = Some(register),
4526                CaptureKey::Arguments => {
4527                    inner.arguments_source = ArgumentsSource::Captured(register);
4528                }
4529                CaptureKey::NewTarget => inner.new_target_capture = Some(register),
4530                CaptureKey::Parent(_) => inner.parent_constructor_capture = Some(register),
4531            }
4532        }
4533        // The function name binds to a self-closure register only when needed;
4534        // named function expressions refer to themselves via the environment
4535        // in this model, so no extra binding is required here.
4536        inner.bind_parameters(builder, parameters, range)?;
4537        if let ArrowBody::Function(FunctionBody::Block(block))
4538        | ArrowBody::Arrow(FunctionBody::Block(block)) = body
4539        {
4540            inner.hoist_vars(builder, &block.data().statements, range)?;
4541        }
4542        match body {
4543            ArrowBody::Function(FunctionBody::Block(block)) => {
4544                inner.lower_block(builder, block.data())?;
4545                inner.emit_return_undefined(builder, range)?;
4546            }
4547            ArrowBody::Arrow(FunctionBody::Block(block)) => {
4548                inner.lower_block(builder, block.data())?;
4549                inner.emit_return_undefined(builder, range)?;
4550            }
4551            ArrowBody::Arrow(FunctionBody::Expression(expression)) => {
4552                let value = inner.lower_expression(builder, expression)?;
4553                inner.emit(range, Instruction::Return { value })?;
4554            }
4555            ArrowBody::Function(FunctionBody::Expression(_)) => {
4556                return Err(self.missing(range, NodeKind::BlockStatement));
4557            }
4558            ArrowBody::Function(FunctionBody::Missing(missing))
4559            | ArrowBody::Arrow(FunctionBody::Missing(missing)) => {
4560                return Err(self.missing(range, missing.expected()));
4561            }
4562        }
4563        let name_constant = match name {
4564            Some(name) => {
4565                Some(builder.intern(Constant::String(EcmaString::from_utf8(&name)), range)?)
4566            }
4567            None => None,
4568        };
4569        let assembled = inner.into_function(name_constant, flags);
4570        builder.fill_function(id, assembled);
4571        Ok(())
4572    }
4573
4574    /// Binds parameters into the leading (post-capture) registers, supporting
4575    /// simple, defaulted, destructured, and rest parameters.
4576    fn bind_parameters(
4577        &mut self,
4578        builder: &mut ModuleBuilder,
4579        parameters: &[ParameterNode],
4580        range: TextRange,
4581    ) -> Result<(), LowerError> {
4582        // Each non-rest parameter occupies one leading register slot.
4583        let rest_index = parameters.iter().position(|parameter| {
4584            matches!(parameter.data().binding.data(), BindingPattern::Rest(_))
4585        });
4586        let fixed = rest_index.unwrap_or(parameters.len());
4587        // Allocate one register per fixed parameter (the raw positional value).
4588        let mut slots = Vec::with_capacity(fixed);
4589        for _ in 0..fixed {
4590            let register = self.alloc_register(range)?;
4591            self.parameter_count += 1;
4592            slots.push(register);
4593        }
4594        // Captured parameter storage must exist before any default or
4595        // destructuring expression can materialize a closure over it.
4596        let mut undefined_seed = None;
4597        for (index, parameter) in parameters.iter().enumerate() {
4598            let mut names = Vec::new();
4599            collect_pattern_names(self.file, &parameter.data().binding, &mut names);
4600            for name in names {
4601                if !self.capture_plan.captures(
4602                    &name,
4603                    binding_site(parameter.range()),
4604                    DeclarationScope::Function,
4605                ) || self
4606                    .scopes
4607                    .first()
4608                    .is_some_and(|scope| scope.contains_key(&name))
4609                {
4610                    continue;
4611                }
4612                let seed = if index < fixed
4613                    && matches!(
4614                        parameter.data().binding.data(),
4615                        BindingPattern::Identifier(identifier)
4616                            if identifier_name(self.file, identifier).as_deref() == Some(&name)
4617                    ) {
4618                    slots[index]
4619                } else if let Some(seed) = undefined_seed {
4620                    seed
4621                } else {
4622                    let seed = self.undefined(builder, parameter.range())?;
4623                    undefined_seed = Some(seed);
4624                    seed
4625                };
4626                let cell = self.alloc_register(parameter.range())?;
4627                self.emit(parameter.range(), Instruction::CreateArray { dst: cell })?;
4628                self.emit(
4629                    parameter.range(),
4630                    Instruction::ArrayPush {
4631                        array: cell,
4632                        value: seed,
4633                    },
4634                )?;
4635                self.declare(name, Binding::Cell(cell), DeclarationScope::Function);
4636            }
4637        }
4638        for (index, parameter) in parameters.iter().take(fixed).enumerate() {
4639            let data = parameter.data();
4640            let slot = slots[index];
4641            let value = match &data.initializer {
4642                Some(default) => self.apply_default(builder, parameter.range(), slot, default)?,
4643                None => slot,
4644            };
4645            self.bind_pattern(builder, &data.binding, value, DeclarationScope::Function)?;
4646        }
4647        if let Some(rest_index) = rest_index {
4648            let parameter = &parameters[rest_index];
4649            let rest_argument = match parameter.data().binding.data() {
4650                BindingPattern::Rest(rest) => &rest.argument,
4651                _ => unreachable!("rest_index points at a rest binding"),
4652            };
4653            let rest = self.collect_rest_parameter(builder, range, fixed as u32)?;
4654            self.bind_pattern(builder, rest_argument, rest, DeclarationScope::Function)?;
4655        }
4656        Ok(())
4657    }
4658
4659    /// Builds the rest-parameter array from `arguments[fixed..]`.
4660    fn collect_rest_parameter(
4661        &mut self,
4662        builder: &mut ModuleBuilder,
4663        range: TextRange,
4664        fixed: u32,
4665    ) -> Result<Register, LowerError> {
4666        // Rest always receives the current activation's own actual arguments,
4667        // independent of the lexical `arguments` binding. For arrows the
4668        // `arguments` *identifier* stays captured from the enclosing function,
4669        // but `(...rest)` must observe this invocation's arguments, so load the
4670        // activation's arguments directly rather than routing through
4671        // `arguments_value`, which models the identifier's lexical semantics.
4672        let arguments = self.alloc_register(range)?;
4673        self.emit(range, Instruction::LoadArguments { dst: arguments })?;
4674        let iterator = self.alloc_register(range)?;
4675        self.emit(
4676            range,
4677            Instruction::GetIterator {
4678                dst: iterator,
4679                src: arguments,
4680                kind: IteratorKind::Sync,
4681            },
4682        )?;
4683        for _ in 0..fixed {
4684            self.iterator_step_discard(range, iterator)?;
4685        }
4686        self.rest_array(builder, range, iterator)
4687    }
4688
4689    fn emit_return_undefined(
4690        &mut self,
4691        builder: &mut ModuleBuilder,
4692        range: TextRange,
4693    ) -> Result<(), LowerError> {
4694        let value = self.undefined(builder, range)?;
4695        self.emit(range, Instruction::Return { value })?;
4696        Ok(())
4697    }
4698
4699    /// Builds the captures array in the enclosing function and creates the
4700    /// closure over `id`.
4701    fn materialize_closure(
4702        &mut self,
4703        builder: &mut ModuleBuilder,
4704        range: TextRange,
4705        id: FunctionId,
4706        captures: &[CaptureKey],
4707    ) -> Result<Register, LowerError> {
4708        if captures.len() > MAX_REGISTERS as usize {
4709            return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Captures)));
4710        }
4711        let array = self.alloc_register(range)?;
4712        self.emit(range, Instruction::CreateArray { dst: array })?;
4713        for capture in captures {
4714            let value = self.capture_value(builder, range, capture)?;
4715            self.emit(range, Instruction::ArrayPush { array, value })?;
4716        }
4717        let dst = self.alloc_register(range)?;
4718        self.emit(
4719            range,
4720            Instruction::CreateClosure {
4721                dst,
4722                function: id,
4723                captures: array,
4724            },
4725        )?;
4726        Ok(dst)
4727    }
4728
4729    fn capture_value(
4730        &mut self,
4731        builder: &mut ModuleBuilder,
4732        range: TextRange,
4733        capture: &CaptureKey,
4734    ) -> Result<Register, LowerError> {
4735        match capture {
4736            CaptureKey::Name(name) => match self.resolve(name) {
4737                Some(Binding::Cell(cell)) => Ok(cell),
4738                Some(Binding::Local(_)) => {
4739                    panic!("capture plan resolved named capture `{name}` to Local")
4740                }
4741                None => Err(self.error(
4742                    range,
4743                    LowerErrorKind::MissingSyntax {
4744                        expected: NodeKind::Identifier,
4745                    },
4746                )),
4747            },
4748            CaptureKey::This => self.this_value(range),
4749            CaptureKey::Arguments => match self.arguments_value(builder, range)? {
4750                Some(register) => Ok(register),
4751                None => self.undefined(builder, range),
4752            },
4753            CaptureKey::NewTarget => self.new_target_value(range),
4754            CaptureKey::Parent(parent) => Ok(*parent),
4755        }
4756    }
4757
4758    /// Computes the deterministic capture list of a nested function: the free
4759    /// variable names it reads that resolve to a function-local binding of this
4760    /// (enclosing) context, plus `this`/`arguments`/`new.target` for arrows.
4761    fn compute_captures(
4762        &self,
4763        parameters: &[ParameterNode],
4764        body: ArrowBody<'_>,
4765        is_arrow: bool,
4766    ) -> Vec<CaptureKey> {
4767        let mut scanner = FreeVarScanner::new(self.file);
4768        scanner.scan_function(parameters, body, is_arrow);
4769        let mut captures = Vec::new();
4770        for name in &scanner.free {
4771            if self.resolve(name).is_some() {
4772                captures.push(CaptureKey::Name(name.clone()));
4773            }
4774        }
4775        if is_arrow {
4776            if scanner.uses_this {
4777                captures.push(CaptureKey::This);
4778            }
4779            if scanner.uses_arguments {
4780                captures.push(CaptureKey::Arguments);
4781            }
4782            if scanner.uses_new_target {
4783                captures.push(CaptureKey::NewTarget);
4784            }
4785        }
4786        captures
4787    }
4788
4789    // ------------------------------------------------------------------
4790    // Classes
4791    // ------------------------------------------------------------------
4792
4793    fn lower_class_declaration(
4794        &mut self,
4795        builder: &mut ModuleBuilder,
4796        range: TextRange,
4797        class: &ClassDeclaration,
4798        forced_name: Option<&str>,
4799    ) -> Result<(), LowerError> {
4800        let name = match forced_name {
4801            Some(name) => Some(name.to_owned()),
4802            None => match &class.name {
4803                Some(identifier) => Some(self.identifier_text(identifier)?),
4804                None => None,
4805            },
4806        };
4807        self.lower_class_value(builder, range, class, name.as_deref(), None)?;
4808        Ok(())
4809    }
4810
4811    /// Builds a class as a constructor function plus a prototype object with
4812    /// methods, accessors, static members, private names, and prototype chain.
4813    fn lower_class_value(
4814        &mut self,
4815        builder: &mut ModuleBuilder,
4816        range: TextRange,
4817        class: &ClassDeclaration,
4818        declaration_name: Option<&str>,
4819        expression_name: Option<(&str, BindingSite)>,
4820    ) -> Result<Register, LowerError> {
4821        if let Some(decorator) = class.decorators.first() {
4822            return Err(self.unsupported(
4823                decorator.range(),
4824                UnsupportedConstruct::DecoratedDeclaration,
4825            ));
4826        }
4827        let expression_cell = if let Some((name, site)) = expression_name {
4828            self.push_scope();
4829            Some(self.predeclare_class_expression_binding(name, range, site)?)
4830        } else {
4831            None
4832        };
4833        // Class declarations retain their predeclared outer cell. A named class
4834        // expression instead shadows it with its own uninitialized cell here.
4835        let parent = match &class.extends {
4836            Some(heritage) => Some(self.lower_expression(builder, &heritage.expression)?),
4837            None => None,
4838        };
4839        let declaration_target = if let Some(name) = declaration_name {
4840            let site = class
4841                .name
4842                .as_ref()
4843                .map_or(binding_site(range), |identifier| {
4844                    binding_site(identifier.range())
4845                });
4846            self.predeclare_captured_binding(name, range, site, DeclarationScope::Lexical)?;
4847            let identity = binding_identity(name, site, DeclarationScope::Lexical);
4848            if self.top_level {
4849                None
4850            } else if let Some(cell) = self.predeclared_cells.get(&identity).copied() {
4851                Some(Binding::Cell(cell))
4852            } else {
4853                let home = self.alloc_register(range)?;
4854                self.declare(
4855                    name.to_owned(),
4856                    Binding::Local(home),
4857                    DeclarationScope::Lexical,
4858                );
4859                Some(Binding::Local(home))
4860            }
4861        } else {
4862            None
4863        };
4864        if expression_cell.is_none() {
4865            self.push_scope();
4866        }
4867        self.create_private_names(builder, range, class)?;
4868        let constructor = self.find_constructor(class);
4869        let ctor = self.build_constructor(builder, range, class, constructor, parent)?;
4870        let prototype = self.alloc_register(range)?;
4871        self.emit(range, Instruction::CreateObject { dst: prototype })?;
4872        if let Some(parent) = parent {
4873            let parent_prototype = self.get_named(builder, range, parent, "prototype")?;
4874            self.emit(
4875                range,
4876                Instruction::SetPrototype {
4877                    object: prototype,
4878                    prototype: parent_prototype,
4879                },
4880            )?;
4881            self.emit(
4882                range,
4883                Instruction::SetPrototype {
4884                    object: ctor,
4885                    prototype: parent,
4886                },
4887            )?;
4888        }
4889        let prototype_key = self.string_reg(builder, EcmaString::from_utf8("prototype"), range)?;
4890        self.emit(
4891            range,
4892            Instruction::SetProperty {
4893                object: ctor,
4894                key: prototype_key,
4895                value: prototype,
4896            },
4897        )?;
4898        if let Some(cell) = expression_cell {
4899            self.store_cell(builder, cell, ctor, range)?;
4900        }
4901        if let Some(name) = declaration_name {
4902            match declaration_target {
4903                Some(Binding::Local(home)) => self.move_to(range, home, ctor)?,
4904                Some(Binding::Cell(cell)) => self.store_cell(builder, cell, ctor, range)?,
4905                None => {
4906                    debug_assert!(self.top_level);
4907                    let id =
4908                        builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
4909                    self.emit(
4910                        range,
4911                        Instruction::StoreGlobal {
4912                            name: id,
4913                            value: ctor,
4914                        },
4915                    )?;
4916                }
4917            }
4918        }
4919        for member in &class.members {
4920            self.lower_class_member(builder, ctor, prototype, member)?;
4921        }
4922        self.pop_scope();
4923        Ok(ctor)
4924    }
4925
4926    fn find_constructor<'c>(
4927        &self,
4928        class: &'c ClassDeclaration,
4929    ) -> Option<&'c crate::syntax::ConstructorDeclaration> {
4930        class.members.iter().find_map(|member| match member.data() {
4931            ClassMember::Constructor(constructor) => Some(constructor),
4932            _ => None,
4933        })
4934    }
4935
4936    /// Creates the class's declared private names as register-local bindings so
4937    /// method and constructor bodies capture and address them uniformly.
4938    fn create_private_names(
4939        &mut self,
4940        builder: &mut ModuleBuilder,
4941        range: TextRange,
4942        class: &ClassDeclaration,
4943    ) -> Result<(), LowerError> {
4944        let mut seen = HashSet::new();
4945        for member in &class.members {
4946            let name = match member.data() {
4947                ClassMember::Method(method) => &method.name,
4948                ClassMember::Property(property) => &property.name,
4949                ClassMember::AutoAccessor(accessor) => &accessor.name,
4950                _ => continue,
4951            };
4952            if let PropertyName::Private(private) = name {
4953                let text = self.private_text(private)?;
4954                if !seen.insert(text.clone()) {
4955                    continue;
4956                }
4957                let description =
4958                    builder.intern(Constant::String(EcmaString::from_utf8(&text)), range)?;
4959                let value = self.alloc_register(range)?;
4960                self.emit(
4961                    range,
4962                    Instruction::CreatePrivateName {
4963                        dst: value,
4964                        description,
4965                    },
4966                )?;
4967                if self.capture_plan.captures(
4968                    &text,
4969                    binding_site(private.range()),
4970                    DeclarationScope::Lexical,
4971                ) {
4972                    let cell = self.alloc_register(range)?;
4973                    self.emit(range, Instruction::CreateArray { dst: cell })?;
4974                    self.emit(range, Instruction::ArrayPush { array: cell, value })?;
4975                    self.declare(text, Binding::Cell(cell), DeclarationScope::Lexical);
4976                } else {
4977                    self.declare(text, Binding::Local(value), DeclarationScope::Lexical);
4978                }
4979            }
4980        }
4981        Ok(())
4982    }
4983
4984    fn build_constructor(
4985        &mut self,
4986        builder: &mut ModuleBuilder,
4987        range: TextRange,
4988        class: &ClassDeclaration,
4989        constructor: Option<&crate::syntax::ConstructorDeclaration>,
4990        parent: Option<Register>,
4991    ) -> Result<Register, LowerError> {
4992        // Instance field initializers to run in the constructor.
4993        let fields: Vec<&crate::syntax::ClassProperty> = class
4994            .members
4995            .iter()
4996            .filter_map(|member| match member.data() {
4997                ClassMember::Property(property)
4998                    if !property.modifiers.is_static
4999                        && !property.modifiers.is_abstract
5000                        && !property.modifiers.is_declare =>
5001                {
5002                    Some(property)
5003                }
5004                _ => None,
5005            })
5006            .collect();
5007        let (parameters, body_block): (&[ParameterNode], Option<&Block>) = match constructor {
5008            Some(constructor) => (&constructor.parameters, Some(constructor.body.data())),
5009            None => (&[], None),
5010        };
5011        let captures = self.compute_constructor_captures(parameters, body_block, &fields, parent);
5012        let id = builder.reserve_function(range)?;
5013        self.build_constructor_into(
5014            builder,
5015            id,
5016            range,
5017            parameters,
5018            body_block,
5019            &fields,
5020            &captures,
5021            parent.is_some(),
5022        )?;
5023        self.materialize_closure(builder, range, id, &captures)
5024    }
5025
5026    fn compute_constructor_captures(
5027        &self,
5028        parameters: &[ParameterNode],
5029        body: Option<&Block>,
5030        fields: &[&crate::syntax::ClassProperty],
5031        parent: Option<Register>,
5032    ) -> Vec<CaptureKey> {
5033        let mut scanner = FreeVarScanner::new(self.file);
5034        scanner.preseed_parameters(parameters);
5035        if let Some(block) = body {
5036            scanner.preseed_vars(&block.statements);
5037        }
5038        scanner.scan_parameter_initializers(parameters);
5039        if let Some(block) = body {
5040            for statement in &block.statements {
5041                scanner.scan_statement(statement);
5042            }
5043        }
5044        for field in fields {
5045            scanner.scan_property_name(&field.name);
5046            if let Some(initializer) = &field.initializer {
5047                scanner.scan_expression(initializer);
5048            }
5049        }
5050        let mut captures = Vec::new();
5051        for name in &scanner.free {
5052            if self.resolve(name).is_some() {
5053                captures.push(CaptureKey::Name(name.clone()));
5054            }
5055        }
5056        if let Some(parent) = parent {
5057            captures.push(CaptureKey::Parent(parent));
5058        }
5059        captures
5060    }
5061
5062    fn derived_super_index(&self, body: &Block) -> Result<usize, LowerError> {
5063        let mut direct = None;
5064        for (index, statement) in body.statements.iter().enumerate() {
5065            if let Statement::Expression(expression) = statement.data()
5066                && let Expression::Call(call) = expression.expression.data()
5067                && !call.optional
5068                && matches!(call.callee.data(), Expression::Super)
5069            {
5070                if direct.replace(index).is_some() {
5071                    return Err(self.unsupported(
5072                        statement.range(),
5073                        UnsupportedConstruct::DerivedConstructorShape,
5074                    ));
5075                }
5076            }
5077        }
5078        let Some(index) = direct else {
5079            return Err(self.unsupported(
5080                body.statements
5081                    .first()
5082                    .map_or_else(zero_range, |statement| statement.range()),
5083                UnsupportedConstruct::DerivedConstructorShape,
5084            ));
5085        };
5086        let first = body
5087            .statements
5088            .first()
5089            .expect("direct super requires a statement");
5090        let last = body
5091            .statements
5092            .last()
5093            .expect("direct super requires a statement");
5094        let super_count = self
5095            .file
5096            .tokens()
5097            .iter()
5098            .filter(|token| {
5099                token.kind() == TokenKind::KwSuper
5100                    && token.range().start() >= first.range().start()
5101                    && token.range().end() <= last.range().end()
5102            })
5103            .count();
5104        if super_count != 1 {
5105            return Err(self.unsupported(
5106                body.statements[index].range(),
5107                UnsupportedConstruct::DerivedConstructorShape,
5108            ));
5109        }
5110        let super_statement = body.statements[index].range();
5111        if self.file.tokens().iter().any(|token| {
5112            token.kind() == TokenKind::KwThis
5113                && token.range().start() >= first.range().start()
5114                && token.range().end() <= super_statement.end()
5115        }) {
5116            return Err(self.unsupported(
5117                super_statement,
5118                UnsupportedConstruct::ThisBeforeDerivedSuper,
5119            ));
5120        }
5121        Ok(index)
5122    }
5123
5124    fn initialize_instance_fields(
5125        &mut self,
5126        builder: &mut ModuleBuilder,
5127        range: TextRange,
5128        fields: &[&crate::syntax::ClassProperty],
5129    ) -> Result<(), LowerError> {
5130        for field in fields {
5131            let this_value = self.this_value(range)?;
5132            let key = self.property_key(builder, &field.name)?;
5133            let value = match &field.initializer {
5134                Some(initializer) => self.lower_expression(builder, initializer)?,
5135                None => self.undefined(builder, range)?,
5136            };
5137            self.emit(
5138                range,
5139                Instruction::SetProperty {
5140                    object: this_value,
5141                    key,
5142                    value,
5143                },
5144            )?;
5145        }
5146        Ok(())
5147    }
5148
5149    fn lower_derived_super(
5150        &mut self,
5151        builder: &mut ModuleBuilder,
5152        range: TextRange,
5153        call: &CallExpression,
5154    ) -> Result<(), LowerError> {
5155        let parent = self.parent_constructor_capture.ok_or_else(|| {
5156            self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape)
5157        })?;
5158        let this_value = self.this_value(range)?;
5159        let arguments = self.build_arguments(builder, range, &call.arguments)?;
5160        let dst = self.alloc_register(range)?;
5161        self.emit(
5162            range,
5163            Instruction::Call {
5164                dst,
5165                callee: parent,
5166                this_value,
5167                arguments,
5168            },
5169        )?;
5170        Ok(())
5171    }
5172
5173    fn lower_implicit_derived_super(
5174        &mut self,
5175        builder: &mut ModuleBuilder,
5176        range: TextRange,
5177    ) -> Result<(), LowerError> {
5178        let parent = self.parent_constructor_capture.ok_or_else(|| {
5179            self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape)
5180        })?;
5181        let this_value = self.this_value(range)?;
5182        let arguments = self.arguments_value(builder, range)?.ok_or_else(|| {
5183            self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape)
5184        })?;
5185        let call_arguments = self.alloc_register(range)?;
5186        self.emit(
5187            range,
5188            Instruction::CreateArray {
5189                dst: call_arguments,
5190            },
5191        )?;
5192        self.emit(
5193            range,
5194            Instruction::ArrayExtend {
5195                array: call_arguments,
5196                iterable: arguments,
5197            },
5198        )?;
5199        let dst = self.alloc_register(range)?;
5200        self.emit(
5201            range,
5202            Instruction::Call {
5203                dst,
5204                callee: parent,
5205                this_value,
5206                arguments: call_arguments,
5207            },
5208        )?;
5209        Ok(())
5210    }
5211
5212    #[allow(clippy::too_many_arguments)]
5213    fn build_constructor_into(
5214        &mut self,
5215        builder: &mut ModuleBuilder,
5216        id: FunctionId,
5217        range: TextRange,
5218        parameters: &[ParameterNode],
5219        body: Option<&Block>,
5220        fields: &[&crate::syntax::ClassProperty],
5221        captures: &[CaptureKey],
5222        derived: bool,
5223    ) -> Result<(), LowerError> {
5224        let capture_plan = CapturePlan::for_constructor(self.file, parameters, body, fields);
5225        let mut inner = FunctionContext {
5226            file: self.file,
5227            code: Vec::new(),
5228            registers: 0,
5229            capture_count: 0,
5230            parameter_count: 0,
5231            scopes: vec![HashMap::new()],
5232            predeclared_cells: HashMap::new(),
5233            capture_plan,
5234            loops: Vec::new(),
5235            handlers: Vec::new(),
5236            finally_stack: Vec::new(),
5237            top_level: false,
5238            goal: self.goal,
5239            completion: None,
5240            completion_pool: Vec::new(),
5241            completion_depth: 0,
5242            this_capture: None,
5243            new_target_capture: None,
5244            parent_constructor_capture: None,
5245            arguments_source: ArgumentsSource::Own,
5246        };
5247        for capture in captures {
5248            let register = inner.alloc_register(range)?;
5249            inner.capture_count += 1;
5250            match capture {
5251                CaptureKey::Name(name) => {
5252                    inner.declare(
5253                        name.clone(),
5254                        Binding::Cell(register),
5255                        DeclarationScope::Function,
5256                    );
5257                }
5258                CaptureKey::Parent(_) => inner.parent_constructor_capture = Some(register),
5259                CaptureKey::This | CaptureKey::Arguments | CaptureKey::NewTarget => {
5260                    unreachable!("constructors do not capture arrow-only bindings")
5261                }
5262            }
5263        }
5264        inner.bind_parameters(builder, parameters, range)?;
5265        if let Some(block) = body {
5266            inner.hoist_vars(builder, &block.statements, range)?;
5267        }
5268        if derived {
5269            if let Some(block) = body {
5270                let super_index = inner.derived_super_index(block)?;
5271                inner.push_scope();
5272                for statement in &block.statements[..super_index] {
5273                    inner.lower_statement(builder, statement)?;
5274                }
5275                let Statement::Expression(expression) = block.statements[super_index].data() else {
5276                    unreachable!("derived_super_index selects an expression statement");
5277                };
5278                let Expression::Call(call) = expression.expression.data() else {
5279                    unreachable!("derived_super_index selects a call expression");
5280                };
5281                inner.lower_derived_super(builder, block.statements[super_index].range(), call)?;
5282                let body_scope = inner.scopes.pop().expect("constructor block scope exists");
5283                inner.initialize_instance_fields(builder, range, fields)?;
5284                inner.scopes.push(body_scope);
5285                for statement in &block.statements[super_index + 1..] {
5286                    inner.lower_statement(builder, statement)?;
5287                }
5288                inner.pop_scope();
5289            } else {
5290                inner.lower_implicit_derived_super(builder, range)?;
5291                inner.initialize_instance_fields(builder, range, fields)?;
5292            }
5293        } else {
5294            inner.initialize_instance_fields(builder, range, fields)?;
5295            if let Some(block) = body {
5296                inner.lower_block(builder, block)?;
5297            }
5298        }
5299        inner.emit_return_undefined(builder, range)?;
5300        let assembled = inner.into_function(None, FunctionFlags::default());
5301        builder.fill_function(id, assembled);
5302        Ok(())
5303    }
5304
5305    fn lower_class_member(
5306        &mut self,
5307        builder: &mut ModuleBuilder,
5308        ctor: Register,
5309        prototype: Register,
5310        member: &crate::syntax::ClassMemberNode,
5311    ) -> Result<(), LowerError> {
5312        let range = member.range();
5313        match member.data() {
5314            ClassMember::Constructor(_) => Ok(()),
5315            ClassMember::Method(method) => {
5316                if method.function.body.is_none() {
5317                    // Abstract method or overload signature: type-only.
5318                    return Ok(());
5319                }
5320                let target = if method.modifiers.is_static {
5321                    ctor
5322                } else {
5323                    prototype
5324                };
5325                let key = self.property_key(builder, &method.name)?;
5326                let value = self.build_function_value(builder, range, None, &method.function)?;
5327                self.install_property(builder, range, target, key, value, method.modifier)
5328            }
5329            ClassMember::Property(property) => {
5330                if property.modifiers.is_abstract || property.modifiers.is_declare {
5331                    // Type-only field declaration: no runtime slot.
5332                    return Ok(());
5333                }
5334                if property.modifiers.is_static {
5335                    // Static field: ctor[key] = init.
5336                    let key = self.property_key(builder, &property.name)?;
5337                    let value = match &property.initializer {
5338                        Some(initializer) => self.lower_expression(builder, initializer)?,
5339                        None => self.undefined(builder, range)?,
5340                    };
5341                    self.emit(
5342                        range,
5343                        Instruction::SetProperty {
5344                            object: ctor,
5345                            key,
5346                            value,
5347                        },
5348                    )?;
5349                }
5350                // Instance fields are initialized in the constructor.
5351                Ok(())
5352            }
5353            ClassMember::AutoAccessor(accessor) => {
5354                if accessor.modifiers.is_abstract || accessor.modifiers.is_declare {
5355                    return Ok(());
5356                }
5357                // An auto-accessor is modeled as a plain data property on its
5358                // target for structural purposes.
5359                let target = if accessor.modifiers.is_static {
5360                    ctor
5361                } else {
5362                    prototype
5363                };
5364                let key = self.property_key(builder, &accessor.name)?;
5365                let value = match &accessor.initializer {
5366                    Some(initializer) => self.lower_expression(builder, initializer)?,
5367                    None => self.undefined(builder, range)?,
5368                };
5369                self.emit(
5370                    range,
5371                    Instruction::SetProperty {
5372                        object: target,
5373                        key,
5374                        value,
5375                    },
5376                )?;
5377                Ok(())
5378            }
5379            ClassMember::StaticBlock(block) => {
5380                // A static initialization block runs at class definition time
5381                // in the enclosing scope, with `this` bound to the constructor.
5382                self.push_scope();
5383                let result = self.lower_block(builder, block.data());
5384                self.pop_scope();
5385                result
5386            }
5387            ClassMember::IndexSignature(_) => Ok(()),
5388            ClassMember::Missing(missing) => Err(self.missing(range, missing.expected())),
5389        }
5390    }
5391}
5392
5393/// A function body that is either a classic function body or an arrow body.
5394#[derive(Clone, Copy)]
5395enum ArrowBody<'a> {
5396    Function(&'a FunctionBody),
5397    Arrow(&'a FunctionBody),
5398}
5399
5400#[derive(Clone)]
5401struct ScannedBinding {
5402    identity: BindingIdentity,
5403    owner_depth: u32,
5404}
5405
5406/// Collects free variables, special lexical uses, and captured root binding
5407/// identities using the same declaration timeline as lowering.
5408struct FreeVarScanner<'a> {
5409    file: &'a SourceFile,
5410    bound: Vec<HashMap<String, ScannedBinding>>,
5411    function_roots: Vec<usize>,
5412    free: BTreeSet<String>,
5413    captured: HashSet<BindingIdentity>,
5414    runtime_cells: HashSet<BindingIdentity>,
5415    initialized: HashSet<BindingIdentity>,
5416    uses_this: bool,
5417    uses_arguments: bool,
5418    uses_new_target: bool,
5419    fn_boundary: u32,
5420    function_depth: u32,
5421}
5422
5423impl CapturePlan {
5424    fn for_statements(file: &SourceFile, statements: &[Stmt]) -> Self {
5425        let mut scanner = FreeVarScanner::new(file);
5426        scanner.preseed_vars(statements);
5427        scanner.predeclare_immediate(statements, false);
5428        for statement in statements {
5429            scanner.scan_statement(statement);
5430        }
5431        Self {
5432            captured: scanner.captured,
5433            runtime_cells: scanner.runtime_cells,
5434        }
5435    }
5436
5437    fn for_function(file: &SourceFile, parameters: &[ParameterNode], body: ArrowBody<'_>) -> Self {
5438        let mut scanner = FreeVarScanner::new(file);
5439        scanner.scan_function(parameters, body, false);
5440        Self {
5441            captured: scanner.captured,
5442            runtime_cells: scanner.runtime_cells,
5443        }
5444    }
5445
5446    fn for_constructor(
5447        file: &SourceFile,
5448        parameters: &[ParameterNode],
5449        body: Option<&Block>,
5450        fields: &[&crate::syntax::ClassProperty],
5451    ) -> Self {
5452        let mut scanner = FreeVarScanner::new(file);
5453        scanner.preseed_parameters(parameters);
5454        if let Some(block) = body {
5455            scanner.preseed_vars(&block.statements);
5456            scanner.predeclare_immediate(&block.statements, false);
5457        }
5458        scanner.scan_parameter_initializers(parameters);
5459        if let Some(block) = body {
5460            for statement in &block.statements {
5461                scanner.scan_statement(statement);
5462            }
5463        }
5464        for field in fields {
5465            scanner.scan_property_name(&field.name);
5466            if let Some(initializer) = &field.initializer {
5467                scanner.scan_expression(initializer);
5468            }
5469        }
5470        Self {
5471            captured: scanner.captured,
5472            runtime_cells: scanner.runtime_cells,
5473        }
5474    }
5475}
5476
5477impl<'a> FreeVarScanner<'a> {
5478    fn new(file: &'a SourceFile) -> Self {
5479        Self {
5480            file,
5481            bound: vec![HashMap::new()],
5482            function_roots: vec![0],
5483            free: BTreeSet::new(),
5484            captured: HashSet::new(),
5485            runtime_cells: HashSet::new(),
5486            initialized: HashSet::new(),
5487            uses_this: false,
5488            uses_arguments: false,
5489            uses_new_target: false,
5490            fn_boundary: 0,
5491            function_depth: 0,
5492        }
5493    }
5494
5495    fn scan_function(
5496        &mut self,
5497        parameters: &[ParameterNode],
5498        body: ArrowBody<'_>,
5499        _is_arrow: bool,
5500    ) {
5501        self.preseed_parameters(parameters);
5502        if let ArrowBody::Function(FunctionBody::Block(block))
5503        | ArrowBody::Arrow(FunctionBody::Block(block)) = body
5504        {
5505            self.preseed_vars(&block.data().statements);
5506            self.predeclare_immediate(&block.data().statements, false);
5507        }
5508        self.scan_parameter_initializers(parameters);
5509        match body {
5510            ArrowBody::Function(FunctionBody::Block(block))
5511            | ArrowBody::Arrow(FunctionBody::Block(block)) => {
5512                for statement in &block.data().statements {
5513                    self.scan_statement(statement);
5514                }
5515            }
5516            ArrowBody::Arrow(FunctionBody::Expression(expression))
5517            | ArrowBody::Function(FunctionBody::Expression(expression)) => {
5518                self.scan_expression(expression);
5519            }
5520            _ => {}
5521        }
5522    }
5523
5524    fn push(&mut self) {
5525        self.bound.push(HashMap::new());
5526    }
5527
5528    fn pop(&mut self) {
5529        self.bound.pop();
5530    }
5531
5532    fn bind_function(&mut self, name: String) {
5533        let root = *self
5534            .function_roots
5535            .last()
5536            .expect("scanner always has a function root");
5537        self.bound[root]
5538            .entry(name.clone())
5539            .or_insert(ScannedBinding {
5540                identity: BindingIdentity::Function(name),
5541                owner_depth: self.function_depth,
5542            });
5543    }
5544
5545    fn bind_lexical(&mut self, name: String, range: TextRange) {
5546        if let Some(scope) = self.bound.last_mut() {
5547            scope.insert(
5548                name,
5549                ScannedBinding {
5550                    identity: BindingIdentity::Lexical(binding_site(range)),
5551                    owner_depth: self.function_depth,
5552                },
5553            );
5554        }
5555    }
5556
5557    fn resolve_binding(&self, name: &str) -> Option<&ScannedBinding> {
5558        self.bound.iter().rev().find_map(|scope| scope.get(name))
5559    }
5560
5561    fn scan_property_name(&mut self, name: &PropertyName) {
5562        match name {
5563            PropertyName::Computed(expression) => self.scan_expression(expression),
5564            PropertyName::Private(private) => {
5565                if let Some(text) = private_name(self.file, private) {
5566                    self.use_name(&text);
5567                }
5568            }
5569            _ => {}
5570        }
5571    }
5572
5573    fn use_name(&mut self, name: &str) {
5574        if name == "arguments" {
5575            if self.fn_boundary == 0 {
5576                self.uses_arguments = true;
5577            }
5578            return;
5579        }
5580        if let Some(binding) = self.resolve_binding(name).cloned() {
5581            if binding.owner_depth == 0 && !self.initialized.contains(&binding.identity) {
5582                self.runtime_cells.insert(binding.identity.clone());
5583            }
5584            if self.function_depth > binding.owner_depth && binding.owner_depth == 0 {
5585                self.captured.insert(binding.identity);
5586            }
5587        } else {
5588            self.free.insert(name.to_owned());
5589        }
5590    }
5591
5592    fn predeclare_immediate(&mut self, statements: &[Stmt], switch_scope: bool) {
5593        for declaration in collect_immediate_declarations(self.file, statements) {
5594            match declaration.kind {
5595                ImmediateDeclarationKind::Function(_) => {
5596                    let identity = BindingIdentity::Function(declaration.name.clone());
5597                    self.bind_function(declaration.name);
5598                    self.initialized.insert(identity);
5599                }
5600                ImmediateDeclarationKind::Lexical => {
5601                    let identity = BindingIdentity::Lexical(declaration.site);
5602                    self.bind_lexical(declaration.name, declaration.range);
5603                    if switch_scope {
5604                        self.runtime_cells.insert(identity);
5605                    }
5606                }
5607            }
5608        }
5609    }
5610
5611    fn initialize_pattern(&mut self, pattern: &Pattern, declaration_scope: DeclarationScope) {
5612        let mut names = Vec::new();
5613        collect_pattern_names(self.file, pattern, &mut names);
5614        for name in names {
5615            if let Some(binding) = self.resolve_binding(&name)
5616                && (matches!(declaration_scope, DeclarationScope::Function)
5617                    || binding.owner_depth == self.function_depth)
5618            {
5619                self.initialized.insert(binding.identity.clone());
5620            }
5621        }
5622    }
5623
5624    fn preseed_parameters(&mut self, parameters: &[ParameterNode]) {
5625        for parameter in parameters {
5626            let mut names = Vec::new();
5627            collect_pattern_names(self.file, &parameter.data().binding, &mut names);
5628            for name in names {
5629                self.bind_function(name.clone());
5630                self.initialized.insert(BindingIdentity::Function(name));
5631            }
5632        }
5633    }
5634
5635    fn preseed_vars(&mut self, statements: &[Stmt]) {
5636        let mut names = Vec::new();
5637        collect_var_names(self.file, statements, &mut names);
5638        for name in names {
5639            self.bind_function(name.clone());
5640            self.initialized.insert(BindingIdentity::Function(name));
5641        }
5642    }
5643
5644    fn scan_parameter_initializers(&mut self, parameters: &[ParameterNode]) {
5645        for parameter in parameters {
5646            let data = parameter.data();
5647            if let Some(initializer) = &data.initializer {
5648                self.scan_expression(initializer);
5649            }
5650            self.scan_pattern_effects(&data.binding);
5651        }
5652    }
5653
5654    fn scan_pattern_effects(&mut self, pattern: &Pattern) {
5655        match pattern.data() {
5656            BindingPattern::Identifier(_) | BindingPattern::Missing(_) => {}
5657            BindingPattern::Object(object) => {
5658                for property in &object.properties {
5659                    if let PropertyName::Computed(expression) = &property.name {
5660                        self.scan_expression(expression);
5661                    }
5662                    if let Some(initializer) = &property.initializer {
5663                        self.scan_expression(initializer);
5664                    }
5665                    self.scan_pattern_effects(&property.binding);
5666                }
5667            }
5668            BindingPattern::Array(array) => {
5669                for element in &array.elements {
5670                    if let ArrayBindingElement::Binding(inner) = element {
5671                        self.scan_pattern_effects(inner);
5672                    }
5673                }
5674            }
5675            BindingPattern::Rest(rest) => self.scan_pattern_effects(&rest.argument),
5676            BindingPattern::Assignment(assignment) => {
5677                self.scan_expression(&assignment.right);
5678                self.scan_pattern_effects(&assignment.left);
5679            }
5680        }
5681    }
5682
5683    fn bind_pattern(&mut self, pattern: &Pattern, declaration_scope: DeclarationScope) {
5684        match pattern.data() {
5685            BindingPattern::Identifier(identifier) => {
5686                if let Some(text) = identifier_name(self.file, identifier) {
5687                    match declaration_scope {
5688                        DeclarationScope::Function => self.bind_function(text),
5689                        DeclarationScope::Lexical | DeclarationScope::Iteration => {
5690                            self.bind_lexical(text, identifier.range());
5691                        }
5692                    }
5693                }
5694            }
5695            BindingPattern::Object(object) => {
5696                for property in &object.properties {
5697                    self.bind_pattern(&property.binding, declaration_scope);
5698                }
5699            }
5700            BindingPattern::Array(array) => {
5701                for element in &array.elements {
5702                    if let ArrayBindingElement::Binding(inner) = element {
5703                        self.bind_pattern(inner, declaration_scope);
5704                    }
5705                }
5706            }
5707            BindingPattern::Rest(rest) => self.bind_pattern(&rest.argument, declaration_scope),
5708            BindingPattern::Assignment(assignment) => {
5709                self.bind_pattern(&assignment.left, declaration_scope);
5710            }
5711            BindingPattern::Missing(_) => {}
5712        }
5713    }
5714
5715    fn scan_statement(&mut self, statement: &Stmt) {
5716        match statement.data() {
5717            Statement::Variable(declaration) => {
5718                let scope = match declaration.kind {
5719                    VariableKind::Var => DeclarationScope::Function,
5720                    _ => DeclarationScope::Lexical,
5721                };
5722                for declarator in &declaration.declarations {
5723                    if let Some(initializer) = &declarator.data().initializer {
5724                        self.scan_expression(initializer);
5725                    }
5726                    self.scan_pattern_effects(&declarator.data().binding);
5727                    self.initialize_pattern(&declarator.data().binding, scope);
5728                }
5729            }
5730            Statement::Function(declaration) => {
5731                self.scan_function_like(&declaration.function);
5732            }
5733            Statement::Class(class) => {
5734                self.scan_class_heritage(class);
5735                if let Some(name) = &class.name
5736                    && let Some(text) = identifier_name(self.file, name)
5737                    && let Some(binding) = self.resolve_binding(&text)
5738                {
5739                    self.initialized.insert(binding.identity.clone());
5740                }
5741                self.scan_class(class);
5742            }
5743            Statement::Expression(expression) => self.scan_expression(&expression.expression),
5744            Statement::Return(statement) => {
5745                if let Some(argument) = &statement.argument {
5746                    self.scan_expression(argument);
5747                }
5748            }
5749            Statement::Throw(statement) => self.scan_expression(&statement.argument),
5750            Statement::If(statement) => {
5751                self.scan_expression(&statement.test);
5752                self.scan_statement(&statement.consequent);
5753                if let Some(alternate) = &statement.alternate {
5754                    self.scan_statement(alternate);
5755                }
5756            }
5757            Statement::Block(block) => {
5758                self.push();
5759                self.predeclare_immediate(&block.data().statements, false);
5760                for statement in &block.data().statements {
5761                    self.scan_statement(statement);
5762                }
5763                self.pop();
5764            }
5765            Statement::While(statement) => {
5766                self.scan_expression(&statement.test);
5767                self.scan_statement(&statement.body);
5768            }
5769            Statement::DoWhile(statement) => {
5770                self.scan_statement(&statement.body);
5771                self.scan_expression(&statement.test);
5772            }
5773            Statement::For(statement) => {
5774                self.push();
5775                if let Some(initializer) = &statement.initializer {
5776                    match initializer {
5777                        ForInitializer::Variable(declaration) => {
5778                            let scope = match declaration.kind {
5779                                VariableKind::Var => DeclarationScope::Function,
5780                                _ => DeclarationScope::Lexical,
5781                            };
5782                            for declarator in &declaration.declarations {
5783                                self.bind_pattern(&declarator.data().binding, scope);
5784                                if let Some(init) = &declarator.data().initializer {
5785                                    self.scan_expression(init);
5786                                }
5787                                self.scan_pattern_effects(&declarator.data().binding);
5788                                self.initialize_pattern(&declarator.data().binding, scope);
5789                            }
5790                        }
5791                        ForInitializer::Expression(expression) => self.scan_expression(expression),
5792                    }
5793                }
5794                if let Some(test) = &statement.test {
5795                    self.scan_expression(test);
5796                }
5797                if let Some(update) = &statement.update {
5798                    self.scan_expression(update);
5799                }
5800                self.scan_statement(&statement.body);
5801                self.pop();
5802            }
5803            Statement::ForIn(statement) => {
5804                self.push();
5805                self.scan_expression(&statement.object);
5806                self.scan_for_binding(&statement.binding);
5807                self.scan_statement(&statement.body);
5808                self.pop();
5809            }
5810            Statement::ForOf(statement) => {
5811                self.push();
5812                self.scan_expression(&statement.iterable);
5813                self.scan_for_binding(&statement.binding);
5814                self.scan_statement(&statement.body);
5815                self.pop();
5816            }
5817            Statement::Switch(statement) => {
5818                self.scan_expression(&statement.discriminant);
5819                self.push();
5820                let statements = statement
5821                    .cases
5822                    .iter()
5823                    .flat_map(|case| case.data().consequent.iter().cloned())
5824                    .collect::<Vec<_>>();
5825                self.predeclare_immediate(&statements, true);
5826                for case in &statement.cases {
5827                    if let Some(test) = &case.data().test {
5828                        self.scan_expression(test);
5829                    }
5830                    for statement in &case.data().consequent {
5831                        self.scan_statement(statement);
5832                    }
5833                }
5834                self.pop();
5835            }
5836            Statement::Try(statement) => {
5837                self.push();
5838                self.predeclare_immediate(&statement.block.data().statements, false);
5839                for statement in &statement.block.data().statements {
5840                    self.scan_statement(statement);
5841                }
5842                self.pop();
5843                if let Some(handler) = &statement.handler {
5844                    self.push();
5845                    if let Some(binding) = &handler.data().binding {
5846                        self.bind_pattern(binding, DeclarationScope::Lexical);
5847                        self.initialize_pattern(binding, DeclarationScope::Lexical);
5848                    }
5849                    self.predeclare_immediate(&handler.data().body.data().statements, false);
5850                    for statement in &handler.data().body.data().statements {
5851                        self.scan_statement(statement);
5852                    }
5853                    self.pop();
5854                }
5855                if let Some(finalizer) = &statement.finalizer {
5856                    self.push();
5857                    self.predeclare_immediate(&finalizer.data().statements, false);
5858                    for statement in &finalizer.data().statements {
5859                        self.scan_statement(statement);
5860                    }
5861                    self.pop();
5862                }
5863            }
5864            Statement::Labeled(statement) => self.scan_statement(&statement.body),
5865            Statement::Export(ExportDeclaration::Named(ExportNamedDeclaration::Declaration(
5866                statement,
5867            ))) => self.scan_statement(statement),
5868            Statement::Export(ExportDeclaration::Default(default)) => match &default.value {
5869                ExportDefaultValue::Expression(expression) => self.scan_expression(expression),
5870                ExportDefaultValue::Function(function) => {
5871                    if function.body.is_some()
5872                        && let Some(name) = &function.name
5873                        && let Some(text) = identifier_name(self.file, name)
5874                    {
5875                        self.bind_function(text);
5876                    }
5877                    self.scan_function_like(function);
5878                }
5879                ExportDefaultValue::Class(class) => {
5880                    self.scan_class_heritage(class);
5881                    if let Some(name) = &class.name
5882                        && let Some(text) = identifier_name(self.file, name)
5883                    {
5884                        self.bind_lexical(text, name.range());
5885                    }
5886                    self.scan_class(class);
5887                }
5888                ExportDefaultValue::Missing(_) => {}
5889            },
5890            _ => {}
5891        }
5892    }
5893
5894    fn scan_for_binding(&mut self, binding: &ForBinding) {
5895        match binding {
5896            ForBinding::Variable(declaration) => {
5897                let scope = match declaration.kind {
5898                    VariableKind::Var => DeclarationScope::Function,
5899                    _ => DeclarationScope::Iteration,
5900                };
5901                for declarator in &declaration.declarations {
5902                    self.bind_pattern(&declarator.data().binding, scope);
5903                    self.scan_pattern_effects(&declarator.data().binding);
5904                }
5905            }
5906            ForBinding::Target(target) => self.scan_assignment_target(target),
5907        }
5908    }
5909
5910    fn scan_function_like(&mut self, function: &FunctionLike) {
5911        self.fn_boundary += 1;
5912        self.function_depth += 1;
5913        self.push();
5914        self.function_roots.push(self.bound.len() - 1);
5915        self.preseed_parameters(&function.parameters);
5916        if let Some(FunctionBody::Block(block)) = &function.body {
5917            self.preseed_vars(&block.data().statements);
5918            self.predeclare_immediate(&block.data().statements, false);
5919        }
5920        self.scan_parameter_initializers(&function.parameters);
5921        if let Some(body) = &function.body {
5922            match body {
5923                FunctionBody::Block(block) => {
5924                    for statement in &block.data().statements {
5925                        self.scan_statement(statement);
5926                    }
5927                }
5928                FunctionBody::Expression(expression) => self.scan_expression(expression),
5929                FunctionBody::Missing(_) => {}
5930            }
5931        }
5932        self.function_roots.pop();
5933        self.pop();
5934        self.function_depth -= 1;
5935        self.fn_boundary -= 1;
5936    }
5937
5938    fn scan_arrow(&mut self, arrow: &ArrowFunction) {
5939        self.function_depth += 1;
5940        self.push();
5941        self.function_roots.push(self.bound.len() - 1);
5942        self.preseed_parameters(&arrow.parameters);
5943        if let FunctionBody::Block(block) = &arrow.body {
5944            self.preseed_vars(&block.data().statements);
5945            self.predeclare_immediate(&block.data().statements, false);
5946        }
5947        self.scan_parameter_initializers(&arrow.parameters);
5948        match &arrow.body {
5949            FunctionBody::Block(block) => {
5950                for statement in &block.data().statements {
5951                    self.scan_statement(statement);
5952                }
5953            }
5954            FunctionBody::Expression(expression) => self.scan_expression(expression),
5955            FunctionBody::Missing(_) => {}
5956        }
5957        self.function_roots.pop();
5958        self.pop();
5959        self.function_depth -= 1;
5960    }
5961
5962    fn scan_class_heritage(&mut self, class: &ClassDeclaration) {
5963        if let Some(heritage) = &class.extends {
5964            self.scan_expression(&heritage.expression);
5965        }
5966    }
5967
5968    fn scan_class(&mut self, class: &ClassDeclaration) {
5969        self.push();
5970        let mut seen_private = HashSet::new();
5971        for member in &class.members {
5972            let name = match member.data() {
5973                ClassMember::Method(method) => &method.name,
5974                ClassMember::Property(property) => &property.name,
5975                ClassMember::AutoAccessor(accessor) => &accessor.name,
5976                _ => continue,
5977            };
5978            if let PropertyName::Private(private) = name
5979                && let Some(text) = private_name(self.file, private)
5980                && seen_private.insert(text.clone())
5981            {
5982                self.bind_lexical(text, private.range());
5983            }
5984        }
5985        let constructor = class.members.iter().find_map(|member| match member.data() {
5986            ClassMember::Constructor(constructor) => Some(constructor),
5987            _ => None,
5988        });
5989        let parameters = constructor
5990            .map(|constructor| constructor.parameters.as_slice())
5991            .unwrap_or(&[]);
5992        self.fn_boundary += 1;
5993        self.function_depth += 1;
5994        self.push();
5995        self.function_roots.push(self.bound.len() - 1);
5996        self.preseed_parameters(parameters);
5997        if let Some(constructor) = constructor {
5998            self.preseed_vars(&constructor.body.data().statements);
5999            self.predeclare_immediate(&constructor.body.data().statements, false);
6000        }
6001        self.scan_parameter_initializers(parameters);
6002        if let Some(constructor) = constructor {
6003            for statement in &constructor.body.data().statements {
6004                self.scan_statement(statement);
6005            }
6006        }
6007        for member in &class.members {
6008            if let ClassMember::Property(property) = member.data()
6009                && !property.modifiers.is_static
6010                && !property.modifiers.is_abstract
6011                && !property.modifiers.is_declare
6012            {
6013                self.scan_property_name(&property.name);
6014                if let Some(initializer) = &property.initializer {
6015                    self.scan_expression(initializer);
6016                }
6017            }
6018        }
6019        self.function_roots.pop();
6020        self.pop();
6021        self.function_depth -= 1;
6022        self.fn_boundary -= 1;
6023
6024        for member in &class.members {
6025            match member.data() {
6026                ClassMember::Constructor(_) => {}
6027                ClassMember::Method(method) => {
6028                    if let PropertyName::Computed(expression) = &method.name {
6029                        self.scan_expression(expression);
6030                    }
6031                    self.scan_function_like(&method.function);
6032                }
6033                ClassMember::Property(property) if property.modifiers.is_static => {
6034                    self.scan_property_name(&property.name);
6035                    if let Some(initializer) = &property.initializer {
6036                        self.scan_expression(initializer);
6037                    }
6038                }
6039                ClassMember::AutoAccessor(accessor) => {
6040                    self.scan_property_name(&accessor.name);
6041                    if let Some(initializer) = &accessor.initializer {
6042                        self.scan_expression(initializer);
6043                    }
6044                }
6045                ClassMember::StaticBlock(block) => {
6046                    self.push();
6047                    self.predeclare_immediate(&block.data().statements, false);
6048                    for statement in &block.data().statements {
6049                        self.scan_statement(statement);
6050                    }
6051                    self.pop();
6052                }
6053                _ => {}
6054            }
6055        }
6056        self.pop();
6057    }
6058
6059    fn scan_expression(&mut self, expression: &Expr) {
6060        match expression.data() {
6061            Expression::Identifier(identifier) => {
6062                if let Some(name) = identifier_name(self.file, identifier) {
6063                    self.use_name(&name);
6064                }
6065            }
6066            Expression::This => {
6067                if self.fn_boundary == 0 {
6068                    self.uses_this = true;
6069                }
6070            }
6071            Expression::Super => {}
6072            Expression::Meta(MetaProperty::NewTarget) => {
6073                if self.fn_boundary == 0 {
6074                    self.uses_new_target = true;
6075                }
6076            }
6077            Expression::Meta(MetaProperty::ImportMeta) => {}
6078            Expression::Literal(_) => {}
6079            Expression::Template(template) => {
6080                for expression in &template.expressions {
6081                    self.scan_expression(expression);
6082                }
6083            }
6084            Expression::TaggedTemplate(tagged) => {
6085                self.scan_expression(&tagged.tag);
6086                for expression in &tagged.template.expressions {
6087                    self.scan_expression(expression);
6088                }
6089            }
6090            Expression::Array(array) => {
6091                for element in &array.elements {
6092                    match element {
6093                        ArrayElement::Expression(expression) => self.scan_expression(expression),
6094                        ArrayElement::Spread(spread) => self.scan_expression(&spread.argument),
6095                        _ => {}
6096                    }
6097                }
6098            }
6099            Expression::Object(object) => {
6100                for member in &object.members {
6101                    match member.data() {
6102                        ObjectMember::Property(property) => {
6103                            if let PropertyName::Computed(key) = &property.name {
6104                                self.scan_expression(key);
6105                            }
6106                            self.scan_expression(&property.value);
6107                        }
6108                        ObjectMember::Method(method) => {
6109                            if let PropertyName::Computed(key) = &method.name {
6110                                self.scan_expression(key);
6111                            }
6112                            self.scan_function_like(&method.function);
6113                        }
6114                        ObjectMember::Spread(spread) => self.scan_expression(&spread.argument),
6115                        ObjectMember::Missing(_) => {}
6116                    }
6117                }
6118            }
6119            Expression::Function(function) => self.scan_function_like(&function.function),
6120            Expression::Class(class) => {
6121                self.scan_class_heritage(&class.class);
6122                self.scan_class(&class.class);
6123            }
6124            Expression::Arrow(arrow) => self.scan_arrow(arrow),
6125            Expression::Call(call) => {
6126                self.scan_expression(&call.callee);
6127                for argument in &call.arguments {
6128                    match argument {
6129                        CallArgument::Expression(expression) => self.scan_expression(expression),
6130                        CallArgument::Spread(spread) => self.scan_expression(&spread.argument),
6131                        CallArgument::Missing(_) => {}
6132                    }
6133                }
6134            }
6135            Expression::New(new) => {
6136                self.scan_expression(&new.callee);
6137                for argument in &new.arguments {
6138                    match argument {
6139                        CallArgument::Expression(expression) => self.scan_expression(expression),
6140                        CallArgument::Spread(spread) => self.scan_expression(&spread.argument),
6141                        CallArgument::Missing(_) => {}
6142                    }
6143                }
6144            }
6145            Expression::Member(member) => {
6146                self.scan_expression(&member.object);
6147                if let MemberProperty::Computed(expression) = &member.property {
6148                    self.scan_expression(expression);
6149                }
6150                if let MemberProperty::Private(private) = &member.property
6151                    && let Some(name) = private_name(self.file, private)
6152                {
6153                    self.use_name(&name);
6154                }
6155            }
6156            Expression::Await(await_expression) => self.scan_expression(&await_expression.argument),
6157            Expression::Yield(yield_expression) => {
6158                if let Some(argument) = &yield_expression.argument {
6159                    self.scan_expression(argument);
6160                }
6161            }
6162            Expression::Unary(unary) => self.scan_expression(&unary.argument),
6163            Expression::Update(update) => self.scan_assignment_target(&update.argument),
6164            Expression::Binary(binary) => {
6165                self.scan_expression(&binary.left);
6166                self.scan_expression(&binary.right);
6167            }
6168            Expression::Logical(logical) => {
6169                self.scan_expression(&logical.left);
6170                self.scan_expression(&logical.right);
6171            }
6172            Expression::Conditional(conditional) => {
6173                self.scan_expression(&conditional.test);
6174                self.scan_expression(&conditional.consequent);
6175                self.scan_expression(&conditional.alternate);
6176            }
6177            Expression::Assignment(assignment) => {
6178                self.scan_expression(&assignment.right);
6179                self.scan_assignment_target(&assignment.left);
6180            }
6181            Expression::Sequence(sequence) => {
6182                for expression in &sequence.expressions {
6183                    self.scan_expression(expression);
6184                }
6185            }
6186            Expression::Parenthesized(inner)
6187            | Expression::NonNull(crate::syntax::NonNullExpression { expression: inner }) => {
6188                self.scan_expression(inner);
6189            }
6190            Expression::As(expression) => self.scan_expression(&expression.expression),
6191            Expression::Satisfies(expression) => self.scan_expression(&expression.expression),
6192            Expression::TypeAssertion(expression) => self.scan_expression(&expression.expression),
6193            Expression::Import(import) => {
6194                self.scan_expression(&import.source);
6195                if let Some(options) = &import.options {
6196                    self.scan_expression(options);
6197                }
6198            }
6199            Expression::Missing(_) => {}
6200        }
6201    }
6202
6203    fn scan_assignment_target(&mut self, target: &AssignmentTargetNode) {
6204        match target.data() {
6205            AssignmentTarget::Identifier(identifier) => {
6206                if let Some(name) = identifier_name(self.file, identifier) {
6207                    self.use_name(&name);
6208                }
6209            }
6210            AssignmentTarget::Member(member) => {
6211                self.scan_expression(&member.object);
6212                if let MemberProperty::Computed(expression) = &member.property {
6213                    self.scan_expression(expression);
6214                }
6215                if let MemberProperty::Private(private) = &member.property
6216                    && let Some(name) = private_name(self.file, private)
6217                {
6218                    self.use_name(&name);
6219                }
6220            }
6221            AssignmentTarget::Object(object) => {
6222                for property in &object.properties {
6223                    if let PropertyName::Computed(key) = &property.name {
6224                        self.scan_expression(key);
6225                    }
6226                    if let Some(initializer) = &property.initializer {
6227                        self.scan_expression(initializer);
6228                    }
6229                    self.scan_assignment_target(&property.target);
6230                }
6231            }
6232            AssignmentTarget::Array(array) => {
6233                for element in &array.elements {
6234                    if let AssignmentArrayElement::Target(inner) = element {
6235                        self.scan_assignment_target(inner);
6236                    }
6237                }
6238            }
6239            AssignmentTarget::Missing(_) => {}
6240        }
6241    }
6242}
6243
6244/// The `for` head range used for the back-edge jump's diagnostic anchor.
6245fn head_range(for_statement: &ForStatement) -> TextRange {
6246    for_statement
6247        .test
6248        .as_ref()
6249        .map_or_else(zero_range, |test| test.range())
6250}
6251
6252fn collect_immediate_declarations<'a>(
6253    file: &SourceFile,
6254    statements: &'a [Stmt],
6255) -> Vec<ImmediateDeclaration<'a>> {
6256    let mut declarations = Vec::new();
6257    for statement in statements {
6258        collect_immediate_declaration(file, statement, &mut declarations);
6259    }
6260    declarations
6261}
6262
6263fn collect_immediate_declaration<'a>(
6264    file: &SourceFile,
6265    statement: &'a Stmt,
6266    declarations: &mut Vec<ImmediateDeclaration<'a>>,
6267) {
6268    match statement.data() {
6269        Statement::Variable(declaration)
6270            if matches!(declaration.kind, VariableKind::Let | VariableKind::Const) =>
6271        {
6272            for declarator in &declaration.declarations {
6273                collect_pattern_declarations(file, &declarator.data().binding, declarations);
6274            }
6275        }
6276        Statement::Function(declaration) => {
6277            let function = &declaration.function;
6278            if function.body.is_some()
6279                && let Some(identifier) = &function.name
6280                && let Some(name) = identifier_name(file, identifier)
6281            {
6282                declarations.push(ImmediateDeclaration {
6283                    name,
6284                    site: binding_site(identifier.range()),
6285                    range: statement.range(),
6286                    kind: ImmediateDeclarationKind::Function(function),
6287                });
6288            }
6289        }
6290        Statement::Class(class) => {
6291            if let Some(identifier) = &class.name
6292                && let Some(name) = identifier_name(file, identifier)
6293            {
6294                declarations.push(ImmediateDeclaration {
6295                    name,
6296                    site: binding_site(identifier.range()),
6297                    range: identifier.range(),
6298                    kind: ImmediateDeclarationKind::Lexical,
6299                });
6300            }
6301        }
6302        Statement::Export(ExportDeclaration::Named(ExportNamedDeclaration::Declaration(
6303            declaration,
6304        ))) => collect_immediate_declaration(file, declaration, declarations),
6305        Statement::Export(ExportDeclaration::Default(default)) => match &default.value {
6306            ExportDefaultValue::Function(function) => {
6307                if function.body.is_some()
6308                    && let Some(identifier) = &function.name
6309                    && let Some(name) = identifier_name(file, identifier)
6310                {
6311                    declarations.push(ImmediateDeclaration {
6312                        name,
6313                        site: binding_site(identifier.range()),
6314                        range: statement.range(),
6315                        kind: ImmediateDeclarationKind::Function(function),
6316                    });
6317                }
6318            }
6319            ExportDefaultValue::Class(class) => {
6320                if let Some(identifier) = &class.name
6321                    && let Some(name) = identifier_name(file, identifier)
6322                {
6323                    declarations.push(ImmediateDeclaration {
6324                        name,
6325                        site: binding_site(identifier.range()),
6326                        range: identifier.range(),
6327                        kind: ImmediateDeclarationKind::Lexical,
6328                    });
6329                }
6330            }
6331            _ => {}
6332        },
6333        _ => {}
6334    }
6335}
6336
6337fn collect_pattern_declarations<'a>(
6338    file: &SourceFile,
6339    pattern: &'a Pattern,
6340    declarations: &mut Vec<ImmediateDeclaration<'a>>,
6341) {
6342    match pattern.data() {
6343        BindingPattern::Identifier(identifier) => {
6344            if let Some(name) = identifier_name(file, identifier) {
6345                declarations.push(ImmediateDeclaration {
6346                    name,
6347                    site: binding_site(identifier.range()),
6348                    range: identifier.range(),
6349                    kind: ImmediateDeclarationKind::Lexical,
6350                });
6351            }
6352        }
6353        BindingPattern::Object(object) => {
6354            for property in &object.properties {
6355                collect_pattern_declarations(file, &property.binding, declarations);
6356            }
6357        }
6358        BindingPattern::Array(array) => {
6359            for element in &array.elements {
6360                if let ArrayBindingElement::Binding(binding) = element {
6361                    collect_pattern_declarations(file, binding, declarations);
6362                }
6363            }
6364        }
6365        BindingPattern::Assignment(assignment) => {
6366            collect_pattern_declarations(file, &assignment.left, declarations);
6367        }
6368        BindingPattern::Rest(rest) => {
6369            collect_pattern_declarations(file, &rest.argument, declarations);
6370        }
6371        BindingPattern::Missing(_) => {}
6372    }
6373}
6374
6375/// The runtime names a statement declares (for `export` linkage).
6376pub(crate) fn declared_names(file: &SourceFile, statement: &Stmt) -> Vec<String> {
6377    let mut names = Vec::new();
6378    match statement.data() {
6379        Statement::Variable(declaration) => {
6380            for declarator in &declaration.declarations {
6381                collect_pattern_names(file, &declarator.data().binding, &mut names);
6382            }
6383        }
6384        Statement::Function(declaration) => {
6385            if let Some(name) = &declaration.function.name
6386                && let Some(text) = identifier_name(file, name)
6387            {
6388                names.push(text);
6389            }
6390        }
6391        Statement::Class(class) => {
6392            if let Some(name) = &class.name
6393                && let Some(text) = identifier_name(file, name)
6394            {
6395                names.push(text);
6396            }
6397        }
6398        _ => {}
6399    }
6400    names
6401}
6402
6403/// Collects every `var`-scoped binding name in a statement list, not descending
6404/// into nested function or class bodies (which have their own `var` scope).
6405pub(crate) fn collect_var_names(file: &SourceFile, statements: &[Stmt], names: &mut Vec<String>) {
6406    for statement in statements {
6407        collect_var_names_stmt(file, statement, names);
6408    }
6409}
6410
6411fn collect_var_names_stmt(file: &SourceFile, statement: &Stmt, names: &mut Vec<String>) {
6412    match statement.data() {
6413        Statement::Variable(declaration) if matches!(declaration.kind, VariableKind::Var) => {
6414            for declarator in &declaration.declarations {
6415                collect_pattern_names(file, &declarator.data().binding, names);
6416            }
6417        }
6418        Statement::Block(block) => collect_var_names(file, &block.data().statements, names),
6419        Statement::If(statement) => {
6420            collect_var_names_stmt(file, &statement.consequent, names);
6421            if let Some(alternate) = &statement.alternate {
6422                collect_var_names_stmt(file, alternate, names);
6423            }
6424        }
6425        Statement::For(statement) => {
6426            if let Some(ForInitializer::Variable(declaration)) = &statement.initializer
6427                && matches!(declaration.kind, VariableKind::Var)
6428            {
6429                for declarator in &declaration.declarations {
6430                    collect_pattern_names(file, &declarator.data().binding, names);
6431                }
6432            }
6433            collect_var_names_stmt(file, &statement.body, names);
6434        }
6435        Statement::ForIn(statement) => {
6436            collect_for_binding_var(file, &statement.binding, names);
6437            collect_var_names_stmt(file, &statement.body, names);
6438        }
6439        Statement::ForOf(statement) => {
6440            collect_for_binding_var(file, &statement.binding, names);
6441            collect_var_names_stmt(file, &statement.body, names);
6442        }
6443        Statement::While(statement) => collect_var_names_stmt(file, &statement.body, names),
6444        Statement::DoWhile(statement) => collect_var_names_stmt(file, &statement.body, names),
6445        Statement::Switch(statement) => {
6446            for case in &statement.cases {
6447                for statement in &case.data().consequent {
6448                    collect_var_names_stmt(file, statement, names);
6449                }
6450            }
6451        }
6452        Statement::Try(statement) => {
6453            for statement in &statement.block.data().statements {
6454                collect_var_names_stmt(file, statement, names);
6455            }
6456            if let Some(handler) = &statement.handler {
6457                for statement in &handler.data().body.data().statements {
6458                    collect_var_names_stmt(file, statement, names);
6459                }
6460            }
6461            if let Some(finalizer) = &statement.finalizer {
6462                for statement in &finalizer.data().statements {
6463                    collect_var_names_stmt(file, statement, names);
6464                }
6465            }
6466        }
6467        Statement::Labeled(statement) => collect_var_names_stmt(file, &statement.body, names),
6468        Statement::Export(ExportDeclaration::Named(ExportNamedDeclaration::Declaration(
6469            statement,
6470        ))) => collect_var_names_stmt(file, statement, names),
6471        _ => {}
6472    }
6473}
6474
6475fn collect_for_binding_var(file: &SourceFile, binding: &ForBinding, names: &mut Vec<String>) {
6476    if let ForBinding::Variable(declaration) = binding
6477        && matches!(declaration.kind, VariableKind::Var)
6478    {
6479        for declarator in &declaration.declarations {
6480            collect_pattern_names(file, &declarator.data().binding, names);
6481        }
6482    }
6483}
6484
6485pub(crate) fn collect_pattern_names(file: &SourceFile, pattern: &Pattern, names: &mut Vec<String>) {
6486    match pattern.data() {
6487        BindingPattern::Identifier(identifier) => {
6488            if let Some(text) = identifier_name(file, identifier) {
6489                names.push(text);
6490            }
6491        }
6492        BindingPattern::Object(object) => {
6493            for property in &object.properties {
6494                collect_pattern_names(file, &property.binding, names);
6495            }
6496        }
6497        BindingPattern::Array(array) => {
6498            for element in &array.elements {
6499                if let ArrayBindingElement::Binding(inner) = element {
6500                    collect_pattern_names(file, inner, names);
6501                }
6502            }
6503        }
6504        BindingPattern::Rest(rest) => collect_pattern_names(file, &rest.argument, names),
6505        BindingPattern::Assignment(assignment) => {
6506            collect_pattern_names(file, &assignment.left, names);
6507        }
6508        BindingPattern::Missing(_) => {}
6509    }
6510}
6511
6512/// The raw text of an identifier node, or `None` for a missing token.
6513fn identifier_name(file: &SourceFile, identifier: &IdentifierNode) -> Option<String> {
6514    let token = identifier.data().token();
6515    if token.is_missing() {
6516        return None;
6517    }
6518    file.token_text(token).map(str::to_owned)
6519}
6520
6521/// The raw text of a private identifier (`#name`), or `None` for a missing
6522/// token.
6523fn private_name(file: &SourceFile, private: &PrivateIdentifierNode) -> Option<String> {
6524    let token = private.data().token();
6525    if token.is_missing() {
6526        return None;
6527    }
6528    file.token_text(token).map(str::to_owned)
6529}
6530
6531/// Maps a source binary operator to its bytecode counterpart.
6532fn map_binary_operator(operator: BinaryOperator) -> BinaryOp {
6533    match operator {
6534        BinaryOperator::Add => BinaryOp::Add,
6535        BinaryOperator::Subtract => BinaryOp::Subtract,
6536        BinaryOperator::Multiply => BinaryOp::Multiply,
6537        BinaryOperator::Divide => BinaryOp::Divide,
6538        BinaryOperator::Remainder => BinaryOp::Remainder,
6539        BinaryOperator::Exponentiate => BinaryOp::Exponent,
6540        BinaryOperator::BitAnd => BinaryOp::BitAnd,
6541        BinaryOperator::BitOr => BinaryOp::BitOr,
6542        BinaryOperator::BitXor => BinaryOp::BitXor,
6543        BinaryOperator::LeftShift => BinaryOp::ShiftLeft,
6544        BinaryOperator::SignedRightShift => BinaryOp::ShiftRight,
6545        BinaryOperator::UnsignedRightShift => BinaryOp::UnsignedShiftRight,
6546        BinaryOperator::Equal => BinaryOp::Equal,
6547        BinaryOperator::NotEqual => BinaryOp::NotEqual,
6548        BinaryOperator::StrictEqual => BinaryOp::StrictEqual,
6549        BinaryOperator::StrictNotEqual => BinaryOp::StrictNotEqual,
6550        BinaryOperator::LessThan => BinaryOp::LessThan,
6551        BinaryOperator::LessThanOrEqual => BinaryOp::LessThanOrEqual,
6552        BinaryOperator::GreaterThan => BinaryOp::GreaterThan,
6553        BinaryOperator::GreaterThanOrEqual => BinaryOp::GreaterThanOrEqual,
6554        BinaryOperator::Instanceof => BinaryOp::InstanceOf,
6555        BinaryOperator::In => BinaryOp::In,
6556    }
6557}
6558
6559/// A compound assignment's underlying operation.
6560enum CompoundOp {
6561    Arithmetic(BinaryOp),
6562    Logical(LogicalOperator),
6563}
6564
6565/// The operation a compound assignment applies, or `None` for plain `=`.
6566fn compound_operator(operator: AssignmentOperator) -> Option<CompoundOp> {
6567    let op = match operator {
6568        AssignmentOperator::Assign => return None,
6569        AssignmentOperator::AddAssign => BinaryOp::Add,
6570        AssignmentOperator::SubtractAssign => BinaryOp::Subtract,
6571        AssignmentOperator::MultiplyAssign => BinaryOp::Multiply,
6572        AssignmentOperator::DivideAssign => BinaryOp::Divide,
6573        AssignmentOperator::RemainderAssign => BinaryOp::Remainder,
6574        AssignmentOperator::ExponentiateAssign => BinaryOp::Exponent,
6575        AssignmentOperator::LeftShiftAssign => BinaryOp::ShiftLeft,
6576        AssignmentOperator::SignedRightShiftAssign => BinaryOp::ShiftRight,
6577        AssignmentOperator::UnsignedRightShiftAssign => BinaryOp::UnsignedShiftRight,
6578        AssignmentOperator::BitAndAssign => BinaryOp::BitAnd,
6579        AssignmentOperator::BitOrAssign => BinaryOp::BitOr,
6580        AssignmentOperator::BitXorAssign => BinaryOp::BitXor,
6581        AssignmentOperator::LogicalAndAssign => {
6582            return Some(CompoundOp::Logical(LogicalOperator::And));
6583        }
6584        AssignmentOperator::LogicalOrAssign => {
6585            return Some(CompoundOp::Logical(LogicalOperator::Or));
6586        }
6587        AssignmentOperator::NullishAssign => {
6588            return Some(CompoundOp::Logical(LogicalOperator::Nullish));
6589        }
6590    };
6591    Some(CompoundOp::Arithmetic(op))
6592}
6593
6594/// The canonical integer `ToString` of a numeric property key.
6595fn numeric_key_text(
6596    context: &FunctionContext<'_>,
6597    number: &NumericLiteralNode,
6598) -> Result<String, LowerError> {
6599    let range = number.range();
6600    let token = number.data().token();
6601    if token.is_missing() {
6602        return Err(context.missing(range, NodeKind::NumericLiteral));
6603    }
6604    let lexeme = context
6605        .file
6606        .token_text(token)
6607        .filter(|text| !text.is_empty())
6608        .ok_or_else(|| context.error(range, LowerErrorKind::InvalidNumericLiteral))?;
6609    let value = cook_number(lexeme)
6610        .ok_or_else(|| context.error(range, LowerErrorKind::InvalidNumericLiteral))?;
6611    if value.fract() == 0.0 && value.is_finite() && (0.0..=9_007_199_254_740_991.0).contains(&value)
6612    {
6613        Ok(format!("{}", value as u64))
6614    } else {
6615        // Non-integer numeric keys stringify by their number value.
6616        Ok(format_number_key(value))
6617    }
6618}
6619
6620fn format_number_key(value: f64) -> String {
6621    if value == 0.0 {
6622        "0".to_owned()
6623    } else {
6624        let text = format!("{value}");
6625        text
6626    }
6627}
6628
6629/// Trims the delimiter characters from a template element's raw lexeme.
6630fn trim_template_delimiters(text: &str, kind: TokenKind) -> &str {
6631    let (head, tail): (usize, usize) = match kind {
6632        // `` `...` `` -> strip 1 and 1.
6633        TokenKind::NoSubstitutionTemplate => (1, 1),
6634        // `` `...${ `` -> strip 1 and 2.
6635        TokenKind::TemplateHead => (1, 2),
6636        // `}...${` -> strip 1 and 2.
6637        TokenKind::TemplateMiddle => (1, 2),
6638        // `}...` `` -> strip 1 and 1.
6639        TokenKind::TemplateTail => (1, 1),
6640        _ => (0, 0),
6641    };
6642    let bytes = text.len();
6643    if bytes < head + tail {
6644        return "";
6645    }
6646    &text[head..bytes - tail]
6647}
6648
6649/// Cooks JavaScript escape sequences in a string/template interior. Malformed
6650/// escapes degrade to their literal characters rather than failing.
6651fn cook_escapes(input: &str) -> EcmaString {
6652    if !input.contains('\\') {
6653        return EcmaString::from_utf8(input);
6654    }
6655    let mut output = EcmaStringBuilder::with_capacity(input.encode_utf16().count());
6656    let mut chars = input.chars().peekable();
6657    while let Some(ch) = chars.next() {
6658        if ch != '\\' {
6659            output
6660                .push_code_point(u32::from(ch))
6661                .expect("a Rust char is a Unicode scalar");
6662            continue;
6663        }
6664        let Some(escape) = chars.next() else {
6665            output.push_unit(b'\\'.into());
6666            break;
6667        };
6668        match escape {
6669            'n' => output.push_unit(b'\n'.into()),
6670            't' => output.push_unit(b'\t'.into()),
6671            'r' => output.push_unit(b'\r'.into()),
6672            'b' => output.push_unit(0x0008),
6673            'f' => output.push_unit(0x000C),
6674            'v' => output.push_unit(0x000B),
6675            '0' if !chars.peek().is_some_and(|c| c.is_ascii_digit()) => output.push_unit(0),
6676            '\n' => {}
6677            '\r' => {
6678                if chars.peek() == Some(&'\n') {
6679                    chars.next();
6680                }
6681            }
6682            'x' => {
6683                let hi = chars.next();
6684                let lo = chars.next();
6685                if let (Some(hi), Some(lo)) = (hi, lo)
6686                    && let (Some(h), Some(l)) = (hi.to_digit(16), lo.to_digit(16))
6687                {
6688                    output.push_unit((h * 16 + l) as u16);
6689                } else {
6690                    output.push_unit(b'x'.into());
6691                }
6692            }
6693            'u' => cook_unicode_escape(&mut chars, &mut output),
6694            other => output
6695                .push_code_point(u32::from(other))
6696                .expect("a Rust char is a Unicode scalar"),
6697        }
6698    }
6699    output.finish()
6700}
6701
6702fn cook_unicode_escape(
6703    chars: &mut std::iter::Peekable<std::str::Chars<'_>>,
6704    output: &mut EcmaStringBuilder,
6705) {
6706    if chars.peek() == Some(&'{') {
6707        chars.next();
6708        let mut value = 0u32;
6709        let mut any = false;
6710        while let Some(&c) = chars.peek() {
6711            if c == '}' {
6712                chars.next();
6713                break;
6714            }
6715            let Some(digit) = c.to_digit(16) else { break };
6716            value = value.saturating_mul(16).saturating_add(digit);
6717            any = true;
6718            chars.next();
6719        }
6720        if any && value <= 0x10_FFFF {
6721            output
6722                .push_code_point(value)
6723                .expect("a bounded code point is representable");
6724        }
6725        return;
6726    }
6727    let mut value = 0u16;
6728    let mut count = 0;
6729    while count < 4 {
6730        let Some(&c) = chars.peek() else { break };
6731        let Some(digit) = c.to_digit(16) else { break };
6732        value = value * 16 + digit as u16;
6733        chars.next();
6734        count += 1;
6735    }
6736    if count == 4 {
6737        output.push_unit(value);
6738    } else {
6739        output.push_unit(b'u'.into());
6740    }
6741}
6742
6743/// Splits a regex literal `/pattern/flags` into `(pattern, flags)`.
6744fn split_regex(lexeme: &str) -> Option<(String, String)> {
6745    let lexeme = lexeme.strip_prefix('/')?;
6746    let last_slash = lexeme.rfind('/')?;
6747    let pattern = &lexeme[..last_slash];
6748    let flags = &lexeme[last_slash + 1..];
6749    Some((pattern.to_owned(), flags.to_owned()))
6750}
6751
6752/// Canonicalizes a decimal bigint lexeme to canonical decimal text.
6753fn canonical_bigint_text(lexeme: &str) -> Option<String> {
6754    let digits = lexeme.strip_suffix('n')?;
6755    if digits.is_empty() {
6756        return None;
6757    }
6758    if digits.len() >= 2 {
6759        let prefix = &digits[..2];
6760        if matches!(prefix, "0x" | "0X" | "0o" | "0O" | "0b" | "0B") {
6761            return None;
6762        }
6763    }
6764    let cleaned: String = digits.chars().filter(|c| *c != '_').collect();
6765    if cleaned.is_empty() || !cleaned.chars().all(|c| c.is_ascii_digit()) {
6766        return None;
6767    }
6768    let trimmed = cleaned.trim_start_matches('0');
6769    if trimmed.is_empty() {
6770        Some("0".to_owned())
6771    } else {
6772        Some(trimmed.to_owned())
6773    }
6774}
6775
6776/// Cooks a scanned numeric lexeme into its ECMAScript number value.
6777fn cook_number(lexeme: &str) -> Option<f64> {
6778    let cleaned: String = lexeme.chars().filter(|c| *c != '_').collect();
6779    if let Some(rest) = cleaned
6780        .strip_prefix("0x")
6781        .or_else(|| cleaned.strip_prefix("0X"))
6782    {
6783        return radix_value(rest, 16);
6784    }
6785    if let Some(rest) = cleaned
6786        .strip_prefix("0o")
6787        .or_else(|| cleaned.strip_prefix("0O"))
6788    {
6789        return radix_value(rest, 8);
6790    }
6791    if let Some(rest) = cleaned
6792        .strip_prefix("0b")
6793        .or_else(|| cleaned.strip_prefix("0B"))
6794    {
6795        return radix_value(rest, 2);
6796    }
6797    cleaned.parse::<f64>().ok()
6798}
6799
6800fn radix_value(digits: &str, radix: u32) -> Option<f64> {
6801    if digits.is_empty() {
6802        return None;
6803    }
6804    let mut value = 0.0_f64;
6805    for ch in digits.chars() {
6806        let digit = ch.to_digit(radix)?;
6807        value = value * f64::from(radix) + f64::from(digit);
6808    }
6809    Some(value)
6810}
6811
6812/// Chooses the canonical pool representation for one number value.
6813fn number_constant(value: f64) -> Constant {
6814    if value.fract() == 0.0
6815        && value.is_finite()
6816        && (f64::from(i32::MIN)..=f64::from(i32::MAX)).contains(&value)
6817        && !(value == 0.0 && value.is_sign_negative())
6818    {
6819        Constant::Int32(value as i32)
6820    } else {
6821        Constant::Number(NumberBits::from_f64(value))
6822    }
6823}
6824
6825#[cfg(test)]
6826mod tests {
6827    use std::collections::BTreeSet;
6828    use std::fs;
6829    use std::path::{Path, PathBuf};
6830    use std::sync::Arc;
6831
6832    use super::{LowerErrorKind, LowerOptions, UnsupportedConstruct, cook_escapes, lower};
6833    use crate::parser::parse;
6834    use crate::scanner::scan;
6835    use crate::source::{ScriptKind, SourceId, SourceText};
6836
6837    fn repository_root() -> PathBuf {
6838        Path::new(env!("CARGO_MANIFEST_DIR"))
6839            .join("../..")
6840            .canonicalize()
6841            .expect("repository root is readable")
6842    }
6843
6844    fn script_kind(path: &str) -> ScriptKind {
6845        if path.ends_with(".d.ts")
6846            || path.ends_with(".ts")
6847            || path.ends_with(".mts")
6848            || path.ends_with(".cts")
6849        {
6850            ScriptKind::TypeScript
6851        } else if path.ends_with(".tsx") {
6852            ScriptKind::TypeScriptReact
6853        } else if path.ends_with(".js") || path.ends_with(".mjs") || path.ends_with(".cjs") {
6854            ScriptKind::JavaScript
6855        } else if path.ends_with(".jsx") {
6856            ScriptKind::JavaScriptReact
6857        } else {
6858            panic!("declared corpus source has unsupported extension: {path}");
6859        }
6860    }
6861
6862    /// Discovers exactly the 20 manifest entrypoints plus 43 project sources.
6863    /// The checked corpus format uses one quoted `entrypoint` per manifest
6864    /// project and one quoted-string `source_files` array per project spec.
6865    fn declared_corpus_sources(root: &Path) -> Vec<String> {
6866        let manifest = fs::read_to_string(root.join("corpus/manifest.toml"))
6867            .expect("corpus manifest is readable");
6868        let mut sources = BTreeSet::new();
6869        for line in manifest.lines() {
6870            if let Some(value) = quoted_value(line, "entrypoint") {
6871                sources.insert(value);
6872            }
6873        }
6874        let specs = root.join("corpus/specs");
6875        for entry in fs::read_dir(&specs).expect("corpus specs directory is readable") {
6876            let path = entry.expect("spec directory entry").path();
6877            if path.extension().and_then(|extension| extension.to_str()) != Some("toml") {
6878                continue;
6879            }
6880            let text = fs::read_to_string(&path).expect("corpus spec is UTF-8");
6881            let start = text
6882                .find("source_files")
6883                .unwrap_or_else(|| panic!("{} has no source_files", path.display()));
6884            let array = &text[start..];
6885            let open = array
6886                .find('[')
6887                .expect("source_files has an opening bracket");
6888            let close = array[open + 1..]
6889                .find(']')
6890                .map(|index| open + 1 + index)
6891                .expect("source_files has a closing bracket");
6892            let contents = &array[open + 1..close];
6893            for item in contents.split(',') {
6894                let item = item.trim();
6895                if item.is_empty() {
6896                    continue;
6897                }
6898                let value = item
6899                    .strip_prefix('"')
6900                    .and_then(|item| item.strip_suffix('"'))
6901                    .unwrap_or_else(|| {
6902                        panic!(
6903                            "{} has malformed source_files item `{item}`",
6904                            path.display()
6905                        )
6906                    });
6907                sources.insert(value.to_owned());
6908            }
6909        }
6910        assert_eq!(
6911            sources.len(),
6912            63,
6913            "the checked corpus contract is 63 sources"
6914        );
6915        sources.into_iter().collect()
6916    }
6917
6918    fn quoted_value(line: &str, key: &str) -> Option<String> {
6919        let line = line.trim();
6920        let value = line
6921            .strip_prefix(key)?
6922            .trim_start()
6923            .strip_prefix('=')?
6924            .trim();
6925        Some(value.strip_prefix('"')?.strip_suffix('"')?.to_owned())
6926    }
6927
6928    #[test]
6929    fn cooking_preserves_lone_surrogate_units() {
6930        assert_eq!(cook_escapes("\\uD800").as_units(), [0xD800]);
6931        assert_eq!(cook_escapes("\\uD83D\\uDE03").as_units(), [0xD83D, 0xDE03]);
6932        assert_eq!(cook_escapes("\\u{1F603}").as_units(), [0xD83D, 0xDE03]);
6933    }
6934
6935    #[test]
6936    fn all_declared_corpus_sources_lower_to_verified_modules() {
6937        let root = repository_root();
6938        let sources = declared_corpus_sources(&root);
6939        let mut failures = Vec::new();
6940        for (index, relative) in sources.iter().enumerate() {
6941            let path = root.join(relative);
6942            let text = fs::read_to_string(&path)
6943                .unwrap_or_else(|error| panic!("{} is unreadable: {error}", path.display()));
6944            let source = Arc::new(SourceText::new(text));
6945            let scanned = scan(SourceId::new(index as u32), script_kind(relative), source);
6946            let parsed = parse(scanned);
6947            match lower(
6948                parsed.product(),
6949                LowerOptions {
6950                    javascript_compatibility: true,
6951                },
6952            ) {
6953                Ok(module) => {
6954                    assert!(
6955                        module.certificate(module.entry()).is_some(),
6956                        "{relative}: entry is verified"
6957                    );
6958                }
6959                Err(error) => failures.push(format!("{relative}: {error}")),
6960            }
6961        }
6962        assert!(
6963            failures.is_empty(),
6964            "{}/{} declared corpus sources failed lowering:\n{}",
6965            failures.len(),
6966            sources.len(),
6967            failures.join("\n")
6968        );
6969    }
6970
6971    use bamts_bytecode::{
6972        BinaryOp, Constant, DecodeLimits, Instruction, Module, Register, Verified, decode_verified,
6973    };
6974
6975    fn lower_js(src: &str) -> Module<Verified> {
6976        let source = Arc::new(SourceText::new(src.to_owned()));
6977        let scanned = scan(SourceId::new(0), ScriptKind::TypeScript, source);
6978        let parsed = parse(scanned);
6979        lower(
6980            parsed.product(),
6981            LowerOptions {
6982                javascript_compatibility: true,
6983            },
6984        )
6985        .expect("snippet lowers to a verified module")
6986    }
6987
6988    #[test]
6989    fn lowering_preserves_lone_surrogate_escapes() {
6990        let module = lower_js("const lone = '\\uD800'; const face = '\\u{1F603}';");
6991        let strings: Vec<_> = module
6992            .constants()
6993            .iter()
6994            .filter_map(|constant| match constant {
6995                Constant::String(value) => Some(value.as_units()),
6996                _ => None,
6997            })
6998            .collect();
6999        assert!(strings.iter().any(|units| *units == [0xD800]));
7000        assert!(strings.iter().any(|units| *units == [0xD83D, 0xDE03]));
7001    }
7002
7003    fn any_instruction(
7004        module: &Module<Verified>,
7005        predicate: impl Fn(&Instruction) -> bool,
7006    ) -> bool {
7007        module
7008            .functions()
7009            .iter()
7010            .flat_map(|function| function.code())
7011            .any(predicate)
7012    }
7013
7014    fn max_capture_count(module: &Module<Verified>) -> u32 {
7015        module
7016            .functions()
7017            .iter()
7018            .map(|function| function.capture_count())
7019            .max()
7020            .unwrap_or(0)
7021    }
7022
7023    fn created_cell_used_as_capture(code: &[Instruction]) -> Option<Register> {
7024        code.iter().find_map(|instruction| {
7025            let Instruction::ArrayPush { array, value } = instruction else {
7026                return None;
7027            };
7028            if array == value {
7029                return None;
7030            }
7031            code.iter()
7032                .any(|candidate| {
7033                    matches!(
7034                        candidate,
7035                        Instruction::CreateArray { dst } | Instruction::CreateCell { dst }
7036                            if dst == value
7037                    )
7038                })
7039                .then_some(*value)
7040        })
7041    }
7042
7043    fn cell_capture_pushes(code: &[Instruction], cell: Register) -> usize {
7044        code.iter()
7045            .filter(|instruction| {
7046                matches!(instruction, Instruction::ArrayPush { value, .. } if *value == cell)
7047            })
7048            .count()
7049    }
7050
7051    fn cell_allocations(code: &[Instruction], cell: Register) -> usize {
7052        code.iter()
7053            .filter(|instruction| {
7054                matches!(
7055                    instruction,
7056                    Instruction::CreateArray { dst } | Instruction::CreateCell { dst }
7057                        if *dst == cell
7058                )
7059            })
7060            .count()
7061    }
7062
7063    fn dormant_promotion_cell_blocks(code: &[Instruction]) -> usize {
7064        code.iter()
7065            .enumerate()
7066            .filter(|(entry, instruction)| {
7067                let Instruction::CreateArray { dst } = instruction else {
7068                    return false;
7069                };
7070                matches!(
7071                    code.get((*entry).saturating_sub(1)),
7072                    Some(Instruction::Jump { target }) if target.get() as usize == *entry + 2
7073                ) && matches!(
7074                    code.get(*entry + 1),
7075                    Some(Instruction::ArrayPush { array, value }) if array == dst && value == dst
7076                ) && !code.iter().any(|instruction| {
7077                    matches!(
7078                        instruction,
7079                        Instruction::Jump { target }
7080                            | Instruction::JumpIfTrue { target, .. }
7081                            | Instruction::JumpIfFalse { target, .. }
7082                            if target.get() as usize == *entry
7083                    )
7084                })
7085            })
7086            .count()
7087    }
7088
7089    #[test]
7090    fn outer_write_is_observed_by_a_captured_read() {
7091        let module = lower_js(
7092            "function outer() { let value = 0; const read = () => value; value = 1; return read; }",
7093        );
7094        let code = module
7095            .functions()
7096            .iter()
7097            .map(|function| function.code())
7098            .find(|code| {
7099                created_cell_used_as_capture(code).is_some()
7100                    && code
7101                        .iter()
7102                        .any(|instruction| matches!(instruction, Instruction::SetProperty { .. }))
7103            })
7104            .expect("outer function owns the promoted binding");
7105        let cell = created_cell_used_as_capture(code).expect("cell is captured");
7106        assert!(code.iter().any(
7107            |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell)
7108        ));
7109        assert!(module.functions().iter().any(|function| {
7110            function.capture_count() == 1
7111                && function.code().iter().any(
7112                    |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7113                )
7114        }));
7115    }
7116
7117    #[test]
7118    fn inner_write_is_observed_by_the_outer_read() {
7119        let module = lower_js(
7120            "function outer() { let value = 0; const write = () => { value = 1; }; write(); return value; }",
7121        );
7122        assert!(module.functions().iter().any(|function| {
7123            function.capture_count() == 1
7124                && function.code().iter().any(
7125                    |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == Register::new(0))
7126                )
7127        }));
7128        assert!(module.functions().iter().any(|function| {
7129            let code = function.code();
7130            created_cell_used_as_capture(code).is_some_and(|cell| {
7131                code.iter().any(
7132                    |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == cell)
7133                )
7134            })
7135        }));
7136    }
7137
7138    #[test]
7139    fn assigned_local_cell_dominates_a_later_conditional_closure() {
7140        let module = lower_js(
7141            "function outer(flag) { let value = 0; value = 1; let read; if (flag) read = () => value; return value; }",
7142        );
7143        let code = module
7144            .functions()
7145            .iter()
7146            .map(|function| function.code())
7147            .find(|code| created_cell_used_as_capture(code).is_some())
7148            .expect("outer function owns the captured cell");
7149        let cell = created_cell_used_as_capture(code).expect("cell is captured");
7150        let allocation = code
7151            .iter()
7152            .position(
7153                |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7154            )
7155            .expect("cell allocation is present");
7156        let conditional = code
7157            .iter()
7158            .position(|instruction| matches!(instruction, Instruction::JumpIfFalse { .. }))
7159            .expect("if statement branches");
7160        assert!(allocation < conditional);
7161        assert!(code[..conditional].iter().any(
7162            |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell)
7163        ));
7164    }
7165
7166    #[test]
7167    fn assigned_parameter_uses_one_cell_before_later_method_capture() {
7168        let module = lower_js(
7169            "function mitt(all) { all = all || new Map(); return { read() { return all; } }; }",
7170        );
7171        let code = module
7172            .functions()
7173            .iter()
7174            .map(|function| function.code())
7175            .find(|code| created_cell_used_as_capture(code).is_some())
7176            .expect("mitt function owns the parameter cell");
7177        let cell = created_cell_used_as_capture(code).expect("parameter cell is captured");
7178        let allocation = code
7179            .iter()
7180            .position(
7181                |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7182            )
7183            .expect("parameter cell is allocated");
7184        let assignment = code
7185            .iter()
7186            .position(
7187                |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell),
7188            )
7189            .expect("parameter assignment stores through its cell");
7190        let closure = code
7191            .iter()
7192            .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7193            .expect("method closure is created");
7194        assert!(allocation < assignment && assignment < closure);
7195        assert_eq!(cell_allocations(code, cell), 1);
7196    }
7197
7198    #[test]
7199    fn captured_parameter_cells_precede_default_initializer_closures() {
7200        for source in [
7201            "function f(a = () => a) { return a; }",
7202            "function f(make = () => later, later = 1) { return make; }",
7203            "function f(make = () => rest, ...rest) { return make; }",
7204            "function f({ value = () => value } = {}) { return value; }",
7205        ] {
7206            let module = lower_js(source);
7207            let owner = module
7208                .functions()
7209                .iter()
7210                .find(|function| created_cell_used_as_capture(function.code()).is_some())
7211                .expect("parameter owner materializes a captured default closure");
7212            let code = owner.code();
7213            let cell = created_cell_used_as_capture(code).expect("parameter cell is captured");
7214            let allocation = code
7215                .iter()
7216                .position(
7217                    |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7218                )
7219                .expect("parameter cell is allocated");
7220            let closure = code
7221                .iter()
7222                .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7223                .expect("default initializer creates a closure");
7224            assert!(allocation < closure, "{source}");
7225            assert_eq!(cell_allocations(code, cell), 1, "{source}");
7226        }
7227    }
7228
7229    #[test]
7230    fn declaration_owned_closures_capture_their_predeclared_cells() {
7231        for source in [
7232            "function outer() { const f = () => f; return f; }",
7233            "function outer() { function f() { return f; } return f; }",
7234            "function outer() { class C { self() { return C; } } return C; }",
7235            "function outer() { const { f = () => f } = {}; return f; }",
7236            "function outer() { const f = () => f; { const f = 1; } return f; }",
7237        ] {
7238            let module = lower_js(source);
7239            let owner = module
7240                .functions()
7241                .iter()
7242                .find(|function| created_cell_used_as_capture(function.code()).is_some())
7243                .expect("declaration owner materializes the captured cell");
7244            let code = owner.code();
7245            let cell = created_cell_used_as_capture(code).expect("cell is captured");
7246            let allocation = code
7247                .iter()
7248                .position(
7249                    |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7250                )
7251                .expect("captured cell is allocated");
7252            let closure = code
7253                .iter()
7254                .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7255                .expect("declaration value creates a closure");
7256            assert!(allocation < closure, "{source}");
7257            assert_eq!(cell_allocations(code, cell), 1, "{source}");
7258            assert!(
7259                code[closure..].iter().any(
7260                    |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell)
7261                ),
7262                "the declaration stores its final value into the same cell: {source}"
7263            );
7264        }
7265    }
7266
7267    #[test]
7268    fn safe_uncaptured_lexical_stays_in_a_register() {
7269        let module = lower_js("function outer() { const value = 1; return value; }");
7270        assert!(module.functions().iter().all(|function| {
7271            !function
7272                .code()
7273                .iter()
7274                .any(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7275        }));
7276    }
7277
7278    #[test]
7279    fn captured_later_lexical_is_predeclared_once() {
7280        let module = lower_js(
7281            "function outer() { const read = () => later; const later = 1; return read; }",
7282        );
7283        let owner = module
7284            .functions()
7285            .iter()
7286            .find(|function| {
7287                function
7288                    .code()
7289                    .iter()
7290                    .any(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7291            })
7292            .expect("owner predeclares the later lexical cell");
7293        assert_eq!(
7294            owner
7295                .code()
7296                .iter()
7297                .filter(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7298                .count(),
7299            1
7300        );
7301        let cell = created_cell_used_as_capture(owner.code()).expect("closure captures the cell");
7302        assert_eq!(cell_allocations(owner.code(), cell), 1);
7303    }
7304
7305    #[test]
7306    fn early_closure_read_uses_one_predeclared_cell_then_initializes_it() {
7307        let module = lower_js(
7308            "function outer() { const read = () => later; read(); let later = 1; return read(); }",
7309        );
7310        let code = module
7311            .functions()
7312            .iter()
7313            .map(|function| function.code())
7314            .find(|code| created_cell_used_as_capture(code).is_some())
7315            .expect("outer owns the captured later binding");
7316        let cell = created_cell_used_as_capture(code).expect("later binding is a cell");
7317        let create = code
7318            .iter()
7319            .position(|instruction| matches!(instruction, Instruction::CreateCell { dst } if *dst == cell))
7320            .expect("cell is seeded at scope entry");
7321        let closure = code
7322            .iter()
7323            .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7324            .expect("reader closure is instantiated");
7325        let initialize = code
7326            .iter()
7327            .position(|instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell))
7328            .expect("declaration initializes the cell");
7329        assert!(create < closure && closure < initialize);
7330        assert_eq!(cell_allocations(code, cell), 1);
7331    }
7332
7333    #[test]
7334    fn function_declaration_is_instantiated_once_before_executable_statements() {
7335        let module = lower_js(
7336            "function outer() { return declaredLater(); function declaredLater() { return 2; } }",
7337        );
7338        let code = module
7339            .functions()
7340            .iter()
7341            .map(|function| function.code())
7342            .find(|code| {
7343                code.iter()
7344                    .any(|instruction| matches!(instruction, Instruction::Call { .. }))
7345            })
7346            .expect("outer function calls its declaration");
7347        let closure = code
7348            .iter()
7349            .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7350            .expect("function declaration is instantiated");
7351        let call = code
7352            .iter()
7353            .position(|instruction| matches!(instruction, Instruction::Call { .. }))
7354            .expect("call is emitted");
7355        assert!(closure < call);
7356        assert_eq!(
7357            code.iter()
7358                .filter(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7359                .count(),
7360            1
7361        );
7362    }
7363
7364    #[test]
7365    fn class_heritage_reads_its_own_uninitialized_cell() {
7366        let module = lower_js("function outer() { class C extends C {} return C; }");
7367        let code = module
7368            .functions()
7369            .iter()
7370            .map(|function| function.code())
7371            .find(|code| {
7372                code.iter()
7373                    .any(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7374            })
7375            .expect("class owner predeclares its binding");
7376        let cell = code
7377            .iter()
7378            .find_map(|instruction| match instruction {
7379                Instruction::CreateCell { dst } => Some(*dst),
7380                _ => None,
7381            })
7382            .expect("class cell exists");
7383        let create = code
7384            .iter()
7385            .position(|instruction| matches!(instruction, Instruction::CreateCell { dst } if *dst == cell))
7386            .expect("class cell allocation");
7387        let heritage_read = code
7388            .iter()
7389            .position(|instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == cell))
7390            .expect("heritage reads through the class cell");
7391        let initialize = code
7392            .iter()
7393            .rposition(|instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell))
7394            .expect("class declaration initializes the same cell");
7395        assert!(create < heritage_read && heritage_read < initialize);
7396    }
7397
7398    #[test]
7399    fn same_name_shadow_does_not_overbox_uncaptured_binding() {
7400        let module =
7401            lower_js("function outer() { let value = 1; { let value = 2; } return () => value; }");
7402        let code = module
7403            .functions()
7404            .iter()
7405            .map(|function| function.code())
7406            .find(|code| created_cell_used_as_capture(code).is_some())
7407            .expect("outer binding is captured");
7408        assert_eq!(
7409            code.iter()
7410                .filter(|instruction| matches!(
7411                    instruction,
7412                    Instruction::CreateArray { .. } | Instruction::CreateCell { .. }
7413                ))
7414                .count(),
7415            2,
7416            "only the captured binding cell and closure capture array allocate"
7417        );
7418        assert_eq!(dormant_promotion_cell_blocks(code), 0);
7419    }
7420
7421    #[test]
7422    fn sibling_getter_and_setter_capture_the_same_cell() {
7423        let module = lower_js(
7424            "function outer() { let value = 0; const get = () => value; const set = (next) => { value = next; }; return [get, set]; }",
7425        );
7426        assert!(module.functions().iter().any(|function| {
7427            let code = function.code();
7428            created_cell_used_as_capture(code)
7429                .is_some_and(|cell| cell_capture_pushes(code, cell) == 2)
7430        }));
7431    }
7432
7433    #[test]
7434    fn transitive_capture_passes_an_existing_cell_without_wrapping_it() {
7435        let module = lower_js("function outer() { let value = 1; return () => () => value; }");
7436        let middle = module
7437            .functions()
7438            .iter()
7439            .find(|function| {
7440                function.capture_count() == 1
7441                    && function
7442                        .code()
7443                        .iter()
7444                        .any(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7445            })
7446            .expect("middle closure materializes the inner closure");
7447        assert_eq!(
7448            middle
7449                .code()
7450                .iter()
7451                .filter(|instruction| matches!(instruction, Instruction::CreateArray { .. }))
7452                .count(),
7453            1,
7454            "only the capture array is allocated; capture register zero is already a cell"
7455        );
7456        assert!(middle.code().iter().any(
7457            |instruction| matches!(instruction, Instruction::ArrayPush { value, .. } if *value == Register::new(0))
7458        ));
7459        assert!(module.functions().iter().any(|function| {
7460            function.capture_count() == 1
7461                && !function
7462                    .code()
7463                    .iter()
7464                    .any(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7465                && function.code().iter().any(
7466                    |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7467                )
7468        }));
7469    }
7470
7471    #[test]
7472    fn capture_of_capture_reads_and_reexports_the_same_cell() {
7473        let module = lower_js(
7474            "function outer() { let value = 1; return () => { value; return () => value; }; }",
7475        );
7476        let middle = module
7477            .functions()
7478            .iter()
7479            .find(|function| {
7480                function.capture_count() == 1
7481                    && function
7482                        .code()
7483                        .iter()
7484                        .any(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7485            })
7486            .expect("middle closure reads and reexports the capture");
7487        assert!(middle.code().iter().any(
7488            |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7489        ));
7490        assert_eq!(
7491            middle
7492                .code()
7493                .iter()
7494                .filter(|instruction| matches!(instruction, Instruction::CreateArray { .. }))
7495                .count(),
7496            1
7497        );
7498    }
7499
7500    #[test]
7501    fn logical_assignment_reads_and_writes_cell_contents() {
7502        let module = lower_js(
7503            "function outer() { let optional = false; return (term) => { optional ||= term; }; }",
7504        );
7505        assert!(module.functions().iter().any(|function| {
7506            function.capture_count() == 1
7507                && function.code().iter().any(
7508                    |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7509                )
7510                && function.code().iter().any(
7511                    |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == Register::new(0))
7512                )
7513        }));
7514    }
7515
7516    #[test]
7517    fn classic_for_let_rebinds_but_for_var_reuses_its_cell() {
7518        let lexical = lower_js(
7519            "function outer() { const reads = []; for (let index = 0; index < 2; index++) reads.push(() => index); return reads; }",
7520        );
7521        let shared = lower_js(
7522            "function outer() { const reads = []; for (var index = 0; index < 2; index++) reads.push(() => index); return reads; }",
7523        );
7524        let lexical_allocations = lexical
7525            .functions()
7526            .iter()
7527            .find_map(|function| {
7528                let code = function.code();
7529                let cell = created_cell_used_as_capture(code)?;
7530                Some(cell_allocations(code, cell))
7531            })
7532            .expect("lexical loop captures its binding cell");
7533        let shared_allocations = shared
7534            .functions()
7535            .iter()
7536            .find_map(|function| {
7537                let code = function.code();
7538                let cell = created_cell_used_as_capture(code)?;
7539                Some(cell_allocations(code, cell))
7540            })
7541            .expect("var loop captures its binding cell");
7542        assert_eq!(lexical_allocations, 2, "let copies into a fresh cell");
7543        assert_eq!(shared_allocations, 1, "var retains one function cell");
7544        for module in [&lexical, &shared] {
7545            let code = module
7546                .functions()
7547                .iter()
7548                .map(|function| function.code())
7549                .find(|code| created_cell_used_as_capture(code).is_some())
7550                .expect("captured loop owns a cell");
7551            assert_eq!(
7552                dormant_promotion_cell_blocks(code),
7553                0,
7554                "captured loops contain no late-promotion scaffold"
7555            );
7556        }
7557    }
7558
7559    #[test]
7560    fn uncaptured_ordinary_and_loop_locals_emit_no_cell_scaffolds() {
7561        let ordinary = lower_js("function outer(value) { let copy = value; return copy; }");
7562        let classic =
7563            lower_js("function outer(limit) { for (let index = 0; index < limit; index++) {} }");
7564        let iterator = lower_js("function outer(values) { for (let value of values) {} }");
7565
7566        for (label, module) in [
7567            ("ordinary", &ordinary),
7568            ("classic", &classic),
7569            ("iterator", &iterator),
7570        ] {
7571            let entry = module.entry().get() as usize;
7572            let code = module
7573                .functions()
7574                .iter()
7575                .enumerate()
7576                .find(|(index, _)| *index != entry)
7577                .map(|(_, function)| function.code())
7578                .expect("snippet has one declared function");
7579            assert_eq!(dormant_promotion_cell_blocks(code), 0);
7580            assert!(
7581                !code
7582                    .iter()
7583                    .any(|instruction| matches!(instruction, Instruction::CreateArray { .. })),
7584                "{label} local storage allocates no cell"
7585            );
7586            assert!(
7587                !code
7588                    .iter()
7589                    .any(|instruction| matches!(instruction, Instruction::ArrayPush { .. })),
7590                "{label} local storage initializes no cell"
7591            );
7592        }
7593    }
7594
7595    #[test]
7596    fn iterator_let_allocates_inside_the_loop_but_var_allocates_before_it() {
7597        fn allocation_and_step(module: &Module<Verified>) -> (usize, usize) {
7598            module
7599                .functions()
7600                .iter()
7601                .find_map(|function| {
7602                    let code = function.code();
7603                    let cell = created_cell_used_as_capture(code)?;
7604                    let allocation = code.iter().position(
7605                        |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7606                    )?;
7607                    let step = code
7608                        .iter()
7609                        .position(|instruction| matches!(instruction, Instruction::IteratorNext { .. }))?;
7610                    Some((allocation, step))
7611                })
7612                .expect("iterator loop captures its declaration")
7613        }
7614
7615        let lexical = lower_js(
7616            "function outer(values) { const reads = []; for (let value of values) reads.push(() => value); return reads; }",
7617        );
7618        let shared = lower_js(
7619            "function outer(values) { const reads = []; for (var value of values) reads.push(() => value); return reads; }",
7620        );
7621        let (lexical_allocation, lexical_step) = allocation_and_step(&lexical);
7622        let (shared_allocation, shared_step) = allocation_and_step(&shared);
7623        assert!(
7624            lexical_step < lexical_allocation,
7625            "let creates a new cell after each iterator step"
7626        );
7627        assert!(
7628            shared_allocation < shared_step,
7629            "var creates one cell before iterator stepping begins"
7630        );
7631    }
7632
7633    #[test]
7634    fn computed_member_access_uses_a_register_key() {
7635        let module = lower_js("const o: any = {}; const k = \"a\"; const v = o[k];");
7636        assert!(any_instruction(&module, |i| matches!(
7637            i,
7638            Instruction::GetProperty { .. }
7639        )));
7640    }
7641
7642    #[test]
7643    fn spread_call_builds_an_arguments_array_with_extend() {
7644        let module = lower_js("declare const f: any; const xs = [1]; f(...xs);");
7645        assert!(any_instruction(&module, |i| matches!(
7646            i,
7647            Instruction::ArrayExtend { .. }
7648        )));
7649        assert!(any_instruction(&module, |i| matches!(
7650            i,
7651            Instruction::Call { .. }
7652        )));
7653    }
7654
7655    #[test]
7656    fn nonempty_array_pushes_elements() {
7657        let module = lower_js("const a = [1, 2, 3];");
7658        assert!(any_instruction(&module, |i| matches!(
7659            i,
7660            Instruction::ArrayPush { .. }
7661        )));
7662    }
7663
7664    #[test]
7665    fn closure_capturing_a_local_emits_a_nonempty_capture() {
7666        let module = lower_js("function outer() { const x = 1; return () => x; }");
7667        assert!(any_instruction(&module, |i| matches!(
7668            i,
7669            Instruction::CreateClosure { .. }
7670        )));
7671        assert!(max_capture_count(&module) >= 1, "the arrow captures `x`");
7672    }
7673
7674    #[test]
7675    fn ordinary_function_declarations_materialize_own_prototypes() {
7676        let module = lower_js("function Base() {}");
7677        let code = module.functions()[0].code();
7678        let constants = module.constants();
7679
7680        // Resolve a key register back to the exact string the lowering interned
7681        // for it, by walking the LoadConst that defines it and reading the
7682        // constant pool. This ties a SetProperty key to a property name rather
7683        // than matching instruction text.
7684        let key_name = |register: Register| -> String {
7685            let id = code
7686                .iter()
7687                .find_map(|instruction| match instruction {
7688                    Instruction::LoadConst { dst, constant } if *dst == register => Some(*constant),
7689                    _ => None,
7690                })
7691                .unwrap_or_else(|| panic!("no LoadConst defines key register {register:?}"));
7692            match &constants[id.get() as usize] {
7693                Constant::String(value) => value
7694                    .to_utf8_strict()
7695                    .expect("compiler-interned property key is well-formed UTF-16"),
7696                other => panic!("expected a string constant for the key, got {other:?}"),
7697            }
7698        };
7699
7700        let (closure_index, closure) = code
7701            .iter()
7702            .enumerate()
7703            .find_map(|(index, instruction)| match instruction {
7704                Instruction::CreateClosure { dst, .. } => Some((index, *dst)),
7705                _ => None,
7706            })
7707            .expect("function declaration materializes a closure");
7708
7709        let (prototype_index, prototype) = code[closure_index + 1..]
7710            .iter()
7711            .enumerate()
7712            .find_map(|(offset, instruction)| match instruction {
7713                Instruction::CreateObject { dst } => Some((closure_index + 1 + offset, *dst)),
7714                _ => None,
7715            })
7716            .expect("ordinary function gets an own prototype object");
7717
7718        // P.constructor = F: the prototype is the object, the closure is the
7719        // value, and the key resolves to the exact string "constructor".
7720        let constructor_key = code[prototype_index + 1..]
7721            .iter()
7722            .find_map(|instruction| match instruction {
7723                Instruction::SetProperty { object, key, value }
7724                    if *object == prototype && *value == closure =>
7725                {
7726                    Some(*key)
7727                }
7728                _ => None,
7729            })
7730            .expect("prototype.constructor is assigned the closure");
7731        assert_eq!(
7732            key_name(constructor_key),
7733            "constructor",
7734            "the reverse link is stored under the key \"constructor\""
7735        );
7736
7737        // F.prototype = P: the closure is the object, the prototype is the
7738        // value, and the key resolves to the exact string "prototype".
7739        let prototype_key = code[closure_index + 1..]
7740            .iter()
7741            .find_map(|instruction| match instruction {
7742                Instruction::SetProperty { object, key, value }
7743                    if *object == closure && *value == prototype =>
7744                {
7745                    Some(*key)
7746                }
7747                _ => None,
7748            })
7749            .expect("closure.prototype is assigned the prototype");
7750        assert_eq!(
7751            key_name(prototype_key),
7752            "prototype",
7753            "the forward link is stored under the key \"prototype\""
7754        );
7755    }
7756
7757    #[test]
7758    fn template_literal_lowers_to_string_concatenation() {
7759        let module = lower_js("const a = 1; const s = `x${a}y`;");
7760        assert!(any_instruction(&module, |i| matches!(
7761            i,
7762            Instruction::Binary {
7763                op: bamts_bytecode::BinaryOp::Add,
7764                ..
7765            }
7766        )));
7767    }
7768
7769    #[test]
7770    fn for_of_lowers_to_the_iterator_protocol() {
7771        let module = lower_js("for (const v of [1, 2]) { globalThis; }");
7772        assert!(any_instruction(&module, |i| matches!(
7773            i,
7774            Instruction::GetIterator { .. }
7775        )));
7776        assert!(any_instruction(&module, |i| matches!(
7777            i,
7778            Instruction::IteratorNext { .. }
7779        )));
7780    }
7781
7782    #[test]
7783    fn object_destructuring_reads_named_properties() {
7784        let module = lower_js("declare const obj: any; const { a, b } = obj;");
7785        assert!(any_instruction(&module, |i| matches!(
7786            i,
7787            Instruction::GetProperty { .. }
7788        )));
7789    }
7790
7791    #[test]
7792    fn regex_literal_lowers_to_create_regexp() {
7793        let module = lower_js("const r = /ab+c/gi;");
7794        assert!(any_instruction(&module, |i| matches!(
7795            i,
7796            Instruction::CreateRegExp { .. }
7797        )));
7798    }
7799
7800    #[test]
7801    fn class_with_extends_builds_prototype_chain() {
7802        let module = lower_js("class B {} class C extends B { m() { return 1; } }");
7803        assert!(any_instruction(&module, |i| matches!(
7804            i,
7805            Instruction::SetPrototype { .. }
7806        )));
7807        assert!(any_instruction(&module, |i| matches!(
7808            i,
7809            Instruction::CreateClosure { .. }
7810        )));
7811    }
7812
7813    #[test]
7814    fn derived_constructor_places_fields_between_super_and_trailing_body() {
7815        let module = lower_js(
7816            "class Base {} class Derived extends Base { field = 1; constructor() { before(); super(); after(); } }",
7817        );
7818        let constructor = module
7819            .functions()
7820            .iter()
7821            .find_map(|function| {
7822                let calls: Vec<_> = function
7823                    .code()
7824                    .iter()
7825                    .enumerate()
7826                    .filter_map(|(index, instruction)| {
7827                        matches!(instruction, Instruction::Call { .. }).then_some(index)
7828                    })
7829                    .collect();
7830                (calls.len() == 3).then_some((function, calls))
7831            })
7832            .expect("derived constructor contains before, super, and after calls");
7833        let field = constructor
7834            .0
7835            .code()
7836            .iter()
7837            .enumerate()
7838            .find_map(|(index, instruction)| {
7839                matches!(instruction, Instruction::SetProperty { .. }).then_some(index)
7840            })
7841            .expect("derived field is initialized");
7842        assert!(constructor.1[1] < field && field < constructor.1[2]);
7843    }
7844
7845    #[test]
7846    fn implicit_derived_constructor_forwards_arguments_before_fields() {
7847        let module = lower_js("class Base {} class Derived extends Base { field = 1; }");
7848        let constructor = module
7849            .functions()
7850            .iter()
7851            .find(|function| {
7852                function
7853                    .code()
7854                    .iter()
7855                    .any(|instruction| matches!(instruction, Instruction::ArrayExtend { .. }))
7856            })
7857            .expect("implicit derived constructor extends its arguments array");
7858        let call = constructor
7859            .code()
7860            .iter()
7861            .position(|instruction| matches!(instruction, Instruction::Call { .. }))
7862            .expect("implicit derived constructor calls its parent");
7863        let field = constructor
7864            .code()
7865            .iter()
7866            .position(|instruction| matches!(instruction, Instruction::SetProperty { .. }))
7867            .expect("implicit derived constructor initializes fields");
7868        assert!(call < field);
7869    }
7870
7871    #[test]
7872    fn unsupported_derived_super_shapes_fail_lowering() {
7873        for source in [
7874            "class Base {} class Derived extends Base { constructor() {} }",
7875            "class Base {} class Derived extends Base { constructor() { super(); super(); } }",
7876            "class Base {} class Derived extends Base { constructor() { if (flag) super(); } }",
7877            "class Base {} class Derived extends Base { constructor() { this.x = 1; super(); } }",
7878        ] {
7879            let source = Arc::new(SourceText::new(source));
7880            let scanned = scan(SourceId::new(0), ScriptKind::TypeScript, source);
7881            let parsed = parse(scanned);
7882            let error = lower(
7883                parsed.product(),
7884                LowerOptions {
7885                    javascript_compatibility: true,
7886                },
7887            )
7888            .expect_err("unsupported derived constructor shape fails lowering");
7889            assert!(matches!(
7890                error.kind,
7891                LowerErrorKind::Unsupported(
7892                    UnsupportedConstruct::DerivedConstructorShape
7893                        | UnsupportedConstruct::ThisBeforeDerivedSuper
7894                )
7895            ));
7896        }
7897    }
7898
7899    #[test]
7900    fn private_field_creates_a_private_name() {
7901        let module = lower_js("class C { #x = 1; read() { return this.#x; } }");
7902        assert!(any_instruction(&module, |i| matches!(
7903            i,
7904            Instruction::CreatePrivateName { .. }
7905        )));
7906        assert!(any_instruction(&module, |i| matches!(
7907            i,
7908            Instruction::LoadThis { .. }
7909        )));
7910    }
7911
7912    #[test]
7913    fn generator_sets_the_flag_and_suspends() {
7914        let module = lower_js("function* g() { yield 1; yield 2; }");
7915        assert!(
7916            module
7917                .functions()
7918                .iter()
7919                .any(|function| function.flags().is_generator)
7920        );
7921        assert!(any_instruction(&module, |i| matches!(
7922            i,
7923            Instruction::Suspend { .. }
7924        )));
7925    }
7926
7927    #[test]
7928    fn async_await_suspends() {
7929        let module = lower_js("async function f(p: any) { return await p; }");
7930        assert!(
7931            module
7932                .functions()
7933                .iter()
7934                .any(|function| function.flags().is_async)
7935        );
7936        assert!(any_instruction(&module, |i| matches!(
7937            i,
7938            Instruction::Suspend { .. }
7939        )));
7940    }
7941
7942    #[test]
7943    fn free_names_load_from_the_environment() {
7944        let module = lower_js("const keys = Object.keys({});");
7945        assert!(any_instruction(&module, |i| matches!(
7946            i,
7947            Instruction::LoadGlobal { .. }
7948        )));
7949    }
7950
7951    #[test]
7952    fn try_finally_routes_completions_through_the_finalizer() {
7953        let module = lower_js(
7954            "function f(x: any) { while (x) { try { return 1; } finally { x = 0; } } return 2; }",
7955        );
7956        // A handler is registered and the finalizer runs before the return.
7957        assert!(
7958            module
7959                .functions()
7960                .iter()
7961                .any(|function| !function.handlers().is_empty())
7962        );
7963        assert_round_trips(&module);
7964    }
7965
7966    #[test]
7967    fn optional_member_call_skips_arguments_and_preserves_receiver() {
7968        let key_is_method = |code: &[Instruction], constants: &[Constant], register: Register| {
7969            let constant = code
7970                .iter()
7971                .find_map(|instruction| match instruction {
7972                    Instruction::LoadConst { dst, constant } if *dst == register => Some(*constant),
7973                    _ => None,
7974                })
7975                .unwrap_or_else(|| panic!("no LoadConst defines property key {register:?}"));
7976            match &constants[constant.get() as usize] {
7977                Constant::String(value) => {
7978                    value.to_utf8_strict().is_ok_and(|value| value == "method")
7979                }
7980                _ => false,
7981            }
7982        };
7983
7984        let assert_optional_call = |src: &str, expect_method_jump: bool| {
7985            let module = lower_js(src);
7986            let code = module.functions()[0].code();
7987            let constants = module.constants();
7988
7989            let (member_index, callee, object) = code
7990                .iter()
7991                .enumerate()
7992                .find_map(|(index, instruction)| match instruction {
7993                    Instruction::GetProperty { dst, object, key }
7994                        if key_is_method(code, constants, *key) =>
7995                    {
7996                        Some((index, *dst, *object))
7997                    }
7998                    _ => None,
7999                })
8000                .expect("optional member call loads its method");
8001
8002            let (call_index, call_dst, this_value) = code
8003                .iter()
8004                .enumerate()
8005                .find_map(|(index, instruction)| match instruction {
8006                    Instruction::Call {
8007                        dst,
8008                        callee: call_callee,
8009                        this_value,
8010                        ..
8011                    } if *call_callee == callee => Some((index, *dst, *this_value)),
8012                    _ => None,
8013                })
8014                .expect("optional member call invokes the loaded method");
8015            assert_eq!(
8016                this_value, object,
8017                "the call keeps the member object as this"
8018            );
8019
8020            let result_move_index = code[call_index + 1..]
8021                .iter()
8022                .enumerate()
8023                .find_map(|(offset, instruction)| match instruction {
8024                    Instruction::Move { src, .. } if *src == call_dst => {
8025                        Some(call_index + 1 + offset)
8026                    }
8027                    _ => None,
8028                })
8029                .expect("the call result is moved into a shared result register");
8030            let merge = result_move_index + 1;
8031
8032            let (object_jump_index, object_jump_target) = code[..member_index]
8033                .iter()
8034                .enumerate()
8035                .rev()
8036                .find_map(|(index, instruction)| match instruction {
8037                    Instruction::JumpIfTrue { condition, target }
8038                        if code[..index].iter().any(|prior| {
8039                            matches!(
8040                                prior,
8041                                Instruction::Binary { dst, op: BinaryOp::Equal, left, .. }
8042                                    if *dst == *condition && *left == object
8043                            )
8044                        }) =>
8045                    {
8046                        Some((index, *target))
8047                    }
8048                    _ => None,
8049                })
8050                .expect("optional object test branches before the member read");
8051            assert!(
8052                code[object_jump_index + 1..call_index]
8053                    .iter()
8054                    .any(|instruction| matches!(instruction, Instruction::Call { .. })),
8055                "the argument side effect is emitted before the member call"
8056            );
8057            assert_eq!(
8058                object_jump_target.get() as usize,
8059                merge,
8060                "the object-nullish branch jumps to the instruction after the result assignment"
8061            );
8062
8063            if expect_method_jump {
8064                let method_jump_target = code[member_index + 1..call_index]
8065                    .iter()
8066                    .enumerate()
8067                    .rev()
8068                    .find_map(|(offset, instruction)| {
8069                        let index = member_index + 1 + offset;
8070                        match instruction {
8071                            Instruction::JumpIfTrue { condition, target }
8072                                if code[member_index + 1..index].iter().any(|prior| {
8073                                    matches!(
8074                                        prior,
8075                                        Instruction::Binary { dst, op: BinaryOp::Equal, left, .. }
8076                                            if *dst == *condition && *left == callee
8077                                    )
8078                                }) =>
8079                            {
8080                                Some(*target)
8081                            }
8082                            _ => None,
8083                        }
8084                    })
8085                    .expect("optional method test branches before the call");
8086                assert_eq!(
8087                    method_jump_target.get() as usize,
8088                    merge,
8089                    "the method-nullish branch jumps to the same merge point"
8090                );
8091            }
8092        };
8093
8094        assert_optional_call(
8095            "declare const obj: any; declare function side_effect(): number; obj?.method(side_effect());",
8096            false,
8097        );
8098        assert_optional_call(
8099            "declare const obj: any; declare function side_effect(): number; obj?.method?.(side_effect());",
8100            true,
8101        );
8102    }
8103
8104    #[test]
8105    fn arrow_rest_loads_the_activations_own_arguments() {
8106        // `(...options) => options` must collect the arrow invocation's own
8107        // actual arguments, not an unconditional empty array. Under the old
8108        // `ArgumentsSource::None` branch the arrow body emitted `CreateArray`
8109        // with no `LoadArguments`, so rest was always empty.
8110        let module = lower_js("const collect = (...options) => options; collect(1, 2);");
8111        let entry = module.entry().get() as usize;
8112        let arrow = module
8113            .functions()
8114            .iter()
8115            .enumerate()
8116            .find(|(index, _)| *index != entry)
8117            .map(|(_, function)| function)
8118            .expect("the arrow is the sole non-entry function");
8119        let code = arrow.code();
8120        assert!(
8121            code.iter()
8122                .any(|instruction| matches!(instruction, Instruction::LoadArguments { .. })),
8123            "the arrow body loads its own activation arguments for rest"
8124        );
8125        assert!(
8126            code.iter()
8127                .any(|instruction| matches!(instruction, Instruction::GetIterator { .. })),
8128            "rest collection iterates the loaded arguments"
8129        );
8130        assert!(
8131            code.iter()
8132                .any(|instruction| matches!(instruction, Instruction::ArrayPush { .. })),
8133            "rest collection pushes into the rest array"
8134        );
8135        assert_round_trips(&module);
8136    }
8137
8138    #[test]
8139    fn regular_function_rest_with_fixed_parameters_loads_arguments() {
8140        // A regular function with fixed parameters followed by rest must still
8141        // load its own arguments, discard the fixed prefix, then collect.
8142        let module = lower_js("function f(a, ...rest) { return rest; }");
8143        let entry = module.entry().get() as usize;
8144        let function = module
8145            .functions()
8146            .iter()
8147            .enumerate()
8148            .find(|(index, _)| *index != entry)
8149            .map(|(_, function)| function)
8150            .expect("the regular function is the sole non-entry function");
8151        let code = function.code();
8152        assert!(
8153            code.iter()
8154                .any(|instruction| matches!(instruction, Instruction::LoadArguments { .. })),
8155            "the function loads its own arguments for rest"
8156        );
8157        assert!(
8158            code.iter()
8159                .any(|instruction| matches!(instruction, Instruction::GetIterator { .. })),
8160            "rest collection iterates the loaded arguments"
8161        );
8162        assert!(
8163            code.iter()
8164                .any(|instruction| matches!(instruction, Instruction::IteratorNext { .. })),
8165            "the fixed parameter is discarded by stepping the iterator"
8166        );
8167        assert_round_trips(&module);
8168    }
8169
8170    #[test]
8171    fn arrow_lexical_arguments_is_captured_not_loaded() {
8172        // An arrow referencing `arguments` captures it from the enclosing
8173        // function; the identifier read must not emit `LoadArguments` in the
8174        // arrow body. This invariant is independent of rest-parameter loading
8175        // and proves the lexical `arguments` binding is untouched.
8176        let module = lower_js("function outer() { const read = () => arguments; return read(); }");
8177        let arrow = module
8178            .functions()
8179            .iter()
8180            .find(|function| function.capture_count() >= 1)
8181            .expect("the arrow captures `arguments` from outer");
8182        assert!(
8183            !arrow
8184                .code()
8185                .iter()
8186                .any(|instruction| matches!(instruction, Instruction::LoadArguments { .. })),
8187            "the arrow reads `arguments` from its capture, not its own activation"
8188        );
8189        assert_round_trips(&module);
8190    }
8191
8192    fn assert_round_trips(module: &Module<Verified>) {
8193        let bytes = module.encode();
8194        decode_verified(&bytes, &DecodeLimits::default())
8195            .expect("a verified module re-decodes and re-verifies");
8196    }
8197}