Skip to main content

boa_engine/bytecompiler/
mod.rs

1//! Boa's bytecode compiler.
2//!
3//! The bytecompiler is responsible for lowering ECMAScript AST nodes into
4//! executable bytecode for the virtual machine.
5//!
6//! It walks the parsed AST and emits instructions using the bytecode emitter,
7//! producing code blocks that are later executed by the VM.
8//!
9//! This module provides the necessary functionality to compile JavaScript code into bytecode.
10//! Conceptually, it is the bridge between the spec's runtime semantics and Boa's opcode-based VM.
11//! When the ECMAScript algorithms use the `!` prefix for an abstract operation, Boa treats the
12//! corresponding assumption as an internal compiler or bytecode invariant, and documents that
13//! mapping near the assertion sites that rely on it.
14//!
15//! Relevant specification entry points include [script evaluation][script],
16//! [module evaluation][module], and the expression and statement [runtime semantics][evaluation]
17//! that eventually feed into those top-level algorithms.
18//!
19//! [script]: https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
20//! [module]: https://tc39.es/ecma262/#sec-moduleevaluation
21//! [evaluation]: https://tc39.es/ecma262/#sec-runtime-semantics-evaluation
22
23mod class;
24mod declaration;
25mod declarations;
26mod env;
27mod expression;
28mod function;
29mod generator;
30mod jump_control;
31mod module;
32mod register;
33mod statement;
34mod utils;
35
36use std::{
37    borrow::{Borrow, BorrowMut},
38    cell::Cell,
39    ops::{Deref, DerefMut},
40};
41
42use crate::{
43    JsBigInt, JsStr, JsString, SourceText, SpannedSourceText,
44    builtins::function::{ThisMode, arguments::MappedArguments},
45    js_string,
46    vm::{
47        CallFrame, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, GlobalFunctionBinding,
48        Handler, InlineCache,
49        opcode::{Address, BindingOpcode, BytecodeEmitter, RegisterOperand},
50        source_info::{SourceInfo, SourceMap, SourceMapBuilder, SourcePath},
51    },
52};
53use boa_ast::{
54    Declaration, Expression, LinearSpan, Position, Spanned, Statement, StatementList,
55    StatementListItem,
56    declaration::{Binding, LexicalDeclaration, VarDeclaration},
57    expression::{
58        Call, Identifier, New, Optional, OptionalOperationKind,
59        access::{PropertyAccess, PropertyAccessField},
60        literal::ObjectMethodDefinition,
61        operator::{
62            Binary,
63            assign::AssignTarget,
64            binary::{BinaryOp, RelationalOp},
65            update::UpdateTarget,
66        },
67    },
68    function::{
69        ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,
70        AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassMethodDefinition,
71        FormalParameterList, FunctionBody, FunctionDeclaration, FunctionExpression,
72        GeneratorDeclaration, GeneratorExpression, PrivateName,
73    },
74    operations::returns_value,
75    pattern::Pattern,
76    property::MethodDefinitionKind,
77    scope::{BindingLocator, BindingLocatorError, FunctionScopes, IdentifierReference, Scope},
78};
79use boa_gc::Gc;
80use boa_interner::{Interner, Sym};
81use boa_macros::js_str;
82use boa_string::StaticJsStrings;
83use rustc_hash::FxHashMap;
84use thin_vec::ThinVec;
85
86pub(crate) use declarations::{
87    global_declaration_instantiation_context, prepare_eval_declaration_instantiation,
88};
89pub(crate) use function::FunctionCompiler;
90pub(crate) use jump_control::JumpControlInfo;
91pub(crate) use register::*;
92
93pub(crate) trait ToJsString {
94    fn to_js_string(&self, interner: &Interner) -> JsString;
95}
96
97impl ToJsString for Sym {
98    #[allow(clippy::cast_possible_truncation)]
99    fn to_js_string(&self, interner: &Interner) -> JsString {
100        let utf16 = interner.resolve_expect(*self).utf16();
101        if interner.is_latin1(*self) {
102            let bytes: Vec<u8> = utf16.iter().map(|&c| c as u8).collect();
103            js_string!(JsStr::latin1(&bytes))
104        } else {
105            js_string!(utf16)
106        }
107    }
108}
109
110impl ToJsString for Identifier {
111    fn to_js_string(&self, interner: &Interner) -> JsString {
112        self.sym().to_js_string(interner)
113    }
114}
115
116/// Describes how a node has been defined in the source code.
117#[derive(Debug, Clone, Copy, PartialEq)]
118pub(crate) enum NodeKind {
119    Declaration,
120    Expression,
121}
122
123/// Describes the type of a function.
124#[derive(Debug, Clone, Copy, PartialEq)]
125pub(crate) enum FunctionKind {
126    Ordinary,
127    Arrow,
128    AsyncArrow,
129    Async,
130    Generator,
131    AsyncGenerator,
132}
133
134impl FunctionKind {
135    pub(crate) const fn is_arrow(self) -> bool {
136        matches!(self, Self::Arrow | Self::AsyncArrow)
137    }
138
139    pub(crate) const fn is_async(self) -> bool {
140        matches!(self, Self::Async | Self::AsyncGenerator | Self::AsyncArrow)
141    }
142
143    pub(crate) const fn is_generator(self) -> bool {
144        matches!(self, Self::Generator | Self::AsyncGenerator)
145    }
146}
147
148/// Describes the complete specification of a function node.
149#[derive(Debug, Clone, Copy)]
150pub(crate) struct FunctionSpec<'a> {
151    pub(crate) kind: FunctionKind,
152    pub(crate) name: Option<Identifier>,
153    parameters: &'a FormalParameterList,
154    body: &'a FunctionBody,
155    pub(crate) scopes: &'a FunctionScopes,
156    pub(crate) name_scope: Option<&'a Scope>,
157    linear_span: Option<LinearSpan>,
158    pub(crate) contains_direct_eval: bool,
159}
160
161impl PartialEq for FunctionSpec<'_> {
162    fn eq(&self, other: &Self) -> bool {
163        // all fields except `linear_span`
164        self.kind == other.kind
165            && self.name == other.name
166            && self.parameters == other.parameters
167            && self.body == other.body
168            && self.scopes == other.scopes
169            && self.name_scope == other.name_scope
170    }
171}
172
173impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> {
174    fn from(function: &'a FunctionDeclaration) -> Self {
175        FunctionSpec {
176            kind: FunctionKind::Ordinary,
177            name: Some(function.name()),
178            parameters: function.parameters(),
179            body: function.body(),
180            scopes: function.scopes(),
181            name_scope: None,
182            linear_span: Some(function.linear_span()),
183            contains_direct_eval: function.contains_direct_eval(),
184        }
185    }
186}
187
188impl<'a> From<&'a GeneratorDeclaration> for FunctionSpec<'a> {
189    fn from(function: &'a GeneratorDeclaration) -> Self {
190        FunctionSpec {
191            kind: FunctionKind::Generator,
192            name: Some(function.name()),
193            parameters: function.parameters(),
194            body: function.body(),
195            scopes: function.scopes(),
196            name_scope: None,
197            linear_span: Some(function.linear_span()),
198            contains_direct_eval: function.contains_direct_eval(),
199        }
200    }
201}
202
203impl<'a> From<&'a AsyncFunctionDeclaration> for FunctionSpec<'a> {
204    fn from(function: &'a AsyncFunctionDeclaration) -> Self {
205        FunctionSpec {
206            kind: FunctionKind::Async,
207            name: Some(function.name()),
208            parameters: function.parameters(),
209            body: function.body(),
210            scopes: function.scopes(),
211            name_scope: None,
212            linear_span: Some(function.linear_span()),
213            contains_direct_eval: function.contains_direct_eval(),
214        }
215    }
216}
217
218impl<'a> From<&'a AsyncGeneratorDeclaration> for FunctionSpec<'a> {
219    fn from(function: &'a AsyncGeneratorDeclaration) -> Self {
220        FunctionSpec {
221            kind: FunctionKind::AsyncGenerator,
222            name: Some(function.name()),
223            parameters: function.parameters(),
224            body: function.body(),
225            scopes: function.scopes(),
226            name_scope: None,
227            linear_span: Some(function.linear_span()),
228            contains_direct_eval: function.contains_direct_eval(),
229        }
230    }
231}
232
233impl<'a> From<&'a FunctionExpression> for FunctionSpec<'a> {
234    fn from(function: &'a FunctionExpression) -> Self {
235        FunctionSpec {
236            kind: FunctionKind::Ordinary,
237            name: function.name(),
238            parameters: function.parameters(),
239            body: function.body(),
240            scopes: function.scopes(),
241            name_scope: function.name_scope(),
242            linear_span: function.linear_span(),
243            contains_direct_eval: function.contains_direct_eval(),
244        }
245    }
246}
247
248impl<'a> From<&'a ArrowFunction> for FunctionSpec<'a> {
249    fn from(function: &'a ArrowFunction) -> Self {
250        FunctionSpec {
251            kind: FunctionKind::Arrow,
252            name: function.name(),
253            parameters: function.parameters(),
254            body: function.body(),
255            scopes: function.scopes(),
256            name_scope: None,
257            linear_span: Some(function.linear_span()),
258            contains_direct_eval: function.contains_direct_eval(),
259        }
260    }
261}
262
263impl<'a> From<&'a AsyncArrowFunction> for FunctionSpec<'a> {
264    fn from(function: &'a AsyncArrowFunction) -> Self {
265        FunctionSpec {
266            kind: FunctionKind::AsyncArrow,
267            name: function.name(),
268            parameters: function.parameters(),
269            body: function.body(),
270            scopes: function.scopes(),
271            name_scope: None,
272            linear_span: Some(function.linear_span()),
273            contains_direct_eval: function.contains_direct_eval(),
274        }
275    }
276}
277
278impl<'a> From<&'a AsyncFunctionExpression> for FunctionSpec<'a> {
279    fn from(function: &'a AsyncFunctionExpression) -> Self {
280        FunctionSpec {
281            kind: FunctionKind::Async,
282            name: function.name(),
283            parameters: function.parameters(),
284            body: function.body(),
285            scopes: function.scopes(),
286            name_scope: function.name_scope(),
287            linear_span: Some(function.linear_span()),
288            contains_direct_eval: function.contains_direct_eval(),
289        }
290    }
291}
292
293impl<'a> From<&'a GeneratorExpression> for FunctionSpec<'a> {
294    fn from(function: &'a GeneratorExpression) -> Self {
295        FunctionSpec {
296            kind: FunctionKind::Generator,
297            name: function.name(),
298            parameters: function.parameters(),
299            body: function.body(),
300            scopes: function.scopes(),
301            name_scope: function.name_scope(),
302            linear_span: Some(function.linear_span()),
303            contains_direct_eval: function.contains_direct_eval(),
304        }
305    }
306}
307
308impl<'a> From<&'a AsyncGeneratorExpression> for FunctionSpec<'a> {
309    fn from(function: &'a AsyncGeneratorExpression) -> Self {
310        FunctionSpec {
311            kind: FunctionKind::AsyncGenerator,
312            name: function.name(),
313            parameters: function.parameters(),
314            body: function.body(),
315            scopes: function.scopes(),
316            name_scope: function.name_scope(),
317            linear_span: Some(function.linear_span()),
318            contains_direct_eval: function.contains_direct_eval(),
319        }
320    }
321}
322
323impl<'a> From<&'a ClassMethodDefinition> for FunctionSpec<'a> {
324    fn from(method: &'a ClassMethodDefinition) -> Self {
325        let kind = match method.kind() {
326            MethodDefinitionKind::Generator => FunctionKind::Generator,
327            MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,
328            MethodDefinitionKind::Async => FunctionKind::Async,
329            _ => FunctionKind::Ordinary,
330        };
331
332        FunctionSpec {
333            kind,
334            name: None,
335            parameters: method.parameters(),
336            body: method.body(),
337            scopes: method.scopes(),
338            name_scope: None,
339            linear_span: Some(method.linear_span()),
340            contains_direct_eval: method.contains_direct_eval(),
341        }
342    }
343}
344
345impl<'a> From<&'a ObjectMethodDefinition> for FunctionSpec<'a> {
346    fn from(method: &'a ObjectMethodDefinition) -> Self {
347        let kind = match method.kind() {
348            MethodDefinitionKind::Generator => FunctionKind::Generator,
349            MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,
350            MethodDefinitionKind::Async => FunctionKind::Async,
351            _ => FunctionKind::Ordinary,
352        };
353
354        FunctionSpec {
355            kind,
356            name: method.name().literal(),
357            parameters: method.parameters(),
358            body: method.body(),
359            scopes: method.scopes(),
360            name_scope: None,
361            linear_span: Some(method.linear_span()),
362            contains_direct_eval: method.contains_direct_eval(),
363        }
364    }
365}
366
367#[derive(Debug, Clone, Copy)]
368pub(crate) enum MethodKind {
369    Get,
370    Set,
371    Ordinary,
372}
373
374/// Represents a callable expression, like `f()` or `new Cl()`
375#[derive(Debug, Clone, Copy)]
376enum Callable<'a> {
377    Call(&'a Call),
378    New(&'a New),
379}
380
381#[derive(Debug, Clone, PartialEq, Eq, Hash)]
382enum Literal {
383    String(JsString),
384    BigInt(JsBigInt),
385}
386
387#[must_use]
388#[derive(Debug, Clone, Copy, PartialEq, Eq)]
389pub(crate) struct Label {
390    index: Address,
391}
392
393/// A loop-invariant operand that was hoisted out of a loop condition.
394///
395/// When a loop condition like `i < 10` has a literal operand, we can compile
396/// the constant (`10`) into a register once before the loop, rather than
397/// re-emitting a `PushInt8`/`PushInt32` on every iteration.
398pub(crate) struct HoistedOperand {
399    /// The register holding the pre-compiled constant value.
400    pub(crate) register: Register,
401    /// `true` if this operand is the RHS of the comparison (e.g. `10` in `i < 10`).
402    pub(crate) is_rhs: bool,
403}
404
405#[derive(Debug, Clone, Copy)]
406#[allow(variant_size_differences)]
407enum Access<'a> {
408    Variable { name: Identifier },
409    Property { access: &'a PropertyAccess },
410    This,
411}
412
413impl Access<'_> {
414    const fn from_assign_target(target: &AssignTarget) -> Result<Access<'_>, &Pattern> {
415        match target {
416            AssignTarget::Identifier(ident) => Ok(Access::Variable { name: *ident }),
417            AssignTarget::Access(access) => Ok(Access::Property { access }),
418            AssignTarget::Pattern(pat) => Err(pat),
419        }
420    }
421
422    const fn from_expression(expr: &Expression) -> Option<Access<'_>> {
423        match expr {
424            Expression::Identifier(name) => Some(Access::Variable { name: *name }),
425            Expression::PropertyAccess(access) => Some(Access::Property { access }),
426            Expression::This(_this) => Some(Access::This),
427            Expression::Parenthesized(expr) => Self::from_expression(expr.expression()),
428            _ => None,
429        }
430    }
431
432    const fn from_update_target(target: &UpdateTarget) -> Access<'_> {
433        match target {
434            UpdateTarget::Identifier(name) => Access::Variable { name: *name },
435            UpdateTarget::PropertyAccess(access) => Access::Property { access },
436        }
437    }
438}
439
440#[derive(Copy, Clone, Debug)]
441pub(crate) enum BindingAccessOpcode {
442    PutLexicalValue,
443    DefInitVar,
444    SetName,
445    SetNameByLocator,
446    GetName,
447    GetNameAndLocator,
448    GetNameOrUndefined,
449    DeleteName,
450    GetLocator,
451    DefVar,
452    DefEvalVar,
453}
454
455/// Manages the source position scope, push on creation, pop on drop.
456pub(crate) struct SourcePositionGuard<'a, 'b> {
457    compiler: &'a mut ByteCompiler<'b>,
458}
459impl<'a, 'b> SourcePositionGuard<'a, 'b> {
460    fn new(compiler: &'a mut ByteCompiler<'b>, position: Position) -> Self {
461        compiler.push_source_position(position);
462        Self { compiler }
463    }
464}
465impl Drop for SourcePositionGuard<'_, '_> {
466    fn drop(&mut self) {
467        self.pop_source_position();
468    }
469}
470impl<'a> Deref for SourcePositionGuard<'_, 'a> {
471    type Target = ByteCompiler<'a>;
472    fn deref(&self) -> &Self::Target {
473        self.compiler
474    }
475}
476impl DerefMut for SourcePositionGuard<'_, '_> {
477    fn deref_mut(&mut self) -> &mut Self::Target {
478        self.compiler
479    }
480}
481impl<'a> Borrow<ByteCompiler<'a>> for SourcePositionGuard<'_, 'a> {
482    fn borrow(&self) -> &ByteCompiler<'a> {
483        self.compiler
484    }
485}
486impl<'a> BorrowMut<ByteCompiler<'a>> for SourcePositionGuard<'_, 'a> {
487    fn borrow_mut(&mut self) -> &mut ByteCompiler<'a> {
488        self.compiler
489    }
490}
491
492/// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode.
493#[derive(Debug)]
494#[allow(clippy::struct_excessive_bools)]
495pub struct ByteCompiler<'ctx> {
496    /// Name of this function.
497    pub(crate) function_name: JsString,
498
499    /// The number of arguments expected.
500    pub(crate) length: u32,
501
502    pub(crate) register_allocator: RegisterAllocator,
503
504    /// `[[ThisMode]]`
505    pub(crate) this_mode: ThisMode,
506
507    /// Parameters passed to this function.
508    pub(crate) params: FormalParameterList,
509
510    /// Scope of the function parameters.
511    pub(crate) parameter_scope: Scope,
512
513    /// Bytecode
514    pub(crate) bytecode: BytecodeEmitter,
515
516    pub(crate) source_map_builder: SourceMapBuilder,
517    pub(crate) source_path: SourcePath,
518
519    pub(crate) constants: ThinVec<Constant>,
520
521    /// Locators for all bindings in the codeblock.
522    pub(crate) bindings: Vec<BindingLocator>,
523
524    pub(crate) local_binding_registers: FxHashMap<IdentifierReference, u32>,
525
526    /// The current variable scope.
527    pub(crate) variable_scope: Scope,
528
529    /// The current lexical scope.
530    pub(crate) lexical_scope: Scope,
531
532    pub(crate) current_open_environments_count: u32,
533    code_block_flags: CodeBlockFlags,
534    handlers: ThinVec<Handler>,
535    pub(crate) ic: Vec<InlineCache>,
536    literals_map: FxHashMap<Literal, u32>,
537    names_map: FxHashMap<Sym, u32>,
538    bindings_map: FxHashMap<BindingLocator, u32>,
539
540    /// Cache of non-local `const` binding values in persistent registers.
541    /// Avoids repeated `GetName` environment lookups for immutable bindings.
542    const_binding_cache: FxHashMap<BindingLocator, u32>,
543
544    jump_info: Vec<JumpControlInfo>,
545
546    /// Used to handle exception throws that escape the async function types.
547    ///
548    /// Async functions and async generator functions, need to be closed and resolved.
549    pub(crate) async_handler: Option<u32>,
550    json_parse: bool,
551
552    /// Whether the function is in a `with` statement.
553    pub(crate) in_with: bool,
554
555    /// Used to determine if a we emitted a `CreateUnmappedArgumentsObject` opcode
556    pub(crate) emitted_mapped_arguments_object_opcode: bool,
557
558    pub(crate) interner: &'ctx mut Interner,
559    spanned_source_text: SpannedSourceText,
560
561    pub(crate) global_lexs: Vec<u32>,
562    pub(crate) global_fns: Vec<GlobalFunctionBinding>,
563    pub(crate) global_vars: Vec<u32>,
564
565    #[cfg(feature = "annex-b")]
566    pub(crate) annex_b_function_names: Vec<Sym>,
567}
568
569pub(crate) enum BindingKind {
570    Stack(u32),
571    Local(Option<u32>),
572    Global(u32),
573}
574
575/// Where the call result should go.
576#[derive(Copy, Clone)]
577enum CallResultDest<'a> {
578    /// Pop result into the given register.
579    Register(&'a Register),
580    /// Discard the result (emit Pop).
581    Discard,
582    /// Leave the result on the stack.
583    Stack,
584}
585
586impl<'ctx> ByteCompiler<'ctx> {
587    /// Represents a placeholder address that will be patched later.
588    const DUMMY_ADDRESS: Address = Address::new(u32::MAX);
589    const DUMMY_LABEL: Label = Label {
590        index: Address::new(u32::MAX),
591    };
592
593    /// Creates a new [`ByteCompiler`].
594    #[inline]
595    #[allow(clippy::too_many_arguments)]
596    #[allow(clippy::fn_params_excessive_bools)]
597    pub(crate) fn new(
598        name: JsString,
599        strict: bool,
600        json_parse: bool,
601        variable_scope: Scope,
602        lexical_scope: Scope,
603        is_async: bool,
604        is_generator: bool,
605        interner: &'ctx mut Interner,
606        in_with: bool,
607        spanned_source_text: SpannedSourceText,
608        source_path: SourcePath,
609    ) -> ByteCompiler<'ctx> {
610        let mut code_block_flags = CodeBlockFlags::empty();
611        code_block_flags.set(CodeBlockFlags::STRICT, strict);
612        code_block_flags.set(CodeBlockFlags::IS_ASYNC, is_async);
613        code_block_flags.set(CodeBlockFlags::IS_GENERATOR, is_generator);
614        code_block_flags |= CodeBlockFlags::HAS_PROTOTYPE_PROPERTY;
615
616        let mut register_allocator = RegisterAllocator::default();
617        let undefined_register = register_allocator.alloc_persistent();
618        debug_assert_eq!(
619            undefined_register.index(),
620            CallFrame::undefined_register().index()
621        );
622        if is_async {
623            let promise_register = register_allocator.alloc_persistent();
624            let resolve_register = register_allocator.alloc_persistent();
625            let reject_register = register_allocator.alloc_persistent();
626
627            debug_assert_eq!(
628                promise_register.index(),
629                CallFrame::promise_capability_promise_register().index()
630            );
631            debug_assert_eq!(
632                resolve_register.index(),
633                CallFrame::promise_capability_resolve_register().index()
634            );
635            debug_assert_eq!(
636                reject_register.index(),
637                CallFrame::promise_capability_reject_register().index()
638            );
639
640            if is_generator {
641                let async_function_object_register = register_allocator.alloc_persistent();
642                debug_assert_eq!(
643                    async_function_object_register.index(),
644                    CallFrame::async_generator_object_register().index()
645                );
646            }
647        }
648
649        Self {
650            function_name: name,
651            length: 0,
652            bytecode: BytecodeEmitter::new(),
653            source_map_builder: SourceMapBuilder::default(),
654            constants: ThinVec::default(),
655            bindings: Vec::default(),
656            local_binding_registers: FxHashMap::default(),
657            this_mode: ThisMode::Global,
658            params: FormalParameterList::default(),
659            parameter_scope: Scope::default(),
660            current_open_environments_count: 0,
661
662            register_allocator,
663            code_block_flags,
664            handlers: ThinVec::default(),
665            ic: Vec::default(),
666
667            literals_map: FxHashMap::default(),
668            names_map: FxHashMap::default(),
669            bindings_map: FxHashMap::default(),
670            const_binding_cache: FxHashMap::default(),
671            jump_info: Vec::new(),
672            async_handler: None,
673            json_parse,
674            variable_scope,
675            lexical_scope,
676            interner,
677            spanned_source_text,
678            source_path,
679
680            #[cfg(feature = "annex-b")]
681            annex_b_function_names: Vec::new(),
682            in_with,
683            emitted_mapped_arguments_object_opcode: false,
684
685            global_lexs: Vec::new(),
686            global_fns: Vec::new(),
687            global_vars: Vec::new(),
688        }
689    }
690
691    pub(crate) fn source_text(&self) -> SourceText {
692        self.spanned_source_text.source_text()
693    }
694
695    pub(crate) const fn strict(&self) -> bool {
696        self.code_block_flags.contains(CodeBlockFlags::STRICT)
697    }
698
699    pub(crate) const fn is_async(&self) -> bool {
700        self.code_block_flags.contains(CodeBlockFlags::IS_ASYNC)
701    }
702
703    pub(crate) const fn is_generator(&self) -> bool {
704        self.code_block_flags.contains(CodeBlockFlags::IS_GENERATOR)
705    }
706
707    pub(crate) const fn is_async_generator(&self) -> bool {
708        self.is_async() && self.is_generator()
709    }
710
711    pub(crate) fn interner(&self) -> &Interner {
712        self.interner
713    }
714
715    fn get_or_insert_literal(&mut self, literal: Literal) -> u32 {
716        if let Some(index) = self.literals_map.get(&literal) {
717            return *index;
718        }
719
720        let value = match literal.clone() {
721            Literal::String(value) => Constant::String(value),
722            Literal::BigInt(value) => Constant::BigInt(value),
723        };
724
725        let index = self.constants.len() as u32;
726        self.constants.push(value);
727        self.literals_map.insert(literal, index);
728        index
729    }
730
731    fn get_or_insert_name(&mut self, name: Sym) -> u32 {
732        if let Some(index) = self.names_map.get(&name) {
733            return *index;
734        }
735
736        let index = self.constants.len() as u32;
737        let string = name.to_js_string(self.interner());
738        self.constants.push(Constant::String(string));
739        self.names_map.insert(name, index);
740        index
741    }
742
743    fn get_or_insert_string(&mut self, value: JsString) -> u32 {
744        self.get_or_insert_literal(Literal::String(value))
745    }
746
747    #[inline]
748    fn get_or_insert_private_name(&mut self, name: PrivateName) -> u32 {
749        self.get_or_insert_name(name.description())
750    }
751
752    // TODO: Make this return `Option<BindingKind>` instead of making BindingKind::Local
753    //       inner field optional.
754    #[inline]
755    pub(crate) fn get_binding(&mut self, binding: &IdentifierReference) -> BindingKind {
756        if binding.is_global_object() {
757            if let Some(index) = self.bindings_map.get(&binding.locator()) {
758                return BindingKind::Global(*index);
759            }
760
761            let index = self.bindings.len() as u32;
762            self.bindings.push(binding.locator().clone());
763            self.bindings_map.insert(binding.locator(), index);
764            return BindingKind::Global(index);
765        }
766
767        if binding.local() {
768            return BindingKind::Local(self.local_binding_registers.get(binding).copied());
769        }
770
771        if let Some(index) = self.bindings_map.get(&binding.locator()) {
772            return BindingKind::Stack(*index);
773        }
774
775        let index = self.bindings.len() as u32;
776        self.bindings.push(binding.locator().clone());
777        self.bindings_map.insert(binding.locator(), index);
778        BindingKind::Stack(index)
779    }
780
781    #[inline]
782    pub(crate) fn insert_binding(&mut self, binding: IdentifierReference) -> BindingKind {
783        if binding.is_global_object() {
784            if let Some(index) = self.bindings_map.get(&binding.locator()) {
785                return BindingKind::Global(*index);
786            }
787
788            let index = self.bindings.len() as u32;
789            self.bindings.push(binding.locator().clone());
790            self.bindings_map.insert(binding.locator(), index);
791            return BindingKind::Global(index);
792        }
793
794        if binding.local() {
795            return BindingKind::Local(Some(
796                *self
797                    .local_binding_registers
798                    .entry(binding)
799                    .or_insert_with(|| self.register_allocator.alloc_persistent().index()),
800            ));
801        }
802
803        if let Some(index) = self.bindings_map.get(&binding.locator()) {
804            return BindingKind::Stack(*index);
805        }
806
807        let index = self.bindings.len() as u32;
808        self.bindings.push(binding.locator().clone());
809        self.bindings_map.insert(binding.locator(), index);
810        BindingKind::Stack(index)
811    }
812
813    #[inline]
814    #[must_use]
815    pub(crate) fn push_function_to_constants(&mut self, function: Gc<CodeBlock>) -> u32 {
816        let index = self.constants.len() as u32;
817        self.constants.push(Constant::Function(function));
818        index
819    }
820
821    fn emit_binding(&mut self, opcode: BindingOpcode, name: JsString, value: &Register) {
822        match opcode {
823            BindingOpcode::Var => {
824                let binding = self.variable_scope.get_identifier_reference(name);
825                if !binding.locator().is_global() {
826                    let index = self.insert_binding(binding);
827                    self.emit_binding_access(BindingAccessOpcode::DefVar, &index, value);
828                }
829            }
830            BindingOpcode::InitVar => match self.lexical_scope.set_mutable_binding(name.clone()) {
831                Ok(binding) => {
832                    let index = self.insert_binding(binding);
833                    self.emit_binding_access(BindingAccessOpcode::DefInitVar, &index, value);
834                }
835                Err(BindingLocatorError::MutateImmutable) => {
836                    let index = self.get_or_insert_string(name);
837                    self.bytecode.emit_throw_mutate_immutable(index.into());
838                }
839                Err(BindingLocatorError::Silent) => {}
840            },
841            BindingOpcode::InitLexical => {
842                let binding = self.lexical_scope.get_identifier_reference(name);
843                let index = self.insert_binding(binding);
844                self.emit_binding_access(BindingAccessOpcode::PutLexicalValue, &index, value);
845            }
846            BindingOpcode::SetName => match self.lexical_scope.set_mutable_binding(name.clone()) {
847                Ok(binding) => {
848                    let index = self.insert_binding(binding);
849                    self.emit_binding_access(BindingAccessOpcode::SetName, &index, value);
850                }
851                Err(BindingLocatorError::MutateImmutable) => {
852                    let index = self.get_or_insert_string(name);
853                    self.bytecode.emit_throw_mutate_immutable(index.into());
854                }
855                Err(BindingLocatorError::Silent) => {}
856            },
857        }
858    }
859
860    fn next_opcode_location(&mut self) -> Address {
861        self.bytecode.next_opcode_location()
862    }
863
864    pub(crate) fn position_guard(
865        &mut self,
866        spanned: impl Spanned,
867    ) -> SourcePositionGuard<'_, 'ctx> {
868        SourcePositionGuard::new(self, spanned.span().start())
869    }
870
871    pub(crate) fn push_source_position<T>(&mut self, position: T)
872    where
873        T: Into<Option<Position>>,
874    {
875        let start_pc = self.next_opcode_location();
876        self.source_map_builder
877            .push_source_position(start_pc.as_u32(), position.into());
878    }
879
880    pub(crate) fn pop_source_position(&mut self) {
881        let start_pc = self.next_opcode_location();
882        self.source_map_builder
883            .pop_source_position(start_pc.as_u32());
884    }
885
886    pub(crate) fn emit_get_function(&mut self, dst: &Register, index: u32) {
887        self.bytecode
888            .emit_get_function(dst.variable(), index.into());
889    }
890
891    fn pop_into_register(&mut self, dst: &Register) {
892        self.bytecode.emit_pop_into_register(dst.variable());
893    }
894
895    pub(crate) fn push_from_register(&mut self, src: &Register) {
896        self.bytecode.emit_push_from_register(src.variable());
897    }
898
899    pub(crate) fn emit_binding_access(
900        &mut self,
901        opcode: BindingAccessOpcode,
902        binding: &BindingKind,
903        value: &Register,
904    ) {
905        match binding {
906            BindingKind::Global(index) => match opcode {
907                BindingAccessOpcode::SetNameByLocator => {
908                    self.bytecode.emit_set_name_by_locator(value.variable());
909                }
910                BindingAccessOpcode::GetName => {
911                    let ic_index = self.ic.len() as u32;
912                    let name = self.bindings[*index as usize].name().clone();
913                    self.ic.push(InlineCache::new(name));
914                    self.bytecode.emit_get_name_global(
915                        value.variable(),
916                        (*index).into(),
917                        ic_index.into(),
918                    );
919                }
920                BindingAccessOpcode::GetLocator => self.bytecode.emit_get_locator((*index).into()),
921                BindingAccessOpcode::DefVar => self.bytecode.emit_def_var((*index).into()),
922                BindingAccessOpcode::DefEvalVar => {
923                    self.bytecode.emit_def_eval_var((*index).into());
924                }
925                BindingAccessOpcode::PutLexicalValue => self
926                    .bytecode
927                    .emit_put_lexical_value(value.variable(), (*index).into()),
928                BindingAccessOpcode::DefInitVar => self
929                    .bytecode
930                    .emit_def_init_var(value.variable(), (*index).into()),
931                BindingAccessOpcode::SetName => self
932                    .bytecode
933                    .emit_set_name(value.variable(), (*index).into()),
934                BindingAccessOpcode::GetNameAndLocator => self
935                    .bytecode
936                    .emit_get_name_and_locator(value.variable(), (*index).into()),
937                BindingAccessOpcode::GetNameOrUndefined => self
938                    .bytecode
939                    .emit_get_name_or_undefined(value.variable(), (*index).into()),
940                BindingAccessOpcode::DeleteName => self
941                    .bytecode
942                    .emit_delete_name(value.variable(), (*index).into()),
943            },
944            BindingKind::Stack(index) => match opcode {
945                BindingAccessOpcode::SetNameByLocator => {
946                    self.bytecode.emit_set_name_by_locator(value.variable());
947                }
948                BindingAccessOpcode::GetLocator => self.bytecode.emit_get_locator((*index).into()),
949                BindingAccessOpcode::DefVar => self.bytecode.emit_def_var((*index).into()),
950                BindingAccessOpcode::DefEvalVar => {
951                    self.bytecode.emit_def_eval_var((*index).into());
952                }
953                BindingAccessOpcode::PutLexicalValue => self
954                    .bytecode
955                    .emit_put_lexical_value(value.variable(), (*index).into()),
956                BindingAccessOpcode::DefInitVar => self
957                    .bytecode
958                    .emit_def_init_var(value.variable(), (*index).into()),
959                BindingAccessOpcode::SetName => self
960                    .bytecode
961                    .emit_set_name(value.variable(), (*index).into()),
962                BindingAccessOpcode::GetName => self
963                    .bytecode
964                    .emit_get_name(value.variable(), (*index).into()),
965                BindingAccessOpcode::GetNameAndLocator => self
966                    .bytecode
967                    .emit_get_name_and_locator(value.variable(), (*index).into()),
968                BindingAccessOpcode::GetNameOrUndefined => self
969                    .bytecode
970                    .emit_get_name_or_undefined(value.variable(), (*index).into()),
971                BindingAccessOpcode::DeleteName => self
972                    .bytecode
973                    .emit_delete_name(value.variable(), (*index).into()),
974            },
975            BindingKind::Local(None) => {
976                let error_msg = self.get_or_insert_literal(Literal::String(js_string!(
977                    "access of uninitialized binding"
978                )));
979                self.bytecode
980                    .emit_throw_new_reference_error(error_msg.into());
981            }
982            BindingKind::Local(Some(index)) => match opcode {
983                BindingAccessOpcode::GetName
984                | BindingAccessOpcode::GetNameOrUndefined
985                | BindingAccessOpcode::GetNameAndLocator => {
986                    self.bytecode.emit_move(value.variable(), (*index).into());
987                }
988                BindingAccessOpcode::GetLocator
989                | BindingAccessOpcode::DefVar
990                | BindingAccessOpcode::DefEvalVar => {}
991                BindingAccessOpcode::SetName
992                | BindingAccessOpcode::DefInitVar
993                | BindingAccessOpcode::PutLexicalValue
994                | BindingAccessOpcode::SetNameByLocator => {
995                    self.bytecode.emit_move((*index).into(), value.variable());
996                }
997                BindingAccessOpcode::DeleteName => self.bytecode.emit_store_false(value.variable()),
998            },
999        }
1000    }
1001
1002    fn emit_get_property_by_name(
1003        &mut self,
1004        dst: &Register,
1005        receiver: Option<&Register>,
1006        value: &Register,
1007        ident: Sym,
1008    ) {
1009        let ic_index = self.ic.len() as u32;
1010
1011        let name_index = self.get_or_insert_name(ident);
1012        let Constant::String(ref name) = self.constants[name_index as usize].clone() else {
1013            unreachable!("there should be a string at index")
1014        };
1015        self.ic.push(InlineCache::new(name.clone()));
1016
1017        if let Some(receiver) = receiver {
1018            self.bytecode.emit_get_property_by_name_with_this(
1019                dst.variable(),
1020                receiver.variable(),
1021                value.variable(),
1022                ic_index.into(),
1023            );
1024        } else if name == &StaticJsStrings::LENGTH {
1025            self.bytecode.emit_get_length_property(
1026                dst.variable(),
1027                value.variable(),
1028                ic_index.into(),
1029            );
1030        } else {
1031            self.bytecode.emit_get_property_by_name(
1032                dst.variable(),
1033                value.variable(),
1034                ic_index.into(),
1035            );
1036        }
1037    }
1038
1039    fn emit_set_property_by_name(
1040        &mut self,
1041        value: &Register,
1042        receiver: Option<&Register>,
1043        object: &Register,
1044        ident: Sym,
1045    ) {
1046        let ic_index = self.ic.len() as u32;
1047
1048        let name_index = self.get_or_insert_name(ident);
1049        let Constant::String(ref name) = self.constants[name_index as usize].clone() else {
1050            unreachable!("there should be a string at index")
1051        };
1052        self.ic.push(InlineCache::new(name.clone()));
1053
1054        if let Some(receiver) = receiver {
1055            self.bytecode.emit_set_property_by_name_with_this(
1056                value.variable(),
1057                receiver.variable(),
1058                object.variable(),
1059                ic_index.into(),
1060            );
1061        } else {
1062            self.bytecode.emit_set_property_by_name(
1063                value.variable(),
1064                object.variable(),
1065                ic_index.into(),
1066            );
1067        }
1068    }
1069
1070    fn emit_type_error(&mut self, message: &str) {
1071        let error_msg = self.get_or_insert_literal(Literal::String(js_string!(message)));
1072        self.bytecode.emit_throw_new_type_error(error_msg.into());
1073    }
1074
1075    fn emit_store_integer(&mut self, value: i32, dst: &Register) {
1076        self.emit_store_integer_with_index(value, dst.variable());
1077    }
1078
1079    fn emit_store_integer_with_index(&mut self, value: i32, dst: RegisterOperand) {
1080        match value {
1081            0 => self.bytecode.emit_store_zero(dst),
1082            1 => self.bytecode.emit_store_one(dst),
1083            x if i32::from(x as i8) == x => self.bytecode.emit_store_int8(dst, x as i8),
1084            x if i32::from(x as i16) == x => {
1085                self.bytecode.emit_store_int16(dst, x as i16);
1086            }
1087            x => self.bytecode.emit_store_int32(dst, x),
1088        }
1089    }
1090
1091    fn emit_store_literal(&mut self, literal: Literal, dst: &Register) {
1092        let index = self.get_or_insert_literal(literal);
1093        self.bytecode
1094            .emit_store_literal(dst.variable(), index.into());
1095    }
1096
1097    fn emit_store_rational(&mut self, value: f64, dst: &Register) {
1098        if value.is_nan() {
1099            return self.bytecode.emit_store_nan(dst.variable());
1100        }
1101
1102        if value.is_infinite() {
1103            if value.is_sign_positive() {
1104                return self.bytecode.emit_store_positive_infinity(dst.variable());
1105            }
1106            return self.bytecode.emit_store_negative_infinity(dst.variable());
1107        }
1108
1109        // Check if the f64 value can fit in an i32.
1110        if f64::from(value as i32).to_bits() == value.to_bits() {
1111            self.emit_store_integer(value as i32, dst);
1112        } else {
1113            let f32_value = value as f32;
1114
1115            #[allow(clippy::float_cmp)]
1116            if f64::from(f32_value) == value {
1117                self.bytecode.emit_store_float(dst.variable(), f32_value);
1118            } else {
1119                self.bytecode.emit_store_double(dst.variable(), value);
1120            }
1121        }
1122    }
1123
1124    fn jump(&mut self) -> Label {
1125        let index = self.next_opcode_location();
1126        self.bytecode.emit_jump(Self::DUMMY_ADDRESS);
1127        Label { index }
1128    }
1129
1130    pub(crate) fn jump_if_true(&mut self, value: &Register) -> Label {
1131        let index = self.next_opcode_location();
1132        self.bytecode
1133            .emit_jump_if_true(Self::DUMMY_ADDRESS, value.variable());
1134        Label { index }
1135    }
1136
1137    pub(crate) fn if_else(
1138        &mut self,
1139        bool: &Register,
1140        true_case: impl FnOnce(&mut ByteCompiler<'_>),
1141        false_case: impl FnOnce(&mut ByteCompiler<'_>),
1142    ) {
1143        let jump_false = self.jump_if_false(bool);
1144
1145        // if true, jump to end to avoid running the code for the `else`
1146        true_case(self);
1147        let jump_to_end = self.jump();
1148
1149        // if false, we should be already at the end so no need to do anything.
1150        self.patch_jump(jump_false);
1151        false_case(self);
1152        self.patch_jump(jump_to_end);
1153    }
1154
1155    /// Generates the `if-else` pattern.
1156    ///
1157    /// This will also deallocate the `bool` register.
1158    pub(crate) fn if_else_with_dealloc(
1159        &mut self,
1160        bool: Register,
1161        true_case: impl FnOnce(&mut ByteCompiler<'_>),
1162        false_case: impl FnOnce(&mut ByteCompiler<'_>),
1163    ) {
1164        let jump_false = self.jump_if_false(&bool);
1165        self.register_allocator.dealloc(bool);
1166
1167        // if true, jump to end to avoid running the code for the `else`
1168        true_case(self);
1169        let jump_to_end = self.jump();
1170
1171        // if false, we should be already at the end so no need to do anything.
1172        self.patch_jump(jump_false);
1173        false_case(self);
1174        self.patch_jump(jump_to_end);
1175    }
1176
1177    pub(crate) fn jump_if_false(&mut self, value: &Register) -> Label {
1178        let index = self.next_opcode_location();
1179        self.bytecode
1180            .emit_jump_if_false(Self::DUMMY_ADDRESS, value.variable());
1181        Label { index }
1182    }
1183
1184    /// Compile a condition expression and emit a conditional jump.
1185    ///
1186    /// When the condition is a relational comparison (`<`, `<=`, `>`, `>=`),
1187    /// emits a single fused comparison+branch opcode instead of separate
1188    /// `LessThan` + `JumpIfFalse` instructions.
1189    ///
1190    /// If `hoisted` is provided, the pre-compiled register is used for one
1191    /// operand of the comparison, avoiding a constant reload on every iteration.
1192    pub(crate) fn compile_condition_and_branch(
1193        &mut self,
1194        condition: &Expression,
1195        hoisted: Option<&HoistedOperand>,
1196    ) -> Label {
1197        if let Expression::Binary(binary) = condition
1198            && let BinaryOp::Relational(op) = binary.op()
1199            && let Some(label) = self.try_fused_comparison_branch(op, binary, hoisted)
1200        {
1201            return label;
1202        }
1203        // Fallback: compile expr + jump_if_false
1204        let value = self.register_allocator.alloc();
1205        self.compile_expr(condition, &value);
1206        let label = self.jump_if_false(&value);
1207        self.register_allocator.dealloc(value);
1208        label
1209    }
1210
1211    /// Try to hoist a constant operand from a loop condition into a register
1212    /// that is loaded once before the loop, rather than on every iteration.
1213    ///
1214    /// Returns `Some(HoistedOperand)` when one side of a relational comparison
1215    /// is a literal value (e.g. `i < 10` hoists `10`).
1216    pub(crate) fn try_hoist_loop_condition(
1217        &mut self,
1218        condition: Option<&Expression>,
1219    ) -> Option<HoistedOperand> {
1220        let condition = condition?;
1221        let Expression::Binary(binary) = condition else {
1222            return None;
1223        };
1224        let BinaryOp::Relational(op) = binary.op() else {
1225            return None;
1226        };
1227        match op {
1228            RelationalOp::LessThan
1229            | RelationalOp::LessThanOrEqual
1230            | RelationalOp::GreaterThan
1231            | RelationalOp::GreaterThanOrEqual => {}
1232            _ => return None,
1233        }
1234        // Prefer hoisting RHS (most common pattern: `i < 10`)
1235        if self.is_loop_invariant(binary.rhs()) {
1236            let reg = self.register_allocator.alloc();
1237            self.compile_expr(binary.rhs(), &reg);
1238            return Some(HoistedOperand {
1239                register: reg,
1240                is_rhs: true,
1241            });
1242        }
1243        // Try LHS (less common: `0 < i`)
1244        if self.is_loop_invariant(binary.lhs()) {
1245            let reg = self.register_allocator.alloc();
1246            self.compile_expr(binary.lhs(), &reg);
1247            return Some(HoistedOperand {
1248                register: reg,
1249                is_rhs: false,
1250            });
1251        }
1252        None
1253    }
1254
1255    /// Returns `true` if the expression is loop-invariant and would benefit
1256    /// from being hoisted out of a loop (compiled once before the loop rather
1257    /// than on every iteration).
1258    ///
1259    /// An expression qualifies if it is:
1260    /// - A literal value (always immutable, always emits code)
1261    /// - A `const` identifier that is NOT already optimized by
1262    ///   [`compile_expr_operand`](Self::compile_expr_operand) (i.e., not local
1263    ///   and not in `const_binding_cache`), since those are already handled
1264    ///   with zero instructions inside the loop.
1265    fn is_loop_invariant(&self, expr: &Expression) -> bool {
1266        match expr {
1267            Expression::Literal(_) => true,
1268            Expression::Identifier(name) => {
1269                let name = self.resolve_identifier_expect(*name);
1270                let binding = self.lexical_scope.get_identifier_reference(name.clone());
1271                // Local bindings already use persistent registers directly
1272                // in compile_expr_operand — hoisting would add a redundant Move.
1273                if binding.local() {
1274                    return false;
1275                }
1276                // Cached const bindings are already handled by compile_expr_operand
1277                // without emitting code — hoisting would add a redundant Move.
1278                if !self.in_with && self.const_binding_cache.contains_key(&binding.locator()) {
1279                    return false;
1280                }
1281                // Only hoist if the binding is immutable (const).
1282                // Mutable bindings (let, var) can change between iterations.
1283                matches!(self.lexical_scope.is_binding_mutable(&name), Some(false))
1284            }
1285            _ => false,
1286        }
1287    }
1288
1289    fn try_fused_comparison_branch(
1290        &mut self,
1291        op: RelationalOp,
1292        binary: &Binary,
1293        hoisted: Option<&HoistedOperand>,
1294    ) -> Option<Label> {
1295        use crate::vm::opcode::BytecodeEmitter;
1296
1297        let emit_fn: fn(&mut BytecodeEmitter, Address, RegisterOperand, RegisterOperand) = match op
1298        {
1299            RelationalOp::LessThan => BytecodeEmitter::emit_jump_if_not_less_than,
1300            RelationalOp::LessThanOrEqual => BytecodeEmitter::emit_jump_if_not_less_than_or_equal,
1301            RelationalOp::GreaterThan => BytecodeEmitter::emit_jump_if_not_greater_than,
1302            RelationalOp::GreaterThanOrEqual => {
1303                BytecodeEmitter::emit_jump_if_not_greater_than_or_equal
1304            }
1305            _ => return None,
1306        };
1307        let mut label_index = Address::new(0);
1308        match hoisted {
1309            Some(h) if h.is_rhs => {
1310                let rhs = h.register.variable();
1311                self.compile_expr_operand(binary.lhs(), |compiler, lhs| {
1312                    label_index = compiler.next_opcode_location();
1313                    emit_fn(&mut compiler.bytecode, Self::DUMMY_ADDRESS, lhs, rhs);
1314                });
1315            }
1316            Some(h) => {
1317                let lhs = h.register.variable();
1318                self.compile_expr_operand(binary.rhs(), |compiler, rhs| {
1319                    label_index = compiler.next_opcode_location();
1320                    emit_fn(&mut compiler.bytecode, Self::DUMMY_ADDRESS, lhs, rhs);
1321                });
1322            }
1323            None => {
1324                self.compile_expr_operand(binary.lhs(), |compiler, lhs| {
1325                    compiler.compile_expr_operand(binary.rhs(), |compiler, rhs| {
1326                        label_index = compiler.next_opcode_location();
1327                        emit_fn(&mut compiler.bytecode, Self::DUMMY_ADDRESS, lhs, rhs);
1328                    });
1329                });
1330            }
1331        }
1332        Some(Label { index: label_index })
1333    }
1334
1335    pub(crate) fn jump_if_null_or_undefined(&mut self, value: &Register) -> Label {
1336        let index = self.next_opcode_location();
1337        self.bytecode
1338            .emit_jump_if_null_or_undefined(Self::DUMMY_ADDRESS, value.variable());
1339        Label { index }
1340    }
1341
1342    pub(crate) fn jump_if_not_undefined(&mut self, value: &Register) -> Label {
1343        let index = self.next_opcode_location();
1344        self.bytecode
1345            .emit_jump_if_not_undefined(Self::DUMMY_ADDRESS, value.variable());
1346        Label { index }
1347    }
1348
1349    pub(crate) fn jump_if_neq(&mut self, lhs: &Register, rhs: &Register) -> Label {
1350        let index = self.next_opcode_location();
1351        self.bytecode
1352            .emit_jump_if_not_equal(Self::DUMMY_ADDRESS, lhs.variable(), rhs.variable());
1353        Label { index }
1354    }
1355
1356    pub(crate) fn case(&mut self, value: &Register, condition: &Register) -> Label {
1357        let index = self.next_opcode_location();
1358        self.bytecode
1359            .emit_case(Self::DUMMY_ADDRESS, value.variable(), condition.variable());
1360        Label { index }
1361    }
1362
1363    pub(crate) fn template_lookup(&mut self, dst: &Register, site: u64) -> Label {
1364        let index = self.next_opcode_location();
1365        self.bytecode
1366            .emit_template_lookup(Self::DUMMY_ADDRESS, site, dst.variable());
1367        Label { index }
1368    }
1369
1370    fn emit_resume_kind(&mut self, resume_kind: GeneratorResumeKind, dst: &Register) {
1371        self.emit_store_integer(resume_kind as i32, dst);
1372    }
1373
1374    fn jump_if_not_resume_kind(
1375        &mut self,
1376        resume_kind: GeneratorResumeKind,
1377        value: &Register,
1378    ) -> Label {
1379        let r1 = self.register_allocator.alloc();
1380        self.emit_store_integer((resume_kind as u8).into(), &r1);
1381
1382        let index = self.next_opcode_location();
1383        self.bytecode
1384            .emit_jump_if_not_equal(Self::DUMMY_ADDRESS, r1.variable(), value.variable());
1385        self.register_allocator.dealloc(r1);
1386        Label { index }
1387    }
1388
1389    #[track_caller]
1390    pub(crate) fn patch_jump_with_target(&mut self, label: Label, target: Address) {
1391        self.bytecode.patch_jump(label.index, target);
1392    }
1393
1394    fn patch_jump(&mut self, label: Label) {
1395        let target = self.next_opcode_location();
1396        self.patch_jump_with_target(label, target);
1397    }
1398
1399    fn resolve_identifier_expect(&self, identifier: Identifier) -> JsString {
1400        identifier.to_js_string(self.interner())
1401    }
1402
1403    fn super_(&mut self, this: &Register, super_: &Register) {
1404        // Reuse super to avoid allocating a register just to get the function object.
1405        self.bytecode.emit_this(this.variable());
1406        self.bytecode.emit_get_function_object(super_.variable());
1407        self.bytecode.emit_get_home_object(super_.variable());
1408
1409        let r1 = self.register_allocator.alloc();
1410        self.bytecode.emit_store_null(r1.variable());
1411        // jump to evaluation if `super` is already a valid home object.
1412        let skip_move = self.jump_if_neq(&r1, super_);
1413        self.bytecode.emit_move(r1.variable(), this.variable());
1414        self.bytecode.emit_is_object(r1.variable());
1415
1416        // If `this` is also not an object, then `super` should be left as `null`.
1417        // Jump to the end in this case because `super` is already `null`.
1418        let skip_eval = self.jump_if_false(&r1);
1419        self.register_allocator.dealloc(r1);
1420
1421        // `this` is an object, so replace `super` with it and get its
1422        // prototype.
1423        self.bytecode.emit_move(super_.variable(), this.variable());
1424        self.patch_jump(skip_move);
1425
1426        self.bytecode.emit_get_prototype(super_.variable());
1427
1428        self.patch_jump(skip_eval);
1429    }
1430
1431    fn access_get(&mut self, access: Access<'_>, dst: &Register) {
1432        match access {
1433            Access::Variable { name } => {
1434                let name = self.resolve_identifier_expect(name);
1435                let binding = self.lexical_scope.get_identifier_reference(name);
1436                let index = self.get_binding(&binding);
1437                if !self.in_with
1438                    && let Some(&cached_reg) = self.const_binding_cache.get(&binding.locator())
1439                {
1440                    self.bytecode.emit_move(dst.variable(), cached_reg.into());
1441                    return;
1442                }
1443                self.emit_binding_access(BindingAccessOpcode::GetName, &index, dst);
1444            }
1445            Access::Property { access } => match access {
1446                PropertyAccess::Simple(access) => {
1447                    let mut compiler = self.position_guard(access.field());
1448
1449                    let object = compiler.register_allocator.alloc();
1450                    compiler.compile_expr(access.target(), &object);
1451
1452                    match access.field() {
1453                        PropertyAccessField::Const(ident) => {
1454                            compiler.emit_get_property_by_name(dst, None, &object, ident.sym());
1455                        }
1456                        PropertyAccessField::Expr(expr) => {
1457                            let key = compiler.register_allocator.alloc();
1458                            compiler.compile_expr(expr, &key);
1459                            compiler.bytecode.emit_get_property_by_value(
1460                                dst.variable(),
1461                                key.variable(),
1462                                object.variable(),
1463                                object.variable(),
1464                            );
1465                            compiler.register_allocator.dealloc(key);
1466                        }
1467                    }
1468                    compiler.register_allocator.dealloc(object);
1469                }
1470                PropertyAccess::Private(access) => {
1471                    let mut compiler = self.position_guard(access.field());
1472
1473                    let index = compiler.get_or_insert_private_name(access.field());
1474                    let object = compiler.register_allocator.alloc();
1475                    compiler.compile_expr(access.target(), &object);
1476                    compiler.bytecode.emit_get_private_field(
1477                        dst.variable(),
1478                        object.variable(),
1479                        index.into(),
1480                    );
1481                    compiler.register_allocator.dealloc(object);
1482                }
1483                PropertyAccess::Super(access) => {
1484                    let mut compiler = self.position_guard(access.field());
1485
1486                    let value = compiler.register_allocator.alloc();
1487                    let receiver = compiler.register_allocator.alloc();
1488                    compiler.super_(&receiver, &value);
1489
1490                    match access.field() {
1491                        PropertyAccessField::Const(ident) => {
1492                            compiler.emit_get_property_by_name(
1493                                dst,
1494                                Some(&receiver),
1495                                &value,
1496                                ident.sym(),
1497                            );
1498                        }
1499                        PropertyAccessField::Expr(expr) => {
1500                            let key = compiler.register_allocator.alloc();
1501                            compiler.compile_expr(expr, &key);
1502                            compiler.bytecode.emit_get_property_by_value(
1503                                dst.variable(),
1504                                key.variable(),
1505                                receiver.variable(),
1506                                value.variable(),
1507                            );
1508                            compiler.register_allocator.dealloc(key);
1509                        }
1510                    }
1511                    compiler.register_allocator.dealloc(receiver);
1512                    compiler.register_allocator.dealloc(value);
1513                }
1514            },
1515            Access::This => {
1516                self.bytecode.emit_this(dst.variable());
1517            }
1518        }
1519    }
1520
1521    fn access_set<'a, F>(&mut self, access: Access<'_>, expr_fn: F)
1522    where
1523        F: FnOnce(&mut ByteCompiler<'_>) -> &'a Register,
1524    {
1525        match access {
1526            Access::Variable { name } => {
1527                let name = self.resolve_identifier_expect(name);
1528                let binding = self.lexical_scope.get_identifier_reference(name.clone());
1529                let is_lexical = binding.is_lexical();
1530                let index = self.get_binding(&binding);
1531
1532                let value = self.register_allocator.alloc();
1533                if !is_lexical {
1534                    self.emit_binding_access(BindingAccessOpcode::GetLocator, &index, &value);
1535                }
1536                self.register_allocator.dealloc(value);
1537
1538                let value = expr_fn(self);
1539
1540                if is_lexical {
1541                    match self.lexical_scope.set_mutable_binding(name.clone()) {
1542                        Ok(binding) => {
1543                            let index = self.insert_binding(binding);
1544                            self.emit_binding_access(BindingAccessOpcode::SetName, &index, value);
1545                        }
1546                        Err(BindingLocatorError::MutateImmutable) => {
1547                            let index = self.get_or_insert_string(name);
1548                            self.bytecode.emit_throw_mutate_immutable(index.into());
1549                        }
1550                        Err(BindingLocatorError::Silent) => {}
1551                    }
1552                } else {
1553                    self.emit_binding_access(BindingAccessOpcode::SetNameByLocator, &index, value);
1554                }
1555            }
1556            Access::Property { access } => match access {
1557                PropertyAccess::Simple(access) => match access.field() {
1558                    PropertyAccessField::Const(name) => {
1559                        let object = self.register_allocator.alloc();
1560                        self.compile_expr(access.target(), &object);
1561                        let value = expr_fn(self);
1562                        self.emit_set_property_by_name(value, None, &object, name.sym());
1563                        self.register_allocator.dealloc(object);
1564                    }
1565                    PropertyAccessField::Expr(expr) => {
1566                        let object = self.register_allocator.alloc();
1567                        self.compile_expr(access.target(), &object);
1568
1569                        let key = self.register_allocator.alloc();
1570                        self.compile_expr(expr, &key);
1571
1572                        let value = expr_fn(self);
1573
1574                        self.bytecode.emit_set_property_by_value(
1575                            value.variable(),
1576                            key.variable(),
1577                            object.variable(),
1578                            object.variable(),
1579                        );
1580
1581                        self.register_allocator.dealloc(object);
1582                        self.register_allocator.dealloc(key);
1583                    }
1584                },
1585                PropertyAccess::Private(access) => {
1586                    let index = self.get_or_insert_private_name(access.field());
1587
1588                    let object = self.register_allocator.alloc();
1589                    self.compile_expr(access.target(), &object);
1590
1591                    let value = expr_fn(self);
1592
1593                    self.bytecode.emit_set_private_field(
1594                        value.variable(),
1595                        object.variable(),
1596                        index.into(),
1597                    );
1598
1599                    self.register_allocator.dealloc(object);
1600                }
1601                PropertyAccess::Super(access) => match access.field() {
1602                    PropertyAccessField::Const(name) => {
1603                        let object = self.register_allocator.alloc();
1604                        let receiver = self.register_allocator.alloc();
1605                        self.super_(&receiver, &object);
1606
1607                        let value = expr_fn(self);
1608
1609                        self.emit_set_property_by_name(value, Some(&receiver), &object, name.sym());
1610
1611                        self.register_allocator.dealloc(receiver);
1612                        self.register_allocator.dealloc(object);
1613                    }
1614                    PropertyAccessField::Expr(expr) => {
1615                        let object = self.register_allocator.alloc();
1616                        let receiver = self.register_allocator.alloc();
1617                        self.super_(&receiver, &object);
1618
1619                        let key = self.register_allocator.alloc();
1620                        self.compile_expr(expr, &key);
1621
1622                        let value = expr_fn(self);
1623
1624                        self.bytecode.emit_set_property_by_value(
1625                            value.variable(),
1626                            key.variable(),
1627                            receiver.variable(),
1628                            object.variable(),
1629                        );
1630
1631                        self.register_allocator.dealloc(key);
1632                        self.register_allocator.dealloc(receiver);
1633                        self.register_allocator.dealloc(object);
1634                    }
1635                },
1636            },
1637            Access::This => {
1638                // In non-strict code, assignment to `this` is a no-op per spec; we still evaluate
1639                // the RHS for side effects (e.g. `this = foo()` must call foo()).
1640                let _ = expr_fn(self);
1641            }
1642        }
1643    }
1644
1645    fn access_delete(&mut self, access: Access<'_>, dst: &Register) {
1646        match access {
1647            Access::Property { access } => match access {
1648                PropertyAccess::Simple(access) => match access.field() {
1649                    PropertyAccessField::Const(name) => {
1650                        let index = self.get_or_insert_name(name.sym());
1651                        self.compile_expr(access.target(), dst);
1652                        self.bytecode
1653                            .emit_delete_property_by_name(dst.variable(), index.into());
1654                    }
1655                    PropertyAccessField::Expr(expr) => {
1656                        self.compile_expr(access.target(), dst);
1657                        let key = self.register_allocator.alloc();
1658                        self.compile_expr(expr, &key);
1659                        self.bytecode
1660                            .emit_delete_property_by_value(dst.variable(), key.variable());
1661                        self.register_allocator.dealloc(key);
1662                    }
1663                },
1664                PropertyAccess::Super(_) => self.bytecode.emit_delete_super_throw(),
1665                PropertyAccess::Private(_) => {
1666                    unreachable!("deleting private properties should always throw early errors.")
1667                }
1668            },
1669            Access::Variable { name } => {
1670                let name = name.to_js_string(self.interner());
1671                let binding = self.lexical_scope.get_identifier_reference(name);
1672                let index = self.get_binding(&binding);
1673                self.emit_binding_access(BindingAccessOpcode::DeleteName, &index, dst);
1674            }
1675            Access::This => self.bytecode.emit_store_true(dst.variable()),
1676        }
1677    }
1678
1679    /// Compile a [`StatementList`].
1680    pub fn compile_statement_list(&mut self, list: &StatementList, use_expr: bool, block: bool) {
1681        if use_expr || self.jump_control_info_has_use_expr() {
1682            let mut use_expr_index = 0;
1683            for (i, statement) in list.statements().iter().enumerate() {
1684                match statement {
1685                    StatementListItem::Statement(statement) => match statement.as_ref() {
1686                        Statement::Break(_) | Statement::Continue(_) => break,
1687                        Statement::Empty | Statement::Var(_) => {}
1688                        Statement::Block(block) if !returns_value(block) => {}
1689                        _ => use_expr_index = i,
1690                    },
1691                    StatementListItem::Declaration(_) => {}
1692                }
1693            }
1694
1695            for (i, item) in list.statements().iter().enumerate() {
1696                self.compile_stmt_list_item(item, i == use_expr_index, block);
1697            }
1698        } else {
1699            for item in list.statements() {
1700                self.compile_stmt_list_item(item, false, block);
1701            }
1702        }
1703    }
1704
1705    /// Compile an [`Expression`].
1706    #[inline]
1707    pub(crate) fn compile_expr(&mut self, expr: &Expression, dst: &'_ Register) {
1708        self.compile_expr_impl(expr, dst);
1709    }
1710
1711    /// Compile an expression purely for its side effects, discarding the result.
1712    ///
1713    /// This avoids allocating a register and emitting a store for the result
1714    /// when it's not needed (e.g., expression statements, for-loop updates).
1715    pub(crate) fn compile_expr_for_side_effects(&mut self, expr: &Expression) {
1716        match expr {
1717            Expression::Call(call) => {
1718                self.call(Callable::Call(call), CallResultDest::Discard);
1719            }
1720            Expression::New(new) => {
1721                self.call(Callable::New(new), CallResultDest::Discard);
1722            }
1723            Expression::Update(update) => {
1724                let tmp = self.register_allocator.alloc();
1725                self.compile_update(update, &tmp, true);
1726                self.register_allocator.dealloc(tmp);
1727            }
1728            Expression::Parenthesized(parenthesized) => {
1729                self.compile_expr_for_side_effects(parenthesized.expression());
1730            }
1731            _ => {
1732                let tmp = self.register_allocator.alloc();
1733                self.compile_expr(expr, &tmp);
1734                self.register_allocator.dealloc(tmp);
1735            }
1736        }
1737    }
1738
1739    /// Compile an expression and leave the result on the stack.
1740    ///
1741    /// For call/new expressions, this avoids the `PopIntoRegister` + `PushFromRegister`
1742    /// round-trip by leaving the call result directly on the stack. For identifier
1743    /// expressions resolving to a local or const-cached register, this pushes from
1744    /// that register directly, avoiding a `Move` into a temporary.
1745    pub(crate) fn compile_expr_to_stack(&mut self, expr: &Expression) {
1746        match expr {
1747            Expression::Call(call) => {
1748                self.call(Callable::Call(call), CallResultDest::Stack);
1749            }
1750            Expression::New(new) => {
1751                self.call(Callable::New(new), CallResultDest::Stack);
1752            }
1753            Expression::Parenthesized(parenthesized) => {
1754                self.compile_expr_to_stack(parenthesized.expression());
1755            }
1756            _ => {
1757                self.compile_expr_operand(expr, |this, op| {
1758                    this.bytecode.emit_push_from_register(op);
1759                });
1760            }
1761        }
1762    }
1763
1764    /// Compile an expression and return the operand where the result lives.
1765    ///
1766    /// For local variable references, this returns the local's persistent register
1767    /// directly without emitting a `Move` instruction. For all other expressions,
1768    /// it allocates a temporary register and compiles into it.
1769    ///
1770    /// The `inner_fn` passed in will be called before the register get deallocated.
1771    pub(crate) fn compile_expr_operand(
1772        &mut self,
1773        expr: &Expression,
1774        inner_fn: impl FnOnce(&mut Self, RegisterOperand),
1775    ) {
1776        if let Expression::Identifier(name) = expr {
1777            let name = self.resolve_identifier_expect(*name);
1778            let binding = self.lexical_scope.get_identifier_reference(name);
1779            let index = self.get_binding(&binding);
1780            if let BindingKind::Local(Some(local_reg)) = &index {
1781                inner_fn(self, RegisterOperand::from(*local_reg));
1782                return;
1783            }
1784            if !self.in_with
1785                && let Some(&cached_reg) = self.const_binding_cache.get(&binding.locator())
1786            {
1787                inner_fn(self, cached_reg.into());
1788                return;
1789            }
1790        }
1791        let reg = self.register_allocator.alloc();
1792        self.compile_expr(expr, &reg);
1793        let op = reg.variable();
1794        inner_fn(self, op);
1795        self.register_allocator.dealloc(reg);
1796    }
1797
1798    /// Compile a property access expression, prepending `this` to the property value in the stack.
1799    ///
1800    /// This compiles the access in a way that the state of the stack after executing the property
1801    /// access becomes `...rest, this, value`. where `...rest` is the rest of the stack, `this` is the
1802    /// `this` value of the access, and `value` is the final result of the access.
1803    ///
1804    /// This is mostly useful for optional chains with calls (`a.b?.()`) and for regular chains
1805    /// with calls (`a.b()`), since both of them must have `a` be the value of `this` for the function
1806    /// call `b()`, but a regular compilation of the access would lose the `this` value after accessing
1807    /// `b`.
1808    fn compile_access_preserve_this(
1809        &mut self,
1810        access: &PropertyAccess,
1811        this: &Register,
1812        dst: &Register,
1813    ) {
1814        match access {
1815            PropertyAccess::Simple(access) => {
1816                self.compile_expr(access.target(), this);
1817
1818                match access.field() {
1819                    PropertyAccessField::Const(ident) => {
1820                        self.emit_get_property_by_name(dst, None, this, ident.sym());
1821                    }
1822                    PropertyAccessField::Expr(field) => {
1823                        let key = self.register_allocator.alloc();
1824                        self.compile_expr(field, &key);
1825                        self.bytecode.emit_get_property_by_value(
1826                            dst.variable(),
1827                            key.variable(),
1828                            this.variable(),
1829                            this.variable(),
1830                        );
1831                        self.register_allocator.dealloc(key);
1832                    }
1833                }
1834            }
1835            PropertyAccess::Private(access) => {
1836                self.compile_expr(access.target(), this);
1837
1838                let index = self.get_or_insert_private_name(access.field());
1839                self.bytecode
1840                    .emit_get_private_field(dst.variable(), this.variable(), index.into());
1841            }
1842            PropertyAccess::Super(access) => {
1843                let object = self.register_allocator.alloc();
1844                self.super_(this, &object);
1845
1846                match access.field() {
1847                    PropertyAccessField::Const(ident) => {
1848                        self.emit_get_property_by_name(dst, Some(this), &object, ident.sym());
1849                    }
1850                    PropertyAccessField::Expr(expr) => {
1851                        let key = self.register_allocator.alloc();
1852                        self.compile_expr(expr, &key);
1853                        self.bytecode.emit_get_property_by_value(
1854                            dst.variable(),
1855                            key.variable(),
1856                            this.variable(),
1857                            object.variable(),
1858                        );
1859                        self.register_allocator.dealloc(key);
1860                    }
1861                }
1862                self.register_allocator.dealloc(object);
1863            }
1864        }
1865    }
1866
1867    /// Compile an optional chain expression, prepending `this` to the property value in the stack.
1868    ///
1869    /// This compiles the access in a way that the state of the stack after executing the optional
1870    /// chain becomes `...rest, this, value`. where `...rest` is the rest of the stack, `this` is the
1871    /// `this` value of the chain, and `value` is the result of the chain.
1872    ///
1873    /// This is mostly useful for inner optional chains with external calls (`(a?.b)()`), because the
1874    /// external call is not in the optional chain, and compiling an optional chain in the usual way
1875    /// would only return the result of the chain without preserving the `this` value. In other words,
1876    /// `this` would be set to `undefined` for that call, which is incorrect since `a` should be the
1877    /// `this` value of the call.
1878    fn compile_optional_preserve_this(
1879        &mut self,
1880        optional: &Optional,
1881        this: &Register,
1882        value: &Register,
1883    ) {
1884        let mut jumps = Vec::with_capacity(optional.chain().len());
1885
1886        match optional.target().flatten() {
1887            Expression::PropertyAccess(access) => {
1888                self.compile_access_preserve_this(access, this, value);
1889            }
1890            Expression::Optional(opt) => self.compile_optional_preserve_this(opt, this, value),
1891            expr => {
1892                self.bytecode.emit_store_undefined(this.variable());
1893                self.compile_expr(expr, value);
1894            }
1895        }
1896
1897        jumps.push(self.jump_if_null_or_undefined(value));
1898
1899        let (first, rest) = optional
1900            .chain()
1901            .split_first()
1902            .expect("chain must have at least one element");
1903        assert!(first.shorted());
1904
1905        self.compile_optional_item_kind(first.kind(), this, value);
1906
1907        for item in rest {
1908            if item.shorted() {
1909                jumps.push(self.jump_if_null_or_undefined(value));
1910            }
1911            self.compile_optional_item_kind(item.kind(), this, value);
1912        }
1913
1914        let skip_undef = self.jump();
1915
1916        for label in jumps {
1917            self.patch_jump(label);
1918            self.bytecode.emit_store_undefined(value.variable());
1919        }
1920
1921        self.patch_jump(skip_undef);
1922    }
1923
1924    /// Compile a `delete` expression on an optional chain.
1925    ///
1926    /// Follows the same short-circuit logic as `compile_optional_preserve_this`, but
1927    /// emits delete opcodes for the final link in the chain instead of get opcodes.
1928    /// When the chain short-circuits (base is null/undefined), returns `true` per spec.
1929    pub(crate) fn compile_optional_delete(&mut self, optional: &Optional, dst: &Register) {
1930        let value = self.register_allocator.alloc();
1931        let this = self.register_allocator.alloc();
1932
1933        let mut jumps = Vec::with_capacity(optional.chain().len());
1934
1935        // Compile the target expression (base of the chain).
1936        match optional.target().flatten() {
1937            Expression::PropertyAccess(access) => {
1938                self.compile_access_preserve_this(access, &this, &value);
1939            }
1940            Expression::Optional(opt) => {
1941                self.compile_optional_preserve_this(opt, &this, &value);
1942            }
1943            expr => {
1944                self.bytecode.emit_store_undefined(this.variable());
1945                self.compile_expr(expr, &value);
1946            }
1947        }
1948
1949        jumps.push(self.jump_if_null_or_undefined(&value));
1950
1951        let chain = optional.chain();
1952        let chain_len = chain.len();
1953
1954        let (first, rest) = chain
1955            .split_first()
1956            .expect("chain must have at least one element");
1957        assert!(first.shorted());
1958
1959        if chain_len == 1 {
1960            // The only item is also the last — emit delete.
1961            self.compile_optional_delete_final(first.kind(), &this, &value, dst);
1962        } else {
1963            // Not the last — emit a regular get.
1964            self.compile_optional_item_kind(first.kind(), &this, &value);
1965
1966            for (i, item) in rest.iter().enumerate() {
1967                if item.shorted() {
1968                    jumps.push(self.jump_if_null_or_undefined(&value));
1969                }
1970                if i == rest.len() - 1 {
1971                    // Last item — emit delete.
1972                    self.compile_optional_delete_final(item.kind(), &this, &value, dst);
1973                } else {
1974                    self.compile_optional_item_kind(item.kind(), &this, &value);
1975                }
1976            }
1977        }
1978
1979        let skip_true = self.jump();
1980
1981        // Short-circuit path: delete on null/undefined base returns true per spec.
1982        for label in jumps {
1983            self.patch_jump(label);
1984        }
1985        self.bytecode.emit_store_true(dst.variable());
1986
1987        self.patch_jump(skip_true);
1988
1989        self.register_allocator.dealloc(this);
1990        self.register_allocator.dealloc(value);
1991    }
1992
1993    /// Emit delete opcodes for the final operation in an optional chain.
1994    fn compile_optional_delete_final(
1995        &mut self,
1996        kind: &OptionalOperationKind,
1997        this: &Register,
1998        value: &Register,
1999        dst: &Register,
2000    ) {
2001        match kind {
2002            OptionalOperationKind::SimplePropertyAccess { field } => {
2003                self.bytecode.emit_move(this.variable(), value.variable());
2004                match field {
2005                    PropertyAccessField::Const(name) => {
2006                        let index = self.get_or_insert_name(name.sym());
2007                        self.bytecode.emit_move(dst.variable(), value.variable());
2008                        self.bytecode
2009                            .emit_delete_property_by_name(dst.variable(), index.into());
2010                    }
2011                    PropertyAccessField::Expr(expr) => {
2012                        self.bytecode.emit_move(dst.variable(), value.variable());
2013                        let key = self.register_allocator.alloc();
2014                        self.compile_expr(expr, &key);
2015                        self.bytecode
2016                            .emit_delete_property_by_value(dst.variable(), key.variable());
2017                        self.register_allocator.dealloc(key);
2018                    }
2019                }
2020            }
2021            OptionalOperationKind::PrivatePropertyAccess { .. } => {
2022                unreachable!("deleting private properties should always throw early errors.")
2023            }
2024            OptionalOperationKind::Call { .. } => {
2025                // `delete obj?.method()` — evaluate the call, then return true.
2026                self.compile_optional_item_kind(kind, this, value);
2027                self.bytecode.emit_store_true(dst.variable());
2028            }
2029        }
2030    }
2031
2032    /// Compile a single operation in an optional chain.
2033    ///
2034    /// On successful compilation, the state of the stack on execution will become `...rest, this, value`,
2035    /// where `this` is the target of the property access (`undefined` on calls), and `value` is the
2036    /// result of executing the action.
2037    /// For example, in the expression `a?.b.c()`, after compiling and executing:
2038    ///
2039    /// - `a?.b`, the state of the stack will become `...rest, a, b`.
2040    /// - `b.c`, the state of the stack will become `...rest, b, c`.
2041    /// - `c()`, the state of the stack will become `...rest, undefined, c()`.
2042    ///
2043    /// # Requirements
2044    /// - This should only be called after verifying that the previous value of the chain
2045    ///   is not null or undefined (if the operator `?.` was used).
2046    /// - This assumes that the state of the stack before compiling is `...rest, this, value`,
2047    ///   since the operation compiled by this function could be a call.
2048    fn compile_optional_item_kind(
2049        &mut self,
2050        kind: &OptionalOperationKind,
2051        this: &Register,
2052        value: &Register,
2053    ) {
2054        match kind {
2055            OptionalOperationKind::SimplePropertyAccess { field } => {
2056                self.bytecode.emit_move(this.variable(), value.variable());
2057                match field {
2058                    PropertyAccessField::Const(name) => {
2059                        self.emit_get_property_by_name(value, None, value, name.sym());
2060                    }
2061                    PropertyAccessField::Expr(expr) => {
2062                        let key = self.register_allocator.alloc();
2063                        self.compile_expr(expr, &key);
2064                        self.bytecode.emit_get_property_by_value(
2065                            value.variable(),
2066                            key.variable(),
2067                            value.variable(),
2068                            value.variable(),
2069                        );
2070                        self.register_allocator.dealloc(key);
2071                    }
2072                }
2073            }
2074            OptionalOperationKind::PrivatePropertyAccess { field } => {
2075                self.bytecode.emit_move(this.variable(), value.variable());
2076                let index = self.get_or_insert_private_name(*field);
2077                self.bytecode.emit_get_private_field(
2078                    value.variable(),
2079                    value.variable(),
2080                    index.into(),
2081                );
2082            }
2083            OptionalOperationKind::Call { args } => {
2084                self.push_from_register(this);
2085                self.push_from_register(value);
2086
2087                let args = &**args;
2088                let contains_spread = args.iter().any(|arg| matches!(arg, Expression::Spread(_)));
2089
2090                if contains_spread {
2091                    let array = self.register_allocator.alloc();
2092                    let array_op = array.variable();
2093
2094                    self.bytecode.emit_store_new_array(array_op);
2095
2096                    for arg in args {
2097                        if let Expression::Spread(spread) = arg {
2098                            let value = self.register_allocator.alloc();
2099                            self.compile_expr(spread.target(), &value);
2100                            self.bytecode.emit_get_iterator(value.variable());
2101                            self.bytecode.emit_push_iterator_to_array(array_op);
2102                            self.register_allocator.dealloc(value);
2103                        } else {
2104                            self.compile_expr_operand(arg, |this, op| {
2105                                this.bytecode.emit_push_value_to_array(op, array_op);
2106                            });
2107                        }
2108                    }
2109
2110                    self.push_from_register(&array);
2111                    self.register_allocator.dealloc(array);
2112
2113                    self.bytecode.emit_call_spread();
2114                } else {
2115                    for arg in args {
2116                        self.compile_expr_to_stack(arg);
2117                    }
2118                    self.bytecode.emit_call((args.len() as u32).into());
2119                }
2120
2121                self.pop_into_register(value);
2122                self.bytecode.emit_store_undefined(this.variable());
2123            }
2124        }
2125    }
2126
2127    /// Compile a [`VarDeclaration`].
2128    fn compile_var_decl(&mut self, decl: &VarDeclaration) {
2129        for variable in decl.0.as_ref() {
2130            match variable.binding() {
2131                Binding::Identifier(ident) => {
2132                    let ident = ident.to_js_string(self.interner());
2133                    if let Some(expr) = variable.init() {
2134                        let binding = self.lexical_scope.get_identifier_reference(ident.clone());
2135                        let index = self.insert_binding(binding);
2136                        let value = self.register_allocator.alloc();
2137                        self.emit_binding_access(BindingAccessOpcode::GetLocator, &index, &value);
2138                        self.compile_expr(expr, &value);
2139                        self.emit_binding_access(
2140                            BindingAccessOpcode::SetNameByLocator,
2141                            &index,
2142                            &value,
2143                        );
2144                        self.register_allocator.dealloc(value);
2145                    } else {
2146                        let value = self.register_allocator.alloc();
2147                        self.emit_binding(BindingOpcode::Var, ident, &value);
2148                        self.register_allocator.dealloc(value);
2149                    }
2150                }
2151                Binding::Pattern(pattern) => {
2152                    let value = self.register_allocator.alloc();
2153                    if let Some(init) = variable.init() {
2154                        self.compile_expr(init, &value);
2155                    } else {
2156                        self.bytecode.emit_store_undefined(value.variable());
2157                    }
2158                    self.compile_declaration_pattern(pattern, BindingOpcode::InitVar, &value);
2159                    self.register_allocator.dealloc(value);
2160                }
2161            }
2162        }
2163    }
2164
2165    /// Compile a [`LexicalDeclaration`].
2166    fn compile_lexical_decl(&mut self, decl: &LexicalDeclaration) {
2167        match decl {
2168            LexicalDeclaration::Let(decls) => {
2169                for variable in decls.as_ref() {
2170                    match variable.binding() {
2171                        Binding::Identifier(ident) => {
2172                            let ident = ident.to_js_string(self.interner());
2173                            let binding = self.lexical_scope.get_identifier_reference(ident);
2174                            if binding.local() {
2175                                // Pre-allocate the persistent register but do NOT
2176                                // insert into local_binding_registers yet, so that
2177                                // TDZ checks still fire during initializer compilation.
2178                                let reg = self.register_allocator.alloc_persistent();
2179                                if let Some(init) = variable.init() {
2180                                    self.compile_expr(init, &reg);
2181                                } else {
2182                                    self.bytecode.emit_store_undefined(reg.variable());
2183                                }
2184                                self.local_binding_registers.insert(binding, reg.index());
2185                            } else {
2186                                let value = self.register_allocator.alloc();
2187                                if let Some(init) = variable.init() {
2188                                    self.compile_expr(init, &value);
2189                                } else {
2190                                    self.bytecode.emit_store_undefined(value.variable());
2191                                }
2192                                let binding_kind = self.insert_binding(binding);
2193                                self.emit_binding_access(
2194                                    BindingAccessOpcode::PutLexicalValue,
2195                                    &binding_kind,
2196                                    &value,
2197                                );
2198                                self.register_allocator.dealloc(value);
2199                            }
2200                        }
2201                        Binding::Pattern(pattern) => {
2202                            let value = self.register_allocator.alloc();
2203                            if let Some(init) = variable.init() {
2204                                self.compile_expr(init, &value);
2205                            } else {
2206                                self.bytecode.emit_store_undefined(value.variable());
2207                            }
2208                            self.compile_declaration_pattern(
2209                                pattern,
2210                                BindingOpcode::InitLexical,
2211                                &value,
2212                            );
2213                            self.register_allocator.dealloc(value);
2214                        }
2215                    }
2216                }
2217            }
2218            LexicalDeclaration::Const(decls) => {
2219                for variable in decls.as_ref() {
2220                    match variable.binding() {
2221                        Binding::Identifier(ident) => {
2222                            let ident = ident.to_js_string(self.interner());
2223                            let init = variable
2224                                .init()
2225                                .expect("const declaration must have initializer");
2226                            let binding =
2227                                self.lexical_scope.get_identifier_reference(ident.clone());
2228                            if binding.local() {
2229                                let reg = self.register_allocator.alloc_persistent();
2230                                self.compile_expr(init, &reg);
2231                                self.local_binding_registers.insert(binding, reg.index());
2232                            } else {
2233                                let value = self.register_allocator.alloc();
2234                                self.compile_expr(init, &value);
2235                                let binding_kind = self.insert_binding(binding.clone());
2236                                self.emit_binding_access(
2237                                    BindingAccessOpcode::PutLexicalValue,
2238                                    &binding_kind,
2239                                    &value,
2240                                );
2241                                // Cache non-local const bindings in a persistent register
2242                                // so subsequent reads avoid GetName environment lookups.
2243                                let cache_reg = self.register_allocator.alloc_persistent();
2244                                self.bytecode
2245                                    .emit_move(cache_reg.variable(), value.variable());
2246                                self.const_binding_cache
2247                                    .insert(binding.locator(), cache_reg.index());
2248                                self.register_allocator.dealloc(value);
2249                            }
2250                        }
2251                        Binding::Pattern(pattern) => {
2252                            let value = self.register_allocator.alloc();
2253                            if let Some(init) = variable.init() {
2254                                self.compile_expr(init, &value);
2255                            } else {
2256                                self.bytecode.emit_store_undefined(value.variable());
2257                            }
2258                            self.compile_declaration_pattern(
2259                                pattern,
2260                                BindingOpcode::InitLexical,
2261                                &value,
2262                            );
2263                            self.register_allocator.dealloc(value);
2264                        }
2265                    }
2266                }
2267            }
2268            LexicalDeclaration::Using(decls) => {
2269                // For each using declaration, we need to:
2270                // 1. Evaluate the initializer
2271                // 2. Add the resource to the disposal stack
2272                // 3. Bind the variable
2273                for variable in decls.as_ref() {
2274                    match variable.binding() {
2275                        Binding::Identifier(ident) => {
2276                            let ident = ident.to_js_string(self.interner());
2277                            let value = self.register_allocator.alloc();
2278
2279                            if let Some(init) = variable.init() {
2280                                self.compile_expr(init, &value);
2281                            } else {
2282                                self.bytecode.emit_store_undefined(value.variable());
2283                            }
2284
2285                            // TODO(@abhinavs1920): Add resource to disposal stack
2286                            // For now, we just bind the variable like a let declaration
2287                            // Full implementation will add: AddDisposableResource opcode
2288
2289                            self.emit_binding(BindingOpcode::InitLexical, ident, &value);
2290                            self.register_allocator.dealloc(value);
2291                        }
2292                        Binding::Pattern(pattern) => {
2293                            let value = self.register_allocator.alloc();
2294
2295                            if let Some(init) = variable.init() {
2296                                self.compile_expr(init, &value);
2297                            } else {
2298                                self.bytecode.emit_store_undefined(value.variable());
2299                            }
2300
2301                            // TODO: Same as above
2302
2303                            self.compile_declaration_pattern(
2304                                pattern,
2305                                BindingOpcode::InitLexical,
2306                                &value,
2307                            );
2308                            self.register_allocator.dealloc(value);
2309                        }
2310                    }
2311                }
2312            }
2313            LexicalDeclaration::AwaitUsing(decls) => {
2314                for variable in decls.as_ref() {
2315                    match variable.binding() {
2316                        Binding::Identifier(ident) => {
2317                            let ident = ident.to_js_string(self.interner());
2318                            let value = self.register_allocator.alloc();
2319
2320                            if let Some(init) = variable.init() {
2321                                self.compile_expr(init, &value);
2322                            } else {
2323                                self.bytecode.emit_store_undefined(value.variable());
2324                            }
2325
2326                            // TODO: Add resource to async disposal stack
2327                            // For now, we just bind the variable like a let declaration
2328                            // Full implementation will add: AddAsyncDisposableResource opcode
2329
2330                            self.emit_binding(BindingOpcode::InitLexical, ident, &value);
2331                            self.register_allocator.dealloc(value);
2332                        }
2333                        Binding::Pattern(pattern) => {
2334                            let value = self.register_allocator.alloc();
2335
2336                            if let Some(init) = variable.init() {
2337                                self.compile_expr(init, &value);
2338                            } else {
2339                                self.bytecode.emit_store_undefined(value.variable());
2340                            }
2341
2342                            // TODO: SAME
2343                            self.compile_declaration_pattern(
2344                                pattern,
2345                                BindingOpcode::InitLexical,
2346                                &value,
2347                            );
2348                            self.register_allocator.dealloc(value);
2349                        }
2350                    }
2351                }
2352            }
2353        }
2354    }
2355
2356    /// Compile a [`StatementListItem`].
2357    fn compile_stmt_list_item(&mut self, item: &StatementListItem, use_expr: bool, block: bool) {
2358        match item {
2359            StatementListItem::Statement(stmt) => {
2360                self.compile_stmt(stmt, use_expr, false);
2361            }
2362            StatementListItem::Declaration(decl) => self.compile_decl(decl, block),
2363        }
2364    }
2365
2366    /// Compile a [`Declaration`].
2367    #[allow(unused_variables)]
2368    pub fn compile_decl(&mut self, decl: &Declaration, block: bool) {
2369        match decl {
2370            #[cfg(feature = "annex-b")]
2371            Declaration::FunctionDeclaration(function) if block => {
2372                let name = function.name();
2373                if self.annex_b_function_names.contains(&name.sym()) {
2374                    let name = name.to_js_string(self.interner());
2375                    let binding = self.lexical_scope.get_identifier_reference(name.clone());
2376                    let index = self.get_binding(&binding);
2377
2378                    let value = self.register_allocator.alloc();
2379                    self.emit_binding_access(BindingAccessOpcode::GetName, &index, &value);
2380                    match self.variable_scope.set_mutable_binding_var(name.clone()) {
2381                        Ok(binding) => {
2382                            let index = self.get_binding(&binding);
2383                            self.emit_binding_access(BindingAccessOpcode::SetName, &index, &value);
2384                        }
2385                        Err(BindingLocatorError::MutateImmutable) => {
2386                            let index = self.get_or_insert_string(name);
2387                            self.bytecode.emit_throw_mutate_immutable(index.into());
2388                        }
2389                        Err(BindingLocatorError::Silent) => {}
2390                    }
2391                    self.register_allocator.dealloc(value);
2392                }
2393            }
2394            Declaration::ClassDeclaration(class) => self.compile_class(class.as_ref().into(), None),
2395            Declaration::Lexical(lexical) => self.compile_lexical_decl(lexical),
2396            _ => {}
2397        }
2398    }
2399
2400    /// Compiles a function AST Node into bytecode, and returns its index into
2401    /// the `functions` array.
2402    pub(crate) fn function(&mut self, function: FunctionSpec<'_>) -> u32 {
2403        let (generator, r#async, arrow) = (
2404            function.kind.is_generator(),
2405            function.kind.is_async(),
2406            function.kind.is_arrow(),
2407        );
2408
2409        let FunctionSpec {
2410            name,
2411            parameters,
2412            body,
2413            scopes,
2414            name_scope,
2415            linear_span,
2416            ..
2417        } = function;
2418
2419        let name = if let Some(name) = name {
2420            Some(name.sym().to_js_string(self.interner()))
2421        } else {
2422            Some(js_string!())
2423        };
2424
2425        let spanned_source_text = SpannedSourceText::new(self.source_text(), linear_span);
2426
2427        let code = FunctionCompiler::new(spanned_source_text)
2428            .name(name)
2429            .generator(generator)
2430            .r#async(r#async)
2431            .strict(self.strict())
2432            .arrow(arrow)
2433            .in_with(self.in_with)
2434            .name_scope(name_scope.cloned())
2435            .source_path(self.source_path.clone())
2436            .compile(
2437                parameters,
2438                body,
2439                self.variable_scope.clone(),
2440                self.lexical_scope.clone(),
2441                scopes,
2442                function.contains_direct_eval,
2443                self.interner,
2444            );
2445
2446        self.push_function_to_constants(code)
2447    }
2448
2449    /// Compiles a function AST Node into bytecode, setting its corresponding binding or
2450    /// pushing it to the stack if necessary.
2451    pub(crate) fn function_with_binding(
2452        &mut self,
2453        function: FunctionSpec<'_>,
2454        node_kind: NodeKind,
2455        dst: &Register,
2456    ) {
2457        let name = function.name;
2458        let index = self.function(function);
2459        self.emit_get_function(dst, index);
2460        match node_kind {
2461            NodeKind::Declaration => {
2462                self.emit_binding(
2463                    BindingOpcode::InitVar,
2464                    name.expect("function declaration must have a name")
2465                        .to_js_string(self.interner()),
2466                    dst,
2467                );
2468            }
2469            NodeKind::Expression => {}
2470        }
2471    }
2472
2473    /// Compile an object method AST Node into bytecode.
2474    pub(crate) fn object_method(
2475        &mut self,
2476        function: FunctionSpec<'_>,
2477        kind: MethodKind,
2478    ) -> Register {
2479        let (generator, r#async, arrow) = (
2480            function.kind.is_generator(),
2481            function.kind.is_async(),
2482            function.kind.is_arrow(),
2483        );
2484        let FunctionSpec {
2485            name,
2486            parameters,
2487            body,
2488            scopes,
2489            name_scope,
2490            linear_span,
2491            ..
2492        } = function;
2493
2494        let name = if let Some(name) = name {
2495            let name = name.sym().to_js_string(self.interner());
2496            match kind {
2497                MethodKind::Ordinary => Some(name),
2498                MethodKind::Get => Some(js_string!(js_str!("get "), &name)),
2499                MethodKind::Set => Some(js_string!(js_str!("set "), &name)),
2500            }
2501        } else {
2502            Some(js_string!())
2503        };
2504
2505        let spanned_source_text = SpannedSourceText::new(self.source_text(), linear_span);
2506
2507        let code = FunctionCompiler::new(spanned_source_text)
2508            .name(name)
2509            .generator(generator)
2510            .r#async(r#async)
2511            .strict(self.strict())
2512            .arrow(arrow)
2513            .method(true)
2514            .in_with(self.in_with)
2515            .name_scope(name_scope.cloned())
2516            .source_path(self.source_path.clone())
2517            .compile(
2518                parameters,
2519                body,
2520                self.variable_scope.clone(),
2521                self.lexical_scope.clone(),
2522                scopes,
2523                function.contains_direct_eval,
2524                self.interner,
2525            );
2526
2527        let index = self.push_function_to_constants(code);
2528        let dst = self.register_allocator.alloc();
2529        self.emit_get_function(&dst, index);
2530        dst
2531    }
2532
2533    /// Compile a class method AST Node into bytecode.
2534    fn method(&mut self, function: FunctionSpec<'_>) -> Register {
2535        let (generator, r#async, arrow) = (
2536            function.kind.is_generator(),
2537            function.kind.is_async(),
2538            function.kind.is_arrow(),
2539        );
2540        let FunctionSpec {
2541            name,
2542            parameters,
2543            body,
2544            scopes,
2545            linear_span,
2546            ..
2547        } = function;
2548
2549        let name = if let Some(name) = name {
2550            Some(name.sym().to_js_string(self.interner()))
2551        } else {
2552            Some(js_string!())
2553        };
2554
2555        let spanned_source_text = SpannedSourceText::new(self.source_text(), linear_span);
2556
2557        let code = FunctionCompiler::new(spanned_source_text)
2558            .name(name)
2559            .generator(generator)
2560            .r#async(r#async)
2561            .strict(true)
2562            .arrow(arrow)
2563            .method(true)
2564            .in_with(self.in_with)
2565            .name_scope(function.name_scope.cloned())
2566            .source_path(self.source_path.clone())
2567            .compile(
2568                parameters,
2569                body,
2570                self.variable_scope.clone(),
2571                self.lexical_scope.clone(),
2572                scopes,
2573                function.contains_direct_eval,
2574                self.interner,
2575            );
2576
2577        let index = self.push_function_to_constants(code);
2578        let dst = self.register_allocator.alloc();
2579        self.emit_get_function(&dst, index);
2580        dst
2581    }
2582
2583    fn call(&mut self, callable: Callable<'_>, dst: CallResultDest<'_>) {
2584        #[derive(PartialEq)]
2585        enum CallKind {
2586            CallEval,
2587            Call,
2588            New,
2589        }
2590
2591        let (call, mut kind) = match callable {
2592            Callable::Call(call) => (call, CallKind::Call),
2593            Callable::New(new) => (new.call(), CallKind::New),
2594        };
2595
2596        match call.function().flatten() {
2597            Expression::PropertyAccess(access) if kind == CallKind::Call => {
2598                let this = self.register_allocator.alloc();
2599                let dst = self.register_allocator.alloc();
2600                self.compile_access_preserve_this(access, &this, &dst);
2601                self.push_from_register(&this);
2602                self.push_from_register(&dst);
2603                self.register_allocator.dealloc(this);
2604                self.register_allocator.dealloc(dst);
2605            }
2606            Expression::Optional(opt) if kind == CallKind::Call => {
2607                let this = self.register_allocator.alloc();
2608                let dst = self.register_allocator.alloc();
2609                self.compile_optional_preserve_this(opt, &this, &dst);
2610                self.push_from_register(&this);
2611                self.push_from_register(&dst);
2612                self.register_allocator.dealloc(this);
2613                self.register_allocator.dealloc(dst);
2614            }
2615            expr if kind == CallKind::Call => {
2616                if let Expression::Identifier(ident) = expr {
2617                    if ident.sym() == Sym::EVAL {
2618                        kind = CallKind::CallEval;
2619                    }
2620
2621                    if self.in_with {
2622                        let name = self.resolve_identifier_expect(*ident);
2623                        let binding = self.lexical_scope.get_identifier_reference(name);
2624                        let index = self.get_binding(&binding);
2625                        let index = match index {
2626                            BindingKind::Global(index) | BindingKind::Stack(index) => index,
2627                            BindingKind::Local(_) => {
2628                                unreachable!("with binding cannot be local")
2629                            }
2630                        };
2631                        let value = self.register_allocator.alloc();
2632                        self.bytecode
2633                            .emit_this_for_object_environment_name(value.variable(), index.into());
2634                        self.push_from_register(&value);
2635                        self.register_allocator.dealloc(value);
2636                    } else {
2637                        self.push_from_register(&CallFrame::undefined_register());
2638                    }
2639                } else {
2640                    self.push_from_register(&CallFrame::undefined_register());
2641                }
2642
2643                self.compile_expr_to_stack(expr);
2644            }
2645            expr => {
2646                let value = self.register_allocator.alloc();
2647                self.compile_expr(expr, &value);
2648                self.push_from_register(&CallFrame::undefined_register());
2649                self.push_from_register(&value);
2650                self.register_allocator.dealloc(value);
2651            }
2652        }
2653
2654        let mut compiler = self.position_guard(call);
2655
2656        let contains_spread = call
2657            .args()
2658            .iter()
2659            .any(|arg| matches!(arg, Expression::Spread(_)));
2660
2661        if contains_spread {
2662            let array = compiler.register_allocator.alloc();
2663            let array_op = array.variable();
2664
2665            compiler.bytecode.emit_store_new_array(array_op);
2666
2667            for arg in call.args() {
2668                if let Expression::Spread(spread) = arg {
2669                    let value = compiler.register_allocator.alloc();
2670                    compiler.compile_expr(spread.target(), &value);
2671                    compiler.bytecode.emit_get_iterator(value.variable());
2672                    compiler.bytecode.emit_push_iterator_to_array(array_op);
2673                    compiler.register_allocator.dealloc(value);
2674                } else {
2675                    compiler.compile_expr_operand(arg, |this, op| {
2676                        this.bytecode.emit_push_value_to_array(op, array_op);
2677                    });
2678                }
2679            }
2680
2681            compiler.push_from_register(&array);
2682            compiler.register_allocator.dealloc(array);
2683        } else {
2684            for arg in call.args() {
2685                compiler.compile_expr_to_stack(arg);
2686            }
2687        }
2688
2689        match kind {
2690            CallKind::CallEval => {
2691                let scope_index = compiler.constants.len() as u32;
2692                let lexical_scope = compiler.lexical_scope.clone();
2693                compiler.constants.push(Constant::Scope(lexical_scope));
2694                if contains_spread {
2695                    compiler.bytecode.emit_call_eval_spread(scope_index.into());
2696                } else {
2697                    compiler
2698                        .bytecode
2699                        .emit_call_eval((call.args().len() as u32).into(), scope_index.into());
2700                }
2701            }
2702            CallKind::Call if contains_spread => compiler.bytecode.emit_call_spread(),
2703            CallKind::Call => {
2704                compiler
2705                    .bytecode
2706                    .emit_call((call.args().len() as u32).into());
2707            }
2708            CallKind::New if contains_spread => compiler.bytecode.emit_new_spread(),
2709            CallKind::New => compiler
2710                .bytecode
2711                .emit_new((call.args().len() as u32).into()),
2712        }
2713        match dst {
2714            CallResultDest::Register(dst) => compiler.pop_into_register(dst),
2715            CallResultDest::Discard => compiler.bytecode.emit_pop(),
2716            CallResultDest::Stack => {} // leave result on stack
2717        }
2718    }
2719
2720    /// Finish compiling code with the [`ByteCompiler`] and return the generated [`CodeBlock`].
2721    #[inline]
2722    #[must_use]
2723    #[allow(clippy::missing_const_for_fn)]
2724    pub fn finish(mut self) -> CodeBlock {
2725        // Push return at the end of the function compilation.
2726        if let Some(async_handler) = self.async_handler {
2727            self.patch_handler(async_handler);
2728        }
2729        self.r#return(false);
2730
2731        let final_bytecode_len = self.next_opcode_location();
2732
2733        let mapped_arguments_binding_indices = if self.emitted_mapped_arguments_object_opcode {
2734            MappedArguments::binding_indices(&self.params, &self.parameter_scope, self.interner)
2735        } else {
2736            ThinVec::default()
2737        };
2738
2739        let register_count = self.register_allocator.finish();
2740
2741        let source_map_entries = self.source_map_builder.build(final_bytecode_len.as_u32());
2742
2743        CodeBlock {
2744            length: self.length,
2745            register_count,
2746            this_mode: self.this_mode,
2747            parameter_length: self.params.as_ref().len() as u32,
2748            mapped_arguments_binding_indices,
2749            bytecode: self.bytecode.into_bytecode(),
2750            constants: self.constants,
2751            bindings: self.bindings.into_boxed_slice(),
2752            handlers: self.handlers,
2753            flags: Cell::new(self.code_block_flags),
2754            ic: self.ic.into_boxed_slice(),
2755            source_info: SourceInfo::new(
2756                SourceMap::new(source_map_entries, self.source_path),
2757                self.function_name,
2758                self.spanned_source_text,
2759            ),
2760            global_lexs: self.global_lexs.into_boxed_slice(),
2761            global_fns: self.global_fns.into_boxed_slice(),
2762            global_vars: self.global_vars.into_boxed_slice(),
2763            debug_id: CodeBlock::get_next_codeblock_id(),
2764            #[cfg(feature = "trace")]
2765            traced: Cell::new(false),
2766        }
2767    }
2768
2769    fn compile_declaration_pattern(
2770        &mut self,
2771        pattern: &Pattern,
2772        def: BindingOpcode,
2773        object: &Register,
2774    ) {
2775        self.compile_declaration_pattern_impl(pattern, def, object);
2776    }
2777}