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