Skip to main content

boa_engine/bytecompiler/
mod.rs

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