Skip to main content

pipa/compiler/
codegen.rs

1use crate::compiler::ast::*;
2use crate::compiler::location::LineNumberTable;
3use crate::compiler::opcode::{Bytecode, Opcode};
4use crate::runtime::context::JSContext;
5use crate::util::FxHashMap;
6use crate::value::JSValue;
7
8#[derive(Debug)]
9struct BreakableFrame {
10    label: Option<String>,
11    break_patches: Vec<usize>,
12    continue_patches: Vec<usize>,
13    continue_target: Option<usize>,
14}
15
16#[derive(Debug, Clone, Copy)]
17enum VarLocation {
18    Local(u16),
19    Upvalue(u16),
20}
21
22#[derive(Debug, Clone, Copy)]
23struct ParentVar {
24    local_idx: u16,
25    kind: VariableKind,
26    initialized: bool,
27    is_inherited: bool,
28    closed: bool,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum OptLevel {
33    O0,
34    O1,
35    O2,
36    O3,
37}
38
39impl Default for OptLevel {
40    fn default() -> Self {
41        Self::O2
42    }
43}
44
45impl OptLevel {
46    pub fn from_flag(flag: &str) -> Option<Self> {
47        match flag {
48            "-O0" => Some(Self::O0),
49            "-O1" => Some(Self::O1),
50            "-O2" => Some(Self::O2),
51            "-O3" => Some(Self::O3),
52            _ => None,
53        }
54    }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58enum P2FoldProfile {
59    Raw,
60    Ternary,
61    Logical,
62    Nullish,
63    Stable,
64    Aggressive,
65}
66
67impl P2FoldProfile {
68    fn from_env_override() -> Option<Self> {
69        let mode = std::env::var("PIPA_P2_PROFILE").ok()?;
70        match mode.as_str() {
71            "raw" => Some(Self::Raw),
72            "ternary" => Some(Self::Ternary),
73            "logical" => Some(Self::Logical),
74            "nullish" => Some(Self::Nullish),
75            "stable" => Some(Self::Stable),
76            "aggressive" => Some(Self::Aggressive),
77            _ => None,
78        }
79    }
80
81    fn allow_ternary_bool(self) -> bool {
82        matches!(
83            self,
84            Self::Ternary | Self::Logical | Self::Nullish | Self::Stable | Self::Aggressive
85        )
86    }
87
88    fn allow_logical_bool_left(self) -> bool {
89        matches!(
90            self,
91            Self::Logical | Self::Nullish | Self::Stable | Self::Aggressive
92        )
93    }
94
95    fn allow_nullish_literal_left(self) -> bool {
96        matches!(self, Self::Nullish | Self::Stable | Self::Aggressive)
97    }
98
99    fn allow_if_bool(self) -> bool {
100        matches!(self, Self::Stable | Self::Aggressive)
101    }
102
103    fn allow_aggressive_truthy(self) -> bool {
104        matches!(self, Self::Aggressive)
105    }
106}
107
108#[derive(Debug, Clone, Copy)]
109struct CodegenSettings {
110    p2_fold_profile: P2FoldProfile,
111    opt_jump_if_nullish: bool,
112    opt_call23: bool,
113    opt_fused_cmp_jump: bool,
114    opt_fused_getprop_call: bool,
115    opt_tiny_inline: bool,
116    opt_branch_result_prealloc: bool,
117}
118
119impl CodegenSettings {
120    fn for_opt_level(opt_level: OptLevel) -> Self {
121        match opt_level {
122            OptLevel::O0 => Self {
123                p2_fold_profile: P2FoldProfile::Raw,
124                opt_jump_if_nullish: false,
125                opt_call23: false,
126                opt_fused_cmp_jump: false,
127                opt_fused_getprop_call: false,
128                opt_tiny_inline: false,
129                opt_branch_result_prealloc: false,
130            },
131            OptLevel::O1 => Self {
132                p2_fold_profile: P2FoldProfile::Ternary,
133                opt_jump_if_nullish: true,
134                opt_call23: true,
135                opt_fused_cmp_jump: false,
136                opt_fused_getprop_call: true,
137                opt_tiny_inline: false,
138                opt_branch_result_prealloc: true,
139            },
140            OptLevel::O2 => Self {
141                p2_fold_profile: P2FoldProfile::Stable,
142                opt_jump_if_nullish: true,
143                opt_call23: true,
144                opt_fused_cmp_jump: true,
145                opt_fused_getprop_call: true,
146                opt_tiny_inline: true,
147                opt_branch_result_prealloc: true,
148            },
149            OptLevel::O3 => Self {
150                p2_fold_profile: P2FoldProfile::Aggressive,
151                opt_jump_if_nullish: true,
152                opt_call23: true,
153                opt_fused_cmp_jump: false,
154                opt_fused_getprop_call: true,
155                opt_tiny_inline: true,
156                opt_branch_result_prealloc: true,
157            },
158        }
159    }
160}
161
162#[derive(Debug, Clone, Copy)]
163pub(crate) struct VarEntry {
164    pub(crate) slot: u16,
165    pub(crate) kind: VariableKind,
166    pub(crate) initialized: bool,
167}
168
169pub struct CodeGenerator {
170    code: Vec<u8>,
171    constants: Vec<JSValue>,
172    next_register: u16,
173    max_register: u16,
174    free_list: Vec<u16>,
175    scope_stack: Vec<FxHashMap<String, VarEntry>>,
176    reserved_slots: std::collections::HashSet<u16>,
177    breakable_frames: Vec<BreakableFrame>,
178
179    parent_vars: FxHashMap<String, ParentVar>,
180    upvalues: Vec<(u32, u16)>,
181    upvalue_slots: FxHashMap<String, u16>,
182    line_table: LineNumberTable,
183    depth: u32,
184    is_script_level: bool,
185    scope_is_global: Vec<bool>,
186    function_uses_arguments: bool,
187    current_function_name: Option<String>,
188    tiny_inline_functions: FxHashMap<String, FunctionDeclaration>,
189    p2_fold_profile: P2FoldProfile,
190    opt_jump_if_nullish: bool,
191    opt_call23: bool,
192    opt_fused_cmp_jump: bool,
193    opt_fused_getprop_call: bool,
194    opt_tiny_inline: bool,
195    opt_branch_result_prealloc: bool,
196    suppress_ref_error: bool,
197    pub(crate) is_strict: bool,
198    pub(crate) is_eval: bool,
199    is_arrow: bool,
200    in_static_method: bool,
201    pending_label: Option<String>,
202    all_var_names: Vec<(String, u16)>,
203}
204
205impl CodeGenerator {
206    pub fn new() -> Self {
207        Self::with_opt_level(OptLevel::default())
208    }
209
210    pub fn with_opt_level(opt_level: OptLevel) -> Self {
211        let mut settings = CodegenSettings::for_opt_level(opt_level);
212        if let Some(profile) = P2FoldProfile::from_env_override() {
213            settings.p2_fold_profile = profile;
214        }
215        settings.opt_jump_if_nullish =
216            Self::env_flag("PIPA_OPT_JUMP_NULLISH", settings.opt_jump_if_nullish);
217        settings.opt_call23 = Self::env_flag("PIPA_OPT_CALL23", settings.opt_call23);
218        settings.opt_fused_cmp_jump =
219            Self::env_flag("PIPA_OPT_FUSED_CMP_JUMP", settings.opt_fused_cmp_jump);
220        settings.opt_fused_getprop_call = Self::env_flag(
221            "PIPA_OPT_FUSED_GETPROP_CALL",
222            settings.opt_fused_getprop_call,
223        );
224        settings.opt_tiny_inline = Self::env_flag("PIPA_OPT_TINY_INLINE", settings.opt_tiny_inline);
225        settings.opt_branch_result_prealloc = Self::env_flag(
226            "PIPA_OPT_BRANCH_RESULT_PREALLOC",
227            settings.opt_branch_result_prealloc,
228        );
229        Self::with_settings(settings)
230    }
231
232    fn with_settings(settings: CodegenSettings) -> Self {
233        Self {
234            code: Vec::new(),
235            constants: Vec::new(),
236            next_register: 0,
237            max_register: 0,
238            free_list: Vec::new(),
239            scope_stack: vec![FxHashMap::default()],
240            reserved_slots: std::collections::HashSet::new(),
241            breakable_frames: Vec::new(),
242            parent_vars: FxHashMap::default(),
243            upvalues: Vec::new(),
244            upvalue_slots: FxHashMap::default(),
245            line_table: LineNumberTable::new(),
246            depth: 0,
247            is_script_level: true,
248            scope_is_global: vec![false],
249            function_uses_arguments: false,
250            current_function_name: None,
251            tiny_inline_functions: FxHashMap::default(),
252            p2_fold_profile: settings.p2_fold_profile,
253            opt_jump_if_nullish: settings.opt_jump_if_nullish,
254            opt_call23: settings.opt_call23,
255            opt_fused_cmp_jump: settings.opt_fused_cmp_jump,
256            opt_fused_getprop_call: settings.opt_fused_getprop_call,
257            opt_tiny_inline: settings.opt_tiny_inline,
258            opt_branch_result_prealloc: settings.opt_branch_result_prealloc,
259            suppress_ref_error: false,
260            is_strict: false,
261            is_eval: false,
262            is_arrow: false,
263            in_static_method: false,
264            pending_label: None,
265            all_var_names: Vec::new(),
266        }
267    }
268
269    fn settings(&self) -> CodegenSettings {
270        CodegenSettings {
271            p2_fold_profile: self.p2_fold_profile,
272            opt_jump_if_nullish: self.opt_jump_if_nullish,
273            opt_call23: self.opt_call23,
274            opt_fused_cmp_jump: self.opt_fused_cmp_jump,
275            opt_fused_getprop_call: self.opt_fused_getprop_call,
276            opt_tiny_inline: self.opt_tiny_inline,
277            opt_branch_result_prealloc: self.opt_branch_result_prealloc,
278        }
279    }
280
281    fn nested_codegen(&self) -> Self {
282        let mut nested = Self::with_settings(self.settings());
283        nested.tiny_inline_functions = self.tiny_inline_functions.clone();
284        nested.is_strict = self.is_strict;
285        nested.is_eval = self.is_eval;
286        nested.in_static_method = self.in_static_method;
287        nested
288    }
289
290    fn env_flag(name: &str, default: bool) -> bool {
291        match std::env::var(name) {
292            Ok(v) => match v.to_ascii_lowercase().as_str() {
293                "1" | "true" | "yes" | "on" => true,
294                "0" | "false" | "no" | "off" => false,
295                _ => default,
296            },
297            Err(_) => default,
298        }
299    }
300
301    fn literal_truthiness(lit: &Literal) -> Option<bool> {
302        match lit {
303            Literal::Boolean(b) => Some(*b),
304            Literal::Null | Literal::Undefined => Some(false),
305            Literal::Number(n) => Some(*n != 0.0 && !n.is_nan()),
306            Literal::LegacyOctal(n) => Some(*n != 0),
307            Literal::String(s, _) => Some(!s.is_empty()),
308            Literal::BigInt(_) => None,
309        }
310    }
311
312    fn detect_tiny_inline_function(func: &FunctionDeclaration) -> bool {
313        if func.generator || func.is_async {
314            return false;
315        }
316        if func.params.len() != 1 {
317            return false;
318        }
319        let param_name = match &func.params[0] {
320            Parameter::Identifier(name) => name,
321            _ => return false,
322        };
323        if func.body.body.len() != 1 {
324            return false;
325        }
326        let ret = match &func.body.body[0] {
327            ASTNode::ReturnStatement(stmt) => stmt,
328            _ => return false,
329        };
330        let expr = match &ret.argument {
331            Some(expr) => expr,
332            None => return false,
333        };
334        match expr {
335            Expression::MemberExpression(member) => match member.object.as_ref() {
336                Expression::Identifier(id) => {
337                    id.name == *param_name
338                        && matches!(member.property, MemberProperty::Identifier(_))
339                }
340                Expression::MemberExpression(inner) => {
341                    if inner.computed || member.computed {
342                        return false;
343                    }
344                    match inner.object.as_ref() {
345                        Expression::Identifier(id) => {
346                            id.name == *param_name
347                                && matches!(inner.property, MemberProperty::Identifier(_))
348                                && matches!(member.property, MemberProperty::Identifier(_))
349                        }
350                        _ => false,
351                    }
352                }
353                _ => false,
354            },
355            _ => false,
356        }
357    }
358
359    fn try_emit_tiny_inline_call(
360        &mut self,
361        call: &CallExpression,
362        ctx: &mut JSContext,
363    ) -> Result<Option<u16>, String> {
364        let callee_name = match call.callee.as_ref() {
365            Expression::Identifier(id) => &id.name,
366            _ => return Ok(None),
367        };
368        let func = match self.tiny_inline_functions.get(callee_name).cloned() {
369            Some(func) => func,
370            None => return Ok(None),
371        };
372        if call.arguments.len() != 1 {
373            return Ok(None);
374        }
375        let arg_reg = match &call.arguments[0] {
376            Argument::Expression(expr) => self.gen_expression(expr, ctx)?,
377            Argument::Spread(_) => return Ok(None),
378        };
379
380        let ret = match &func.body.body[0] {
381            ASTNode::ReturnStatement(stmt) => stmt,
382            _ => return Ok(None),
383        };
384        let expr = match &ret.argument {
385            Some(expr) => expr,
386            None => return Ok(None),
387        };
388
389        let dst = self.alloc_register();
390        match expr {
391            Expression::MemberExpression(member) => match member.object.as_ref() {
392                Expression::Identifier(_) => {
393                    if let MemberProperty::Identifier(name) = &member.property {
394                        let atom = ctx.intern(name);
395                        self.emit(Opcode::GetNamedProp);
396                        self.emit_u16(dst);
397                        self.emit_u16(arg_reg);
398                        self.emit_u32(atom.0);
399                    } else {
400                        self.free_register(arg_reg);
401                        self.free_register(dst);
402                        return Ok(None);
403                    }
404                }
405                Expression::MemberExpression(inner) => {
406                    let mid = self.alloc_register();
407                    let atom1 = match &inner.property {
408                        MemberProperty::Identifier(name) => ctx.intern(name),
409                        _ => {
410                            self.free_register(arg_reg);
411                            self.free_register(mid);
412                            self.free_register(dst);
413                            return Ok(None);
414                        }
415                    };
416                    let atom2 = match &member.property {
417                        MemberProperty::Identifier(name) => ctx.intern(name),
418                        _ => {
419                            self.free_register(arg_reg);
420                            self.free_register(mid);
421                            self.free_register(dst);
422                            return Ok(None);
423                        }
424                    };
425                    self.emit(Opcode::GetNamedProp);
426                    self.emit_u16(mid);
427                    self.emit_u16(arg_reg);
428                    self.emit_u32(atom1.0);
429                    self.emit(Opcode::GetNamedProp);
430                    self.emit_u16(dst);
431                    self.emit_u16(mid);
432                    self.emit_u32(atom2.0);
433                    self.free_register(mid);
434                }
435                _ => {
436                    self.free_register(arg_reg);
437                    self.free_register(dst);
438                    return Ok(None);
439                }
440            },
441            _ => {
442                self.free_register(arg_reg);
443                self.free_register(dst);
444                return Ok(None);
445            }
446        }
447
448        self.free_register(arg_reg);
449        Ok(Some(dst))
450    }
451
452    fn fused_op_from_binary_cmp(bin_op: &BinaryOp, jump_if_true: bool) -> Option<Opcode> {
453        use BinaryOp::*;
454        let op = match (bin_op, jump_if_true) {
455            (Lt, true) => Opcode::LtJumpIf,
456            (Lt, false) => Opcode::LtJumpIfNot,
457            (Lte, true) => Opcode::LteJumpIf,
458            (Lte, false) => Opcode::LteJumpIfNot,
459            (Gt, true) => Opcode::GtJumpIf,
460            (Gt, false) => Opcode::GtJumpIfNot,
461            (Gte, true) => Opcode::GteJumpIf,
462            (Gte, false) => Opcode::GteJumpIfNot,
463            (Eq, true) => Opcode::EqJumpIf,
464            (Eq, false) => Opcode::EqJumpIfNot,
465            (Neq, true) => Opcode::NeqJumpIf,
466            (Neq, false) => Opcode::NeqJumpIfNot,
467            (StrictEq, true) => Opcode::StrictEqJumpIf,
468            (StrictEq, false) => Opcode::StrictEqJumpIfNot,
469            (StrictNeq, true) => Opcode::StrictNeqJumpIf,
470            (StrictNeq, false) => Opcode::StrictNeqJumpIfNot,
471            _ => return None,
472        };
473        Some(op)
474    }
475
476    fn emit(&mut self, op: Opcode) {
477        self.code.push(op as u8);
478    }
479
480    fn emit_u8(&mut self, v: u8) {
481        self.code.push(v);
482    }
483
484    fn emit_u16(&mut self, v: u16) {
485        self.code.extend_from_slice(&v.to_le_bytes());
486    }
487
488    fn emit_i32(&mut self, v: i32) {
489        self.code.extend_from_slice(&v.to_le_bytes());
490    }
491
492    fn emit_u32(&mut self, v: u32) {
493        self.code.extend_from_slice(&v.to_le_bytes());
494    }
495
496    fn emit_int_const(&mut self, dst: u16, val: i32) {
497        if (i8::MIN as i32..=i8::MAX as i32).contains(&val) {
498            self.emit(Opcode::LoadInt8);
499            self.emit_u16(dst);
500            self.emit_u8(val as i8 as u8);
501        } else {
502            self.emit(Opcode::LoadInt);
503            self.emit_u16(dst);
504            self.emit_i32(val);
505        }
506    }
507
508    fn emit_bool_const(&mut self, dst: u16, b: bool) {
509        if b {
510            self.emit(Opcode::LoadTrue);
511        } else {
512            self.emit(Opcode::LoadFalse);
513        }
514        self.emit_u16(dst);
515    }
516
517    fn emit_backward_jump(&mut self, target: usize) {
518        let compact_offset = (target as i64 - (self.code.len() as i64 + 2)) as i32;
519        if compact_offset >= i8::MIN as i32 && compact_offset <= i8::MAX as i32 {
520            self.emit(Opcode::Jump8);
521            self.emit_u8(compact_offset as i8 as u8);
522        } else {
523            let full_offset = (target as i64 - (self.code.len() as i64 + 5)) as i32;
524            self.emit(Opcode::Jump);
525            self.emit_i32(full_offset);
526        }
527    }
528
529    fn emit_conditional_backward_jump(&mut self, src: u16, target: usize, jump_if_true: bool) {
530        let compact_offset = (target as i64 - (self.code.len() as i64 + 4)) as i32;
531        if compact_offset >= i8::MIN as i32 && compact_offset <= i8::MAX as i32 {
532            if jump_if_true {
533                self.emit(Opcode::JumpIf8);
534            } else {
535                self.emit(Opcode::JumpIfNot8);
536            }
537            self.emit_u16(src);
538            self.emit_u8(compact_offset as i8 as u8);
539        } else {
540            let full_offset = (target as i64 - (self.code.len() as i64 + 7)) as i32;
541            if jump_if_true {
542                self.emit(Opcode::JumpIf);
543            } else {
544                self.emit(Opcode::JumpIfNot);
545            }
546            self.emit_u16(src);
547            self.emit_i32(full_offset);
548        }
549    }
550
551    fn patch_jump(&mut self, patch_pos: usize, target_pos: usize) {
552        if patch_pos >= 1 {
553            let op_byte = self.code[patch_pos - 1];
554            let is_uncond = op_byte == Opcode::Jump as u8;
555            let is_cond = op_byte == Opcode::JumpIf as u8 || op_byte == Opcode::JumpIfNot as u8;
556
557            if is_uncond || is_cond {
558                let compact_offset = (target_pos as i64 - patch_pos as i64 - 1) as i32;
559                if compact_offset >= i8::MIN as i32 && compact_offset <= i8::MAX as i32 {
560                    if is_uncond {
561                        self.code[patch_pos - 1] = Opcode::Jump8 as u8;
562                    } else {
563                        self.code[patch_pos - 3] = if op_byte == Opcode::JumpIf as u8 {
564                            Opcode::JumpIf8 as u8
565                        } else {
566                            Opcode::JumpIfNot8 as u8
567                        };
568                    }
569                    self.code[patch_pos] = compact_offset as i8 as u8;
570
571                    let fill_end = patch_pos + 4;
572                    for i in (patch_pos + 1)..fill_end {
573                        self.code[i] = Opcode::Nop as u8;
574                    }
575                    return;
576                }
577            }
578        }
579
580        if patch_pos >= 6 {
581            let fused_op = self.code[patch_pos - 5];
582            let is_fused = fused_op == Opcode::LtJumpIfNot as u8
583                || fused_op == Opcode::LtJumpIf as u8
584                || fused_op == Opcode::LteJumpIfNot as u8
585                || fused_op == Opcode::LteJumpIf as u8
586                || fused_op == Opcode::GtJumpIfNot as u8
587                || fused_op == Opcode::GtJumpIf as u8
588                || fused_op == Opcode::GteJumpIfNot as u8
589                || fused_op == Opcode::GteJumpIf as u8
590                || fused_op == Opcode::EqJumpIfNot as u8
591                || fused_op == Opcode::EqJumpIf as u8
592                || fused_op == Opcode::NeqJumpIfNot as u8
593                || fused_op == Opcode::NeqJumpIf as u8
594                || fused_op == Opcode::StrictEqJumpIfNot as u8
595                || fused_op == Opcode::StrictEqJumpIf as u8
596                || fused_op == Opcode::StrictNeqJumpIfNot as u8
597                || fused_op == Opcode::StrictNeqJumpIf as u8;
598            if is_fused {
599                let offset = (target_pos as i64 - patch_pos as i64 - 4) as i32;
600                self.code[patch_pos..patch_pos + 4].copy_from_slice(&offset.to_le_bytes());
601                return;
602            }
603        }
604
605        let full_offset = (target_pos as i64 - patch_pos as i64 - 4) as i32;
606        self.code[patch_pos..patch_pos + 4].copy_from_slice(&full_offset.to_le_bytes());
607    }
608
609    fn emit_line(&mut self, line: u32) {
610        self.line_table.add_entry(self.code.len() as u32, line);
611    }
612
613    fn add_constant(&mut self, val: JSValue) -> u32 {
614        let idx = self.constants.len() as u32;
615        self.constants.push(val);
616        idx
617    }
618
619    fn intern_and_add_const(&mut self, ctx: &mut JSContext, name: &str) -> u32 {
620        let atom = ctx.atom_table_mut().intern(name);
621        self.add_constant(JSValue::new_string(atom))
622    }
623
624    fn emit_embedded_function(
625        &mut self,
626        bytecode: &Bytecode,
627        upvalues: &[(u32, u16)],
628        func_name_atom: u32,
629        dst: u16,
630        is_generator: bool,
631        is_async: bool,
632    ) {
633        if is_generator && is_async {
634            self.emit(Opcode::NewAsyncGeneratorFunction);
635        } else if is_generator {
636            self.emit(Opcode::NewGeneratorFunction);
637        } else if is_async {
638            self.emit(Opcode::NewAsyncFunction);
639        } else {
640            self.emit(Opcode::NewFunction);
641        }
642        self.emit_u16(bytecode.param_count);
643        self.emit_u8(if bytecode.uses_arguments { 1 } else { 0 });
644        self.emit_u8(if bytecode.is_strict { 1 } else { 0 });
645        let locals_bytes = (bytecode.locals_count as u16).to_le_bytes();
646        self.code.extend_from_slice(&locals_bytes);
647        self.emit_u32(bytecode.code.len() as u32);
648        self.code.extend_from_slice(&bytecode.code);
649        self.emit_u32(bytecode.constants.len() as u32);
650        for c in &bytecode.constants {
651            if c.is_undefined() {
652                self.emit_u8(0);
653            } else if c.is_null() {
654                self.emit_u8(1);
655            } else if c.is_bool() && c.get_bool() {
656                self.emit_u8(2);
657            } else if c.is_bool() && !c.get_bool() {
658                self.emit_u8(3);
659            } else if c.is_int() {
660                self.emit_u8(4);
661                self.code.extend_from_slice(&c.get_int().to_le_bytes());
662            } else if c.is_float() {
663                self.emit_u8(5);
664                let bytes = c.get_float().to_le_bytes();
665                self.code.extend_from_slice(&bytes);
666            } else if c.is_string() {
667                self.emit_u8(6);
668                self.emit_u32(c.get_atom().0);
669            } else {
670                self.emit_u8(0);
671            }
672        }
673        self.emit_u32(upvalues.len() as u32);
674        for (atom_id, local_idx) in upvalues {
675            self.emit_u32(*atom_id);
676            self.emit_u32(*local_idx as u32);
677        }
678
679        if let Some(ref table) = bytecode.line_number_table {
680            let entries = table.entries();
681            self.emit_u32(entries.len() as u32);
682            for (off, line) in entries {
683                self.emit_u32(*off);
684                self.emit_u32(*line);
685            }
686        } else {
687            self.emit_u32(0);
688        }
689        self.emit_u32(func_name_atom);
690        self.emit_u32(bytecode.var_name_to_slot.len() as u32);
691        for &(atom_id, slot) in bytecode.var_name_to_slot.iter() {
692            self.emit_u32(atom_id);
693            self.emit_u16(slot);
694        }
695        self.emit_u16(dst);
696    }
697
698    fn alloc_register(&mut self) -> u16 {
699        while let Some(free_r) = self.free_list.pop() {
700            if !self.reserved_slots.contains(&free_r) {
701                if free_r > self.max_register {
702                    self.max_register = free_r;
703                }
704                return free_r;
705            }
706        }
707
708        let mut attempts = 0u32;
709        while self.reserved_slots.contains(&self.next_register) {
710            self.next_register = self.next_register.wrapping_add(1);
711            attempts += 1;
712            if attempts >= 65536 {
713                panic!("alloc_register: all u16 slots are reserved");
714            }
715        }
716        let r = self.next_register;
717        self.next_register = self.next_register.wrapping_add(1);
718        if r > self.max_register {
719            self.max_register = r;
720        }
721        r
722    }
723
724    fn free_register(&mut self, r: u16) {
725        if !self.reserved_slots.contains(&r) && !self.free_list.contains(&r) {
726            self.free_list.push(r);
727        }
728    }
729
730    fn push_scope(&mut self) {
731        self.scope_stack.push(FxHashMap::default());
732        self.scope_is_global.push(false);
733        self.depth += 1;
734    }
735
736    fn pop_scope(&mut self) {
737        // Mark variables in the popped scope as closed in parent_vars so
738        // that closures created after this scope see them as inaccessible.
739        if let Some(scope) = self.scope_stack.last() {
740            for (name, entry) in scope.iter() {
741                if entry.kind != VariableKind::Var {
742                    if let Some(pv) = self.parent_vars.get_mut(name) {
743                        pv.closed = true;
744                    }
745                }
746            }
747        }
748        self.scope_stack.pop();
749        self.scope_is_global.pop();
750        self.depth = self.depth.saturating_sub(1);
751    }
752
753    fn declare_var(&mut self, name: &str, kind: VariableKind) -> u16 {
754        if kind == VariableKind::Var {
755            if let Some(entry) = self.scope_stack[0].get(name) {
756                return entry.slot;
757            }
758        }
759        let slot = self.alloc_register();
760        let entry = VarEntry {
761            slot,
762            kind,
763            initialized: kind == VariableKind::Var,
764        };
765        self.reserved_slots.insert(slot);
766        self.all_var_names.push((name.to_string(), slot));
767        match kind {
768            VariableKind::Var => {
769                self.scope_stack[0].insert(name.to_string(), entry);
770            }
771            _ => {
772                self.scope_stack[self.depth as usize].insert(name.to_string(), entry);
773                // Add to parent_vars for nested scopes so closures created
774                // after the scope exits can detect them as inaccessible.
775                if self.depth > 1 {
776                    self.parent_vars.insert(
777                        name.to_string(),
778                        ParentVar {
779                            local_idx: slot,
780                            kind,
781                            initialized: false,
782                            is_inherited: false,
783                            closed: false,
784                        },
785                    );
786                }
787            }
788        }
789        slot
790    }
791
792    fn is_strict_reserved_word(name: &str) -> bool {
793        matches!(
794            name,
795            "implements"
796                | "interface"
797                | "let"
798                | "package"
799                | "private"
800                | "protected"
801                | "public"
802                | "static"
803                | "yield"
804                | "eval"
805                | "arguments"
806        )
807    }
808
809    fn has_use_strict_directive(body: &BlockStatement) -> bool {
810        for stmt in &body.body {
811            match stmt {
812                ASTNode::ExpressionStatement(es) => {
813                    if let Expression::Literal(Literal::String(s, has_escape)) = &es.expression {
814                        if !has_escape && s == "use strict" {
815                            return true;
816                        }
817                    } else {
818                        return false;
819                    }
820                }
821                _ => return false,
822            }
823        }
824        false
825    }
826
827    fn is_global_scope(&self) -> bool {
828        self.scope_is_global.first().copied().unwrap_or(false)
829    }
830
831    fn is_global_var(&self, name: &str) -> bool {
832        if !self.is_global_scope() {
833            return false;
834        }
835        self.scope_stack
836            .first()
837            .map_or(false, |s| s.contains_key(name))
838    }
839
840    fn lookup_var(&self, name: &str) -> Option<&VarEntry> {
841        for scope in self.scope_stack.iter().rev() {
842            if let Some(entry) = scope.get(name) {
843                return Some(entry);
844            }
845        }
846        None
847    }
848
849    fn lookup_var_slot(&self, name: &str) -> Option<u16> {
850        self.lookup_var(name).map(|e| e.slot)
851    }
852
853    fn resolve_var(&mut self, name: &str, ctx: &mut JSContext) -> Option<VarLocation> {
854        if let Some(entry) = self.lookup_var(name) {
855            return Some(VarLocation::Local(entry.slot));
856        }
857        if let Some(parent_var) = self.parent_vars.get(name) {
858            // If the variable's scope has been closed (popped), don't resolve
859            // it as an upvalue — closures after the scope should not see it.
860            if parent_var.closed {
861                return None;
862            }
863            if let Some(&upvalue_slot) = self.upvalue_slots.get(name) {
864                return Some(VarLocation::Upvalue(upvalue_slot));
865            }
866            let upvalue_slot = self.upvalues.len() as u16;
867            self.upvalue_slots.insert(name.to_string(), upvalue_slot);
868            self.reserved_slots.insert(upvalue_slot);
869            let atom = ctx.atom_table_mut().intern(name);
870            let local_idx = if parent_var.is_inherited {
871                u16::MAX
872            } else {
873                parent_var.local_idx
874            };
875            self.upvalues.push((atom.0, local_idx));
876            return Some(VarLocation::Upvalue(upvalue_slot));
877        }
878        None
879    }
880
881    fn collect_inherited_upvalues_stmt(
882        stmt: &ASTNode,
883        locals: &mut std::collections::HashSet<String>,
884        parent_vars: &FxHashMap<String, ParentVar>,
885        result: &mut std::collections::HashSet<String>,
886    ) {
887        match stmt {
888            ASTNode::ExpressionStatement(es) => {
889                Self::collect_inherited_upvalues_expr(&es.expression, locals, parent_vars, result);
890            }
891            ASTNode::VariableDeclaration(decl) => {
892                for d in &decl.declarations {
893                    if let BindingPattern::Identifier(name) = &d.id {
894                        if let Some(init) = &d.init {
895                            Self::collect_inherited_upvalues_expr(
896                                init,
897                                locals,
898                                parent_vars,
899                                result,
900                            );
901                        }
902                        locals.insert(name.clone());
903                    }
904                }
905            }
906            ASTNode::BlockStatement(block) => {
907                let mut block_locals = locals.clone();
908                for s in &block.body {
909                    Self::collect_inherited_upvalues_stmt(
910                        s,
911                        &mut block_locals,
912                        parent_vars,
913                        result,
914                    );
915                }
916            }
917            ASTNode::IfStatement(stmt) => {
918                Self::collect_inherited_upvalues_expr(&stmt.test, locals, parent_vars, result);
919                let mut cons_locals = locals.clone();
920                Self::collect_inherited_upvalues_stmt(
921                    &stmt.consequent,
922                    &mut cons_locals,
923                    parent_vars,
924                    result,
925                );
926                if let Some(alt) = &stmt.alternate {
927                    let mut alt_locals = locals.clone();
928                    Self::collect_inherited_upvalues_stmt(
929                        alt,
930                        &mut alt_locals,
931                        parent_vars,
932                        result,
933                    );
934                }
935            }
936            ASTNode::WhileStatement(stmt) => {
937                Self::collect_inherited_upvalues_expr(&stmt.test, locals, parent_vars, result);
938                let mut loop_locals = locals.clone();
939                Self::collect_inherited_upvalues_stmt(
940                    &stmt.body,
941                    &mut loop_locals,
942                    parent_vars,
943                    result,
944                );
945            }
946            ASTNode::DoWhileStatement(stmt) => {
947                let mut loop_locals = locals.clone();
948                Self::collect_inherited_upvalues_stmt(
949                    &stmt.body,
950                    &mut loop_locals,
951                    parent_vars,
952                    result,
953                );
954                Self::collect_inherited_upvalues_expr(&stmt.test, locals, parent_vars, result);
955            }
956            ASTNode::ForStatement(stmt) => {
957                let mut for_locals = locals.clone();
958                if let Some(init) = &stmt.init {
959                    match init {
960                        ForInit::VariableDeclaration(decl) => {
961                            for d in &decl.declarations {
962                                if let BindingPattern::Identifier(name) = &d.id {
963                                    if let Some(init_expr) = &d.init {
964                                        Self::collect_inherited_upvalues_expr(
965                                            init_expr,
966                                            &mut for_locals,
967                                            parent_vars,
968                                            result,
969                                        );
970                                    }
971                                    for_locals.insert(name.clone());
972                                }
973                            }
974                        }
975                        ForInit::Expression(expr) => {
976                            Self::collect_inherited_upvalues_expr(
977                                expr,
978                                &mut for_locals,
979                                parent_vars,
980                                result,
981                            );
982                        }
983                    }
984                }
985                if let Some(test) = &stmt.test {
986                    Self::collect_inherited_upvalues_expr(
987                        test,
988                        &mut for_locals,
989                        parent_vars,
990                        result,
991                    );
992                }
993                Self::collect_inherited_upvalues_stmt(
994                    &stmt.body,
995                    &mut for_locals,
996                    parent_vars,
997                    result,
998                );
999                if let Some(update) = &stmt.update {
1000                    Self::collect_inherited_upvalues_expr(
1001                        update,
1002                        &mut for_locals,
1003                        parent_vars,
1004                        result,
1005                    );
1006                }
1007            }
1008            ASTNode::SwitchStatement(stmt) => {
1009                Self::collect_inherited_upvalues_expr(
1010                    &stmt.discriminant,
1011                    locals,
1012                    parent_vars,
1013                    result,
1014                );
1015                for case in &stmt.cases {
1016                    let mut case_locals = locals.clone();
1017                    if let Some(test) = &case.test {
1018                        Self::collect_inherited_upvalues_expr(
1019                            test,
1020                            &mut case_locals,
1021                            parent_vars,
1022                            result,
1023                        );
1024                    }
1025                    for s in &case.consequent {
1026                        Self::collect_inherited_upvalues_stmt(
1027                            s,
1028                            &mut case_locals,
1029                            parent_vars,
1030                            result,
1031                        );
1032                    }
1033                }
1034            }
1035            ASTNode::ReturnStatement(stmt) => {
1036                if let Some(expr) = &stmt.argument {
1037                    Self::collect_inherited_upvalues_expr(expr, locals, parent_vars, result);
1038                }
1039            }
1040            ASTNode::BreakStatement(_) | ASTNode::ContinueStatement(_) => {}
1041            ASTNode::FunctionDeclaration(func) => {
1042                let mut func_locals = std::collections::HashSet::new();
1043                func_locals.insert(func.name.clone());
1044                for param in &func.params {
1045                    Self::collect_parameter_bindings(param, &mut func_locals);
1046                }
1047                for s in &func.body.body {
1048                    Self::collect_inherited_upvalues_stmt(s, &mut func_locals, parent_vars, result);
1049                }
1050            }
1051            _ => {}
1052        }
1053    }
1054
1055    fn collect_binding_pattern_bindings(
1056        pattern: &BindingPattern,
1057        out: &mut std::collections::HashSet<String>,
1058    ) {
1059        match pattern {
1060            BindingPattern::Identifier(name) => {
1061                out.insert(name.clone());
1062            }
1063            BindingPattern::ArrayPattern(arr) => {
1064                for elem in &arr.elements {
1065                    if let Some(elem) = elem {
1066                        match elem {
1067                            PatternElement::Pattern(target) => {
1068                                Self::collect_assignment_target_bindings(target, out);
1069                            }
1070                            PatternElement::RestElement(rest) => {
1071                                Self::collect_assignment_target_bindings(&rest.argument, out);
1072                            }
1073                            PatternElement::AssignmentPattern(ap) => {
1074                                Self::collect_assignment_target_bindings(&ap.left, out);
1075                            }
1076                        }
1077                    }
1078                }
1079            }
1080            BindingPattern::ObjectPattern(obj) => {
1081                for prop in &obj.properties {
1082                    match prop {
1083                        ObjectPatternProperty::Property { value, .. } => {
1084                            Self::collect_assignment_target_bindings(value, out);
1085                        }
1086                        ObjectPatternProperty::RestElement(rest) => {
1087                            Self::collect_assignment_target_bindings(&rest.argument, out);
1088                        }
1089                    }
1090                }
1091            }
1092            BindingPattern::AssignmentPattern(ap) => {
1093                Self::collect_assignment_target_bindings(&ap.left, out);
1094            }
1095        }
1096    }
1097
1098    fn collect_assignment_target_bindings(
1099        target: &AssignmentTarget,
1100        out: &mut std::collections::HashSet<String>,
1101    ) {
1102        match target {
1103            AssignmentTarget::Identifier(name) => {
1104                out.insert(name.clone());
1105            }
1106            AssignmentTarget::ArrayPattern(arr) => {
1107                Self::collect_binding_pattern_bindings(
1108                    &BindingPattern::ArrayPattern(arr.clone()),
1109                    out,
1110                );
1111            }
1112            AssignmentTarget::ObjectPattern(obj) => {
1113                Self::collect_binding_pattern_bindings(
1114                    &BindingPattern::ObjectPattern(obj.clone()),
1115                    out,
1116                );
1117            }
1118            AssignmentTarget::AssignmentPattern(ap) => {
1119                Self::collect_assignment_target_bindings(&ap.left, out);
1120            }
1121            AssignmentTarget::RestElement(rest) => {
1122                Self::collect_assignment_target_bindings(&rest.argument, out);
1123            }
1124            _ => {}
1125        }
1126    }
1127
1128    fn collect_parameter_bindings(param: &Parameter, out: &mut std::collections::HashSet<String>) {
1129        match param {
1130            Parameter::Identifier(name) => {
1131                out.insert(name.clone());
1132            }
1133            Parameter::Pattern(pattern) => {
1134                Self::collect_binding_pattern_bindings(pattern, out);
1135            }
1136            Parameter::AssignmentPattern(ap) => {
1137                Self::collect_assignment_target_bindings(&ap.left, out);
1138            }
1139            Parameter::RestElement(rest) => {
1140                Self::collect_assignment_target_bindings(&rest.argument, out);
1141            }
1142        }
1143    }
1144
1145    fn collect_inherited_upvalues_expr(
1146        expr: &Expression,
1147        locals: &mut std::collections::HashSet<String>,
1148        parent_vars: &FxHashMap<String, ParentVar>,
1149        result: &mut std::collections::HashSet<String>,
1150    ) {
1151        match expr {
1152            Expression::Identifier(id) => {
1153                if !locals.contains(&id.name) && parent_vars.contains_key(&id.name) {
1154                    result.insert(id.name.clone());
1155                }
1156            }
1157            Expression::BinaryExpression(bin) => {
1158                Self::collect_inherited_upvalues_expr(&bin.left, locals, parent_vars, result);
1159                Self::collect_inherited_upvalues_expr(&bin.right, locals, parent_vars, result);
1160            }
1161            Expression::UnaryExpression(unary) => {
1162                Self::collect_inherited_upvalues_expr(&unary.argument, locals, parent_vars, result);
1163            }
1164            Expression::UpdateExpression(update) => {
1165                if let Expression::Identifier(id) = &*update.argument {
1166                    if !locals.contains(&id.name) && parent_vars.contains_key(&id.name) {
1167                        result.insert(id.name.clone());
1168                    }
1169                }
1170            }
1171            Expression::AssignmentExpression(assign) => {
1172                match &assign.left {
1173                    AssignmentTarget::Identifier(name) => {
1174                        if !locals.contains(name) && parent_vars.contains_key(name) {
1175                            result.insert(name.clone());
1176                        }
1177                    }
1178                    AssignmentTarget::MemberExpression(member) => {
1179                        Self::collect_inherited_upvalues_expr(
1180                            &member.object,
1181                            locals,
1182                            parent_vars,
1183                            result,
1184                        );
1185                        if let MemberProperty::Computed(e) = &member.property {
1186                            Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1187                        }
1188                    }
1189                    AssignmentTarget::ComputedMember(obj, key) => {
1190                        Self::collect_inherited_upvalues_expr(obj, locals, parent_vars, result);
1191                        Self::collect_inherited_upvalues_expr(key, locals, parent_vars, result);
1192                    }
1193                    _ => {}
1194                }
1195                Self::collect_inherited_upvalues_expr(&assign.right, locals, parent_vars, result);
1196            }
1197            Expression::ConditionalExpression(cond) => {
1198                Self::collect_inherited_upvalues_expr(&cond.test, locals, parent_vars, result);
1199                Self::collect_inherited_upvalues_expr(
1200                    &cond.consequent,
1201                    locals,
1202                    parent_vars,
1203                    result,
1204                );
1205                Self::collect_inherited_upvalues_expr(&cond.alternate, locals, parent_vars, result);
1206            }
1207            Expression::LogicalExpression(logical) => {
1208                Self::collect_inherited_upvalues_expr(&logical.left, locals, parent_vars, result);
1209                Self::collect_inherited_upvalues_expr(&logical.right, locals, parent_vars, result);
1210            }
1211            Expression::MemberExpression(member) => {
1212                Self::collect_inherited_upvalues_expr(&member.object, locals, parent_vars, result);
1213                if let MemberProperty::Computed(e) = &member.property {
1214                    Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1215                }
1216            }
1217            Expression::CallExpression(call) => {
1218                Self::collect_inherited_upvalues_expr(&call.callee, locals, parent_vars, result);
1219                for arg in &call.arguments {
1220                    if let Argument::Expression(e) = arg {
1221                        Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1222                    }
1223                }
1224            }
1225            Expression::NewExpression(new) => {
1226                Self::collect_inherited_upvalues_expr(&new.callee, locals, parent_vars, result);
1227                for arg in &new.arguments {
1228                    if let Argument::Expression(e) = arg {
1229                        Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1230                    }
1231                }
1232            }
1233            Expression::ArrayExpression(arr) => {
1234                for elem in &arr.elements {
1235                    if let Some(ArrayElement::Expression(e)) = elem {
1236                        Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1237                    }
1238                }
1239            }
1240            Expression::ObjectExpression(obj) => {
1241                for prop in &obj.properties {
1242                    if let Property::Property { value, .. } = prop {
1243                        Self::collect_inherited_upvalues_expr(value, locals, parent_vars, result);
1244                    }
1245                }
1246            }
1247            Expression::FunctionExpression(func) => {
1248                let mut func_locals = std::collections::HashSet::new();
1249                if let Some(ref name) = func.name {
1250                    func_locals.insert(name.clone());
1251                }
1252                for param in &func.params {
1253                    Self::collect_parameter_bindings(param, &mut func_locals);
1254                }
1255                for s in &func.body.body {
1256                    Self::collect_inherited_upvalues_stmt(s, &mut func_locals, parent_vars, result);
1257                }
1258            }
1259            Expression::ArrowFunction(arrow) => {
1260                let mut func_locals = std::collections::HashSet::new();
1261                for param in &arrow.params {
1262                    Self::collect_parameter_bindings(param, &mut func_locals);
1263                }
1264                let body = match &arrow.body {
1265                    ArrowBody::Expression(expr) => BlockStatement {
1266                        body: vec![ASTNode::ReturnStatement(ReturnStatement {
1267                            argument: Some((**expr).clone()),
1268                        })],
1269                        lines: vec![],
1270                    },
1271                    ArrowBody::Block(block) => block.clone(),
1272                };
1273                for s in &body.body {
1274                    Self::collect_inherited_upvalues_stmt(s, &mut func_locals, parent_vars, result);
1275                }
1276            }
1277            Expression::SequenceExpression(seq) => {
1278                for e in &seq.expressions {
1279                    Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1280                }
1281            }
1282            Expression::TemplateLiteral(tpl) => {
1283                for e in &tpl.expressions {
1284                    Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1285                }
1286            }
1287            Expression::TaggedTemplateExpression(tagged) => {
1288                Self::collect_inherited_upvalues_expr(&tagged.tag, locals, parent_vars, result);
1289                for e in &tagged.quasi.expressions {
1290                    Self::collect_inherited_upvalues_expr(e, locals, parent_vars, result);
1291                }
1292            }
1293            Expression::SpreadElement(spread) => {
1294                Self::collect_inherited_upvalues_expr(
1295                    &spread.argument,
1296                    locals,
1297                    parent_vars,
1298                    result,
1299                );
1300            }
1301            _ => {}
1302        }
1303    }
1304
1305    pub fn compile_expression(
1306        &mut self,
1307        expr: &Expression,
1308        ctx: &mut JSContext,
1309    ) -> Result<u16, String> {
1310        self.gen_expression(expr, ctx)
1311    }
1312
1313    pub fn compile_function(
1314        &mut self,
1315        params: &[Parameter],
1316        body: &BlockStatement,
1317        ctx: &mut JSContext,
1318    ) -> Result<(Bytecode, Vec<(u32, u16)>), String> {
1319        self.compile_function_with_parent(params, body, ctx, FxHashMap::default(), None, false)
1320    }
1321
1322    pub fn compile_script(
1323        &mut self,
1324        body: &BlockStatement,
1325        ctx: &mut JSContext,
1326    ) -> Result<(Bytecode, Vec<(u32, u16)>), String> {
1327        let saved_scope_is_global = self.scope_is_global.clone();
1328        self.scope_is_global = vec![true];
1329        let result =
1330            self.compile_function_with_parent(&[], body, ctx, FxHashMap::default(), None, true);
1331        self.scope_is_global = saved_scope_is_global;
1332        result
1333    }
1334
1335    fn compile_function_with_parent(
1336        &mut self,
1337        params: &[Parameter],
1338        body: &BlockStatement,
1339        ctx: &mut JSContext,
1340        parent_vars: FxHashMap<String, ParentVar>,
1341        function_name: Option<&str>,
1342        return_last_expr: bool,
1343    ) -> Result<(Bytecode, Vec<(u32, u16)>), String> {
1344        self.is_strict = self.is_strict || Self::has_use_strict_directive(body);
1345        let saved_is_script_level = self.is_script_level;
1346        let saved_scope_is_global = self.scope_is_global.clone();
1347        self.next_register = 0;
1348        self.max_register = 0;
1349        self.free_list.clear();
1350        self.scope_stack = vec![FxHashMap::default()];
1351
1352        if self.scope_is_global != vec![true] {
1353            self.scope_is_global = vec![false];
1354        }
1355        self.reserved_slots.clear();
1356        self.all_var_names.clear();
1357        self.code.clear();
1358        self.constants.clear();
1359        self.parent_vars = parent_vars.clone();
1360        self.upvalues.clear();
1361        self.upvalue_slots.clear();
1362        self.line_table = LineNumberTable::new();
1363        self.depth = 0;
1364        if return_last_expr {
1365            self.is_script_level = true;
1366        } else {
1367            self.is_script_level = false;
1368        }
1369        self.function_uses_arguments = false;
1370        self.current_function_name = function_name.map(|s| s.to_string());
1371
1372        let this_slot = self.alloc_register();
1373        assert_eq!(this_slot, 0);
1374        self.reserved_slots.insert(this_slot);
1375
1376        // Allocate parameter variable slots AFTER the value register range
1377        // so that we can load TDZ without corrupting argument values.
1378        let value_reg_count = params.len() as u16;
1379        // Leave room for value_regs (1..=value_reg_count) and this_slot (0).
1380        // Parameter variable slots and save temps start at value_reg_count + 1.
1381        let param_slot_start = value_reg_count + 1;
1382        if self.next_register < param_slot_start {
1383            self.next_register = param_slot_start;
1384        }
1385
1386        let mut fixed_param_count = 0u16;
1387        let mut before_default = true;
1388        let mut needs_tdz = false;
1389
1390        for param in params {
1391            if self.is_strict {
1392                match param {
1393                    Parameter::Identifier(name) if Self::is_strict_reserved_word(name) => {
1394                        return Err(format!(
1395                            "SyntaxError: '{}' cannot be declared in strict mode code",
1396                            name
1397                        ));
1398                    }
1399                    _ => {}
1400                }
1401            }
1402            match param {
1403                Parameter::Identifier(name) => {
1404                    let slot = self.declare_var(name, VariableKind::Let);
1405                    if before_default {
1406                        fixed_param_count += 1;
1407                    } else {
1408                        needs_tdz = true;
1409                    }
1410                    // Only emit LoadTdz for params that come AFTER the first
1411                    // default (may be referenced by earlier defaults) or for
1412                    // params that ARE defaults (self-reference check).
1413                    if needs_tdz || matches!(param, Parameter::AssignmentPattern(_)) {
1414                        self.emit(Opcode::LoadTdz);
1415                        self.emit_u16(slot);
1416                    }
1417                }
1418                Parameter::Pattern(pattern) => {
1419                    self.predeclare_binding_pattern(pattern, VariableKind::Let);
1420                    if before_default {
1421                        fixed_param_count += 1;
1422                    } else {
1423                        needs_tdz = true;
1424                    }
1425                }
1426                Parameter::AssignmentPattern(ap) => {
1427                    self.predeclare_assignment_target(&ap.left, VariableKind::Let);
1428                    before_default = false;
1429                    // Put this param's variable slot into TDZ so its own
1430                    // default value evaluation can detect self-references.
1431                    if let Some(name) = self.first_binding_name(&ap.left) {
1432                        if let Some(slot) = self.lookup_var_slot(&name) {
1433                            self.emit(Opcode::LoadTdz);
1434                            self.emit_u16(slot);
1435                        }
1436                    }
1437                }
1438                Parameter::RestElement(rest) => {
1439                    self.predeclare_assignment_target(&rest.argument, VariableKind::Let);
1440                    before_default = false;
1441                }
1442            }
1443        }
1444        self.max_register = self.max_register.max(fixed_param_count);
1445
1446        // Save argument values into temp registers before loading TDZ.
1447        // This preserves the actual argument values from being overwritten
1448        // when we LoadTdz into the same registers (slot == value_reg for
1449        // simple params).
1450        let mut arg_saves: Vec<Option<u16>> = Vec::new();
1451        for (index, param) in params.iter().enumerate() {
1452            if matches!(param, Parameter::RestElement(_)) {
1453                arg_saves.push(None);
1454                continue;
1455            }
1456            let value_reg = (index as u16) + 1;
1457            let tmp = self.alloc_register();
1458            self.emit(Opcode::Move);
1459            self.emit_u16(tmp);
1460            self.emit_u16(value_reg);
1461            arg_saves.push(Some(tmp));
1462        }
1463
1464        // Gather rest parameters BEFORE LoadTdz (which overwrites param
1465        // slots that may overlap with extra argument registers).
1466        if params
1467            .iter()
1468            .any(|p| matches!(p, Parameter::RestElement(_)))
1469        {
1470            let rest_source_reg = fixed_param_count + 1;
1471            self.emit(Opcode::GatherRest);
1472            self.emit_u16(rest_source_reg);
1473
1474            for param in params {
1475                if let Parameter::RestElement(rest) = param {
1476                    self.gen_assignment_target_binding(
1477                        &rest.argument,
1478                        rest_source_reg,
1479                        VariableKind::Let,
1480                        ctx,
1481                    )?;
1482                    break;
1483                }
1484            }
1485        }
1486
1487        for (index, param) in params.iter().enumerate() {
1488            let value_reg = (index as u16) + 1;
1489            let saved_tmp = arg_saves[index];
1490
1491            if let Some(tmp) = saved_tmp {
1492                // Restore the argument value from the saved temp, overwriting TDZ.
1493                self.emit(Opcode::Move);
1494                self.emit_u16(value_reg);
1495                self.emit_u16(tmp);
1496                self.free_register(tmp);
1497            }
1498
1499            match param {
1500                Parameter::Identifier(name) => {
1501                    if let Some(slot) = self.lookup_var_slot(name) {
1502                        if slot != value_reg {
1503                            self.emit(Opcode::Move);
1504                            self.emit_u16(slot);
1505                            self.emit_u16(value_reg);
1506                        }
1507                        for scope in self.scope_stack.iter_mut().rev() {
1508                            if let Some(entry) = scope.get_mut(name) {
1509                                entry.initialized = true;
1510                                break;
1511                            }
1512                        }
1513                    }
1514                }
1515                Parameter::Pattern(pattern) => {
1516                    self.gen_binding_pattern(pattern, value_reg, VariableKind::Let, ctx)?;
1517                }
1518                Parameter::AssignmentPattern(ap) => {
1519                    let binding_name = match &*ap.left {
1520                        AssignmentTarget::Identifier(name) => Some(name.as_str()),
1521                        _ => None,
1522                    };
1523                    self.gen_default_value_binding(&ap.right, value_reg, binding_name, ctx)?;
1524                    self.gen_assignment_target_binding(
1525                        &ap.left,
1526                        value_reg,
1527                        VariableKind::Let,
1528                        ctx,
1529                    )?;
1530                }
1531                Parameter::RestElement(_) => break,
1532            }
1533        }
1534
1535        let mut locals: std::collections::HashSet<String> =
1536            self.scope_stack[0].keys().cloned().collect();
1537        let mut inherited = std::collections::HashSet::new();
1538        for stmt in &body.body {
1539            Self::collect_inherited_upvalues_stmt(stmt, &mut locals, &parent_vars, &mut inherited);
1540        }
1541        for name in inherited {
1542            if let Some(parent_var) = parent_vars.get(&name) {
1543                if self.upvalue_slots.contains_key(&name) {
1544                    continue;
1545                }
1546                let upvalue_slot = self.upvalues.len() as u16;
1547                self.upvalue_slots.insert(name.clone(), upvalue_slot);
1548                self.reserved_slots.insert(upvalue_slot);
1549                let atom = ctx.atom_table_mut().intern(name.as_str());
1550                let local_idx = if parent_var.is_inherited {
1551                    u16::MAX
1552                } else {
1553                    parent_var.local_idx
1554                };
1555                self.upvalues.push((atom.0, local_idx));
1556            }
1557        }
1558
1559        for stmt in &body.body {
1560            if let ASTNode::FunctionDeclaration(func) = stmt {
1561                self.declare_var(&func.name, VariableKind::Var);
1562                if Self::detect_tiny_inline_function(func) {
1563                    self.tiny_inline_functions
1564                        .insert(func.name.clone(), func.clone());
1565                }
1566            }
1567        }
1568
1569        for stmt in &body.body {
1570            if let ASTNode::VariableDeclaration(decl) = stmt {
1571                if decl.kind == VariableKind::Var {
1572                    for d in &decl.declarations {
1573                        self.predeclare_binding_pattern(&d.id, VariableKind::Var);
1574                    }
1575                }
1576            }
1577        }
1578
1579        self.push_scope();
1580        self.pre_scan_let_const(&body.body)?;
1581        self.emit_tdz_for_block(&body.body)?;
1582
1583        {
1584            for stmt in &body.body {
1585                if let ASTNode::FunctionDeclaration(func) = stmt {
1586                    let slot = self
1587                        .lookup_var_slot(&func.name)
1588                        .unwrap_or_else(|| self.declare_var(&func.name, VariableKind::Var));
1589                    let reg = self.gen_function_expression(
1590                        &func.params,
1591                        &func.body,
1592                        Some(&func.name),
1593                        ctx,
1594                        func.generator,
1595                        func.is_async,
1596                        false,
1597                    )?;
1598                    if slot != reg {
1599                        self.emit(Opcode::Move);
1600                        self.emit_u16(slot);
1601                        self.emit_u16(reg);
1602                        self.free_register(reg);
1603                    }
1604
1605                    if self.is_global_scope() && !(self.is_strict && self.is_eval) {
1606                        let atom = ctx.intern(&func.name);
1607                        let idx = self.add_constant(JSValue::new_string(atom));
1608                        if self.is_eval {
1609                            self.emit(Opcode::SetGlobalVar);
1610                        } else {
1611                            self.emit(Opcode::SetGlobal);
1612                        }
1613                        self.emit_u32(idx);
1614                        self.emit_u16(slot);
1615                    }
1616                }
1617            }
1618        }
1619
1620        let stmt_count = body.body.len();
1621        let mut last_expr_reg: Option<u16> = None;
1622
1623        let mut last_nonempty_stmt_idx = stmt_count.wrapping_sub(1);
1624        while last_nonempty_stmt_idx < stmt_count {
1625            if !matches!(body.body[last_nonempty_stmt_idx], ASTNode::EmptyStatement) {
1626                break;
1627            }
1628            if last_nonempty_stmt_idx == 0 {
1629                last_nonempty_stmt_idx = stmt_count.wrapping_sub(1);
1630                break;
1631            }
1632            last_nonempty_stmt_idx -= 1;
1633        }
1634
1635        let already_returns = body
1636            .body
1637            .last()
1638            .map_or(false, |s| matches!(s, ASTNode::ReturnStatement(_)));
1639
1640        for (i, stmt) in body.body.iter().enumerate() {
1641            if let Some(&line) = body.lines.get(i) {
1642                self.emit_line(line);
1643            }
1644
1645            if return_last_expr && i == last_nonempty_stmt_idx {
1646                if let ASTNode::ExpressionStatement(es) = stmt {
1647                    last_expr_reg = Some(self.gen_expression(&es.expression, ctx)?);
1648                } else if matches!(stmt, ASTNode::FunctionDeclaration(_)) {
1649                } else if let Some(reg) = self.gen_statement_as_expression(stmt, ctx)? {
1650                    last_expr_reg = Some(reg);
1651                } else {
1652                    self.gen_statement(stmt, ctx)?;
1653                }
1654            } else if matches!(stmt, ASTNode::FunctionDeclaration(_)) {
1655            } else if return_last_expr {
1656                // Track expression statement values for the UpdateEmpty chain.
1657                // Declaration/control-flow statements produce empty completions
1658                // and should preserve the previous value.
1659                if let ASTNode::ExpressionStatement(es) = stmt {
1660                    let reg = self.gen_expression(&es.expression, ctx)?;
1661                    if let Some(prev) = last_expr_reg {
1662                        if prev != reg {
1663                            self.emit(Opcode::Move);
1664                            self.emit_u16(prev);
1665                            self.emit_u16(reg);
1666                        }
1667                        self.free_register(reg);
1668                    } else {
1669                        last_expr_reg = Some(reg);
1670                    }
1671                } else {
1672                    self.gen_statement(stmt, ctx)?;
1673                }
1674            } else {
1675                self.gen_statement(stmt, ctx)?;
1676            }
1677        }
1678
1679        self.pop_scope();
1680
1681        if !already_returns {
1682            let return_reg = if let Some(reg) = last_expr_reg {
1683                reg
1684            } else {
1685                self.emit(Opcode::LoadUndefined);
1686                self.emit_u16(0);
1687                0
1688            };
1689            self.emit(Opcode::Return);
1690            self.emit_u16(return_reg);
1691        }
1692
1693        let line_table = std::mem::take(&mut self.line_table);
1694        let code = std::mem::take(&mut self.code);
1695        let mut constants = std::mem::take(&mut self.constants);
1696        if self.function_uses_arguments {
1697            let atom = if let Some(atom) = ctx.lookup_atom("__usesArguments__") {
1698                atom
1699            } else {
1700                ctx.atom_table_mut().intern("__usesArguments__")
1701            };
1702            if !constants
1703                .iter()
1704                .any(|v| v.is_string() && v.get_atom() == atom)
1705            {
1706                constants.push(JSValue::new_string(atom));
1707            }
1708        }
1709        self.is_script_level = saved_is_script_level;
1710        self.scope_is_global = saved_scope_is_global;
1711        let var_name_to_slot: std::rc::Rc<Vec<(u32, u16)>> = std::rc::Rc::new(
1712            self.all_var_names
1713                .iter()
1714                .map(|(name, slot)| {
1715                    let atom = ctx.intern(name);
1716                    (atom.0, *slot)
1717                })
1718                .collect(),
1719        );
1720        let mut bc = Bytecode {
1721            code,
1722            constants,
1723            locals_count: (self.max_register as u32) + 1,
1724            param_count: fixed_param_count,
1725            line_number_table: if line_table.entries().is_empty() {
1726                None
1727            } else {
1728                Some(std::sync::Arc::new(line_table))
1729            },
1730            ic_table: crate::compiler::InlineCacheTable::new(),
1731            shared_ic_table_ptr: std::ptr::null_mut(),
1732            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
1733            shared_code_ptr: std::ptr::null(),
1734            shared_code_len: 0,
1735            shared_const_ptr: std::ptr::null(),
1736            shared_const_len: 0,
1737            uses_arguments: self.function_uses_arguments,
1738            is_strict: self.is_strict,
1739            var_name_to_slot,
1740            nested_bytecodes: Vec::new(),
1741            is_simple_constructor: false,
1742            simple_constructor_props: Vec::new(),
1743            cached_constructor_final_shape: None,
1744            cached_constructor_atoms: Vec::new(),
1745        };
1746        bc.is_simple_constructor = detect_simple_constructor_props(&bc);
1747        if bc.is_simple_constructor {
1748            let code = &bc.code;
1749            let mut pc = 0usize;
1750            while pc < code.len() {
1751                let op = Opcode::from_u8_unchecked(code[pc]);
1752                let size = Opcode::instruction_size(op);
1753                if size == 0 || pc + size > code.len() {
1754                    break;
1755                }
1756                if op == Opcode::SetNamedProp {
1757                    let obj = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
1758                    let val = u16::from_le_bytes([code[pc + 3], code[pc + 4]]);
1759                    let atom = u32::from_le_bytes([
1760                        code[pc + 5],
1761                        code[pc + 6],
1762                        code[pc + 7],
1763                        code[pc + 8],
1764                    ]);
1765                    if obj == 0 && val >= 1 && val <= bc.param_count {
1766                        bc.simple_constructor_props.push((
1767                            crate::runtime::atom::Atom(atom),
1768                            val - 1,
1769                            (pc + size) as u16,
1770                        ));
1771                        bc.cached_constructor_atoms
1772                            .push(crate::runtime::atom::Atom(atom));
1773                    }
1774                }
1775                pc += size;
1776            }
1777        }
1778        Ok((bc, std::mem::take(&mut self.upvalues)))
1779    }
1780
1781    pub fn take_bytecode(&mut self) -> (Vec<u8>, Vec<JSValue>, u32, u16) {
1782        let result = (
1783            std::mem::take(&mut self.code),
1784            std::mem::take(&mut self.constants),
1785            (self.max_register as u32) + 1,
1786            0,
1787        );
1788        self.next_register = 0;
1789        self.max_register = 0;
1790        self.free_list.clear();
1791        self.scope_stack = vec![FxHashMap::default()];
1792        self.reserved_slots.clear();
1793        self.upvalues.clear();
1794        self.upvalue_slots.clear();
1795        self.depth = 0;
1796        self.function_uses_arguments = false;
1797
1798        result
1799    }
1800
1801    fn pre_scan_let_const(&mut self, body: &[ASTNode]) -> Result<(), String> {
1802        for node in body {
1803            match node {
1804                ASTNode::VariableDeclaration(decl) if decl.kind != VariableKind::Var => {
1805                    for d in &decl.declarations {
1806                        self.pre_scan_binding(&d.id, decl.kind);
1807                    }
1808                }
1809                ASTNode::ClassDeclaration(class) => {
1810                    if let Some(last) = self.scope_stack.last() {
1811                        if !last.contains_key(&class.name) {
1812                            self.declare_var(&class.name, VariableKind::Let);
1813                        }
1814                    }
1815                }
1816                ASTNode::BlockStatement(block) => {
1817                    self.push_scope();
1818                    self.pre_scan_let_const(&block.body)?;
1819                    self.pop_scope();
1820                }
1821                ASTNode::TryStatement(stmt) => {
1822                    self.push_scope();
1823                    self.pre_scan_let_const(&stmt.block.body)?;
1824                    self.pop_scope();
1825                    if let Some(handler) = &stmt.handler {
1826                        self.push_scope();
1827                        if let Some(param) = &handler.param {
1828                            self.pre_scan_binding(param, VariableKind::Let);
1829                        }
1830                        self.pre_scan_let_const(&handler.body.body)?;
1831                        self.pop_scope();
1832                    }
1833                    if let Some(finalizer) = &stmt.finalizer {
1834                        self.push_scope();
1835                        self.pre_scan_let_const(&finalizer.body)?;
1836                        self.pop_scope();
1837                    }
1838                }
1839                ASTNode::ForStatement(stmt) => {
1840                    if let Some(init) = &stmt.init {
1841                        if let ForInit::VariableDeclaration(vd) = init {
1842                            if vd.kind != VariableKind::Var {
1843                                for d in &vd.declarations {
1844                                    self.pre_scan_binding(&d.id, vd.kind);
1845                                }
1846                            }
1847                        }
1848                    }
1849                    self.push_scope();
1850                    if let ASTNode::BlockStatement(block) = stmt.body.as_ref() {
1851                        self.pre_scan_let_const(&block.body)?;
1852                    } else {
1853                        self.pre_scan_let_const(&[stmt.body.as_ref().clone()])?;
1854                    }
1855                    self.pop_scope();
1856                }
1857                _ => {}
1858            }
1859        }
1860        Ok(())
1861    }
1862
1863    fn pre_scan_binding(&mut self, pattern: &BindingPattern, kind: VariableKind) {
1864        match pattern {
1865            BindingPattern::Identifier(name) => {
1866                if let Some(last) = self.scope_stack.last() {
1867                    if !last.contains_key(name) {
1868                        self.declare_var(name, kind);
1869                    }
1870                }
1871            }
1872            BindingPattern::ArrayPattern(arr) => {
1873                for elem in &arr.elements {
1874                    if let Some(pat) = elem {
1875                        match pat {
1876                            PatternElement::Pattern(p) => {
1877                                self.pre_scan_assignment_target_binding(p, kind)
1878                            }
1879                            PatternElement::RestElement(r) => {
1880                                self.pre_scan_assignment_target_binding(&r.argument, kind)
1881                            }
1882                            PatternElement::AssignmentPattern(ap) => {
1883                                self.pre_scan_assignment_target_binding(&ap.left, kind)
1884                            }
1885                        }
1886                    }
1887                }
1888            }
1889            BindingPattern::ObjectPattern(obj) => {
1890                for prop in &obj.properties {
1891                    match prop {
1892                        ObjectPatternProperty::Property { value, .. } => {
1893                            self.pre_scan_assignment_target_binding(value, kind);
1894                        }
1895                        ObjectPatternProperty::RestElement(r) => {
1896                            self.pre_scan_assignment_target_binding(&r.argument, kind);
1897                        }
1898                    }
1899                }
1900            }
1901            BindingPattern::AssignmentPattern(ap) => {
1902                self.pre_scan_assignment_target_binding(&ap.left, kind);
1903            }
1904        }
1905    }
1906
1907    fn pre_scan_assignment_target_binding(
1908        &mut self,
1909        target: &AssignmentTarget,
1910        kind: VariableKind,
1911    ) {
1912        match target {
1913            AssignmentTarget::Identifier(name) => {
1914                if let Some(last) = self.scope_stack.last() {
1915                    if !last.contains_key(name) {
1916                        self.declare_var(name, kind);
1917                    }
1918                }
1919            }
1920            AssignmentTarget::ArrayPattern(arr) => {
1921                for elem in &arr.elements {
1922                    if let Some(pat) = elem {
1923                        match pat {
1924                            PatternElement::Pattern(p) => {
1925                                self.pre_scan_assignment_target_binding(p, kind)
1926                            }
1927                            PatternElement::RestElement(r) => {
1928                                self.pre_scan_assignment_target_binding(&r.argument, kind)
1929                            }
1930                            PatternElement::AssignmentPattern(ap) => {
1931                                self.pre_scan_assignment_target_binding(&ap.left, kind)
1932                            }
1933                        }
1934                    }
1935                }
1936            }
1937            AssignmentTarget::ObjectPattern(obj) => {
1938                for prop in &obj.properties {
1939                    match prop {
1940                        ObjectPatternProperty::Property { value, .. } => {
1941                            self.pre_scan_assignment_target_binding(value, kind);
1942                        }
1943                        ObjectPatternProperty::RestElement(r) => {
1944                            self.pre_scan_assignment_target_binding(&r.argument, kind);
1945                        }
1946                    }
1947                }
1948            }
1949            AssignmentTarget::AssignmentPattern(ap) => {
1950                self.pre_scan_assignment_target_binding(&ap.left, kind);
1951            }
1952            _ => {}
1953        }
1954    }
1955
1956    fn emit_tdz_for_block(&mut self, body: &[ASTNode]) -> Result<(), String> {
1957        for node in body {
1958            if let ASTNode::VariableDeclaration(decl) = node {
1959                if decl.kind != VariableKind::Var {
1960                    for d in &decl.declarations {
1961                        self.emit_tdz_for_binding(&d.id)?;
1962                    }
1963                }
1964            } else if let ASTNode::ClassDeclaration(class) = node {
1965                if let Some(slot) = self.lookup_var_slot(&class.name) {
1966                    self.emit(Opcode::LoadTdz);
1967                    self.emit_u16(slot);
1968                }
1969            }
1970        }
1971        Ok(())
1972    }
1973
1974    fn emit_tdz_for_binding(&mut self, pattern: &BindingPattern) -> Result<(), String> {
1975        match pattern {
1976            BindingPattern::Identifier(name) => {
1977                if let Some(slot) = self.lookup_var_slot(name) {
1978                    self.emit(Opcode::LoadTdz);
1979                    self.emit_u16(slot);
1980                }
1981            }
1982            BindingPattern::ArrayPattern(arr) => {
1983                for elem in &arr.elements {
1984                    if let Some(pat) = elem {
1985                        match pat {
1986                            PatternElement::Pattern(p) => self.emit_tdz_for_assignment_target(p)?,
1987                            PatternElement::RestElement(r) => {
1988                                self.emit_tdz_for_assignment_target(&r.argument)?
1989                            }
1990                            PatternElement::AssignmentPattern(ap) => {
1991                                self.emit_tdz_for_assignment_target(&ap.left)?
1992                            }
1993                        }
1994                    }
1995                }
1996            }
1997            BindingPattern::ObjectPattern(obj) => {
1998                for prop in &obj.properties {
1999                    match prop {
2000                        ObjectPatternProperty::Property { value, .. } => {
2001                            self.emit_tdz_for_assignment_target(value)?;
2002                        }
2003                        ObjectPatternProperty::RestElement(r) => {
2004                            self.emit_tdz_for_assignment_target(&r.argument)?;
2005                        }
2006                    }
2007                }
2008            }
2009            BindingPattern::AssignmentPattern(ap) => {
2010                self.emit_tdz_for_assignment_target(&ap.left)?;
2011            }
2012        }
2013        Ok(())
2014    }
2015
2016    fn emit_tdz_for_assignment_target(&mut self, target: &AssignmentTarget) -> Result<(), String> {
2017        match target {
2018            AssignmentTarget::Identifier(name) => {
2019                if let Some(slot) = self.lookup_var_slot(name) {
2020                    self.emit(Opcode::LoadTdz);
2021                    self.emit_u16(slot);
2022                }
2023            }
2024            AssignmentTarget::ArrayPattern(arr) => {
2025                for elem in &arr.elements {
2026                    if let Some(pat) = elem {
2027                        match pat {
2028                            PatternElement::Pattern(p) => self.emit_tdz_for_assignment_target(p)?,
2029                            PatternElement::RestElement(r) => {
2030                                self.emit_tdz_for_assignment_target(&r.argument)?
2031                            }
2032                            PatternElement::AssignmentPattern(ap) => {
2033                                self.emit_tdz_for_assignment_target(&ap.left)?
2034                            }
2035                        }
2036                    }
2037                }
2038            }
2039            AssignmentTarget::ObjectPattern(obj) => {
2040                for prop in &obj.properties {
2041                    match prop {
2042                        ObjectPatternProperty::Property { value, .. } => {
2043                            self.emit_tdz_for_assignment_target(value)?;
2044                        }
2045                        ObjectPatternProperty::RestElement(r) => {
2046                            self.emit_tdz_for_assignment_target(&r.argument)?;
2047                        }
2048                    }
2049                }
2050            }
2051            AssignmentTarget::AssignmentPattern(ap) => {
2052                self.emit_tdz_for_assignment_target(&ap.left)?;
2053            }
2054            _ => {}
2055        }
2056        Ok(())
2057    }
2058
2059    fn gen_statement(&mut self, stmt: &ASTNode, ctx: &mut JSContext) -> Result<(), String> {
2060        match stmt {
2061            ASTNode::ExpressionStatement(es) => {
2062                let r = self.gen_expression(&es.expression, ctx)?;
2063
2064                self.free_register(r);
2065            }
2066            ASTNode::VariableDeclaration(decl) => {
2067                self.gen_variable_declaration(decl, ctx)?;
2068            }
2069            ASTNode::FunctionDeclaration(func) => {
2070                let slot = self
2071                    .lookup_var_slot(&func.name)
2072                    .unwrap_or_else(|| self.declare_var(&func.name, VariableKind::Var));
2073                let reg = self.gen_function_expression(
2074                    &func.params,
2075                    &func.body,
2076                    Some(&func.name),
2077                    ctx,
2078                    func.generator,
2079                    func.is_async,
2080                    false,
2081                )?;
2082                if slot != reg {
2083                    self.emit(Opcode::Move);
2084                    self.emit_u16(slot);
2085                    self.emit_u16(reg);
2086                }
2087
2088                if self.is_global_scope() && !(self.is_strict && self.is_eval) {
2089                    let atom = ctx.intern(&func.name);
2090                    let idx = self.add_constant(JSValue::new_string(atom));
2091                    if self.is_eval {
2092                        self.emit(Opcode::SetGlobalVar);
2093                    } else {
2094                        self.emit(Opcode::SetGlobal);
2095                    }
2096                    self.emit_u32(idx);
2097                    self.emit_u16(slot);
2098                }
2099            }
2100            ASTNode::ClassDeclaration(class) => {
2101                self.gen_class_declaration(class, ctx)?;
2102            }
2103            ASTNode::BlockStatement(block) => {
2104                self.push_scope();
2105                self.pre_scan_let_const(&block.body)?;
2106                self.emit_tdz_for_block(&block.body)?;
2107                for (i, s) in block.body.iter().enumerate() {
2108                    if let Some(&line) = block.lines.get(i) {
2109                        self.emit_line(line);
2110                    }
2111                    self.gen_statement(s, ctx)?;
2112                }
2113                self.pop_scope();
2114            }
2115            ASTNode::IfStatement(stmt) => {
2116                self.gen_if_statement(stmt, ctx)?;
2117            }
2118            ASTNode::WhileStatement(stmt) => {
2119                self.gen_while_statement(stmt, ctx)?;
2120            }
2121            ASTNode::ForStatement(stmt) => {
2122                self.gen_for_statement(stmt, ctx)?;
2123            }
2124            ASTNode::ForInStatement(stmt) => {
2125                self.gen_for_in_statement(stmt, ctx)?;
2126            }
2127            ASTNode::ForOfStatement(stmt) => {
2128                self.gen_for_of_statement(stmt, ctx)?;
2129            }
2130            ASTNode::DoWhileStatement(stmt) => {
2131                self.gen_do_while_statement(stmt, ctx)?;
2132            }
2133            ASTNode::SwitchStatement(stmt) => {
2134                self.gen_switch_statement(stmt, ctx)?;
2135            }
2136            ASTNode::BreakStatement(stmt) => {
2137                if stmt.label.is_some() {
2138                    if !self
2139                        .breakable_frames
2140                        .iter()
2141                        .any(|f| f.label.as_deref() == stmt.label.as_deref())
2142                    {
2143                        return Err(format!(
2144                            "SyntaxError: Label '{}' not found",
2145                            stmt.label.as_deref().unwrap()
2146                        ));
2147                    }
2148                } else if self.breakable_frames.is_empty() {
2149                    return Err("SyntaxError: Illegal break statement".to_string());
2150                }
2151                self.emit(Opcode::Jump);
2152                let patch = self.code.len();
2153                self.emit_i32(0);
2154                if let Some(label) = &stmt.label {
2155                    if let Some(frame) = self
2156                        .breakable_frames
2157                        .iter_mut()
2158                        .rev()
2159                        .find(|f| f.label.as_deref() == Some(label))
2160                    {
2161                        frame.break_patches.push(patch);
2162                    }
2163                } else if let Some(frame) = self.breakable_frames.last_mut() {
2164                    frame.break_patches.push(patch);
2165                }
2166            }
2167            ASTNode::ContinueStatement(stmt) => {
2168                if stmt.label.is_some() {
2169                    let found = self.breakable_frames.iter().any(|f| {
2170                        f.label.as_deref() == stmt.label.as_deref() && f.continue_target.is_some()
2171                    });
2172                    if !found {
2173                        return Err(format!(
2174                            "SyntaxError: Continue target '{}' not found",
2175                            stmt.label.as_deref().unwrap()
2176                        ));
2177                    }
2178                } else if !self
2179                    .breakable_frames
2180                    .iter()
2181                    .any(|f| f.continue_target.is_some())
2182                {
2183                    return Err("SyntaxError: Illegal continue statement".to_string());
2184                }
2185                self.emit(Opcode::Jump);
2186                let patch = self.code.len();
2187                self.emit_i32(0);
2188                if let Some(label) = &stmt.label {
2189                    let found =
2190                        self.breakable_frames.iter_mut().rev().find(|f| {
2191                            f.label.as_deref() == Some(label) && f.continue_target.is_some()
2192                        });
2193                    if let Some(frame) = found {
2194                        frame.continue_patches.push(patch);
2195                    }
2196                } else if let Some(frame) = self
2197                    .breakable_frames
2198                    .iter_mut()
2199                    .rev()
2200                    .find(|f| f.continue_target.is_some())
2201                {
2202                    frame.continue_patches.push(patch);
2203                }
2204            }
2205            ASTNode::ReturnStatement(stmt) => {
2206                if self.is_script_level {
2207                    return Err("SyntaxError: Illegal return statement".to_string());
2208                }
2209                let return_reg = if let Some(expr) = &stmt.argument {
2210                    self.gen_expression(expr, ctx)?
2211                } else {
2212                    self.emit(Opcode::LoadUndefined);
2213                    self.emit_u16(0);
2214                    0
2215                };
2216                self.emit(Opcode::Return);
2217                self.emit_u16(return_reg);
2218            }
2219            ASTNode::ThrowStatement(stmt) => {
2220                self.gen_throw_statement(stmt, ctx)?;
2221            }
2222            ASTNode::TryStatement(stmt) => {
2223                self.gen_try_statement(stmt, ctx, None)?;
2224            }
2225            ASTNode::WithStatement(_) => {
2226                if self.is_strict {
2227                    return Err(
2228                        "SyntaxError: Strict mode code may not include a with statement"
2229                            .to_string(),
2230                    );
2231                }
2232                return Err("with statement is not supported".to_string());
2233            }
2234            ASTNode::EmptyStatement => {}
2235            ASTNode::LabelledStatement(stmt) => {
2236                let saved = self.pending_label.clone();
2237                self.pending_label = None;
2238                self.breakable_frames.push(BreakableFrame {
2239                    label: Some(stmt.label.clone()),
2240                    break_patches: Vec::new(),
2241                    continue_patches: Vec::new(),
2242                    continue_target: None,
2243                });
2244                let saved_label = self.pending_label.clone();
2245                self.pending_label = Some(stmt.label.clone());
2246                self.gen_statement(&stmt.body, ctx)?;
2247                self.pending_label = saved_label;
2248                let frame = self.breakable_frames.pop().unwrap();
2249                let end_pos = self.code.len();
2250                for patch in frame.break_patches {
2251                    self.patch_jump(patch, end_pos);
2252                }
2253                self.pending_label = saved;
2254            }
2255            _ => {}
2256        }
2257        Ok(())
2258    }
2259
2260    fn gen_binding_pattern(
2261        &mut self,
2262        pattern: &BindingPattern,
2263        value_reg: u16,
2264        kind: VariableKind,
2265        ctx: &mut JSContext,
2266    ) -> Result<(), String> {
2267        match pattern {
2268            BindingPattern::Identifier(name) => {
2269                if self.is_strict && Self::is_strict_reserved_word(name) {
2270                    return Err(format!(
2271                        "SyntaxError: '{}' cannot be declared in strict mode code",
2272                        name
2273                    ));
2274                }
2275                let slot = if kind == VariableKind::Var {
2276                    self.declare_var(name, kind)
2277                } else {
2278                    self.lookup_var_slot(name)
2279                        .unwrap_or_else(|| self.declare_var(name, kind))
2280                };
2281
2282                for scope in self.scope_stack.iter_mut().rev() {
2283                    if let Some(entry) = scope.get_mut(name) {
2284                        entry.initialized = true;
2285                        break;
2286                    }
2287                }
2288                if slot != value_reg {
2289                    self.emit(Opcode::Move);
2290                    self.emit_u16(slot);
2291                    self.emit_u16(value_reg);
2292                }
2293
2294                if kind == VariableKind::Var
2295                    && self.is_global_scope()
2296                    && !(self.is_strict && self.is_eval)
2297                {
2298                    let atom = ctx.intern(name);
2299                    let idx = self.add_constant(JSValue::new_string(atom));
2300                    self.emit(Opcode::SetGlobalVar);
2301                    self.emit_u32(idx);
2302                    self.emit_u16(slot);
2303                }
2304                Ok(())
2305            }
2306            BindingPattern::ArrayPattern(arr) => {
2307                self.emit(Opcode::CheckObjectCoercible);
2308                self.emit_u16(value_reg);
2309
2310                let iter_reg = self.alloc_register();
2311                self.emit(Opcode::GetIterator);
2312                self.emit_u16(iter_reg);
2313                self.emit_u16(value_reg);
2314
2315                let len = arr.elements.len();
2316                for (i, elem) in arr.elements.iter().enumerate() {
2317                    if i == len - 1 {
2318                        if let Some(PatternElement::RestElement(rest)) = elem {
2319                            let rest_reg = self.alloc_register();
2320                            self.emit(Opcode::NewArray);
2321                            self.emit_u16(rest_reg);
2322                            self.emit_u16(0);
2323                            let loop_start = self.code.len();
2324                            let val_for_rest = self.alloc_register();
2325                            let done_for_rest = self.alloc_register();
2326                            self.emit(Opcode::IteratorNext);
2327                            self.emit_u16(val_for_rest);
2328                            self.emit_u16(done_for_rest);
2329                            self.emit_u16(iter_reg);
2330                            self.emit(Opcode::JumpIf);
2331                            self.emit_u16(done_for_rest);
2332                            let exit_patch = self.code.len();
2333                            self.emit_i32(0);
2334                            self.free_register(done_for_rest);
2335                            self.emit(Opcode::ArrayPush);
2336                            self.emit_u16(rest_reg);
2337                            self.emit_u16(val_for_rest);
2338                            self.free_register(val_for_rest);
2339                            self.emit_backward_jump(loop_start);
2340                            let end_pos = self.code.len();
2341                            self.patch_jump(exit_patch, end_pos);
2342                            self.gen_assignment_target_binding(
2343                                &rest.argument,
2344                                rest_reg,
2345                                kind,
2346                                ctx,
2347                            )?;
2348                            self.free_register(rest_reg);
2349                            self.free_register(iter_reg);
2350                            return Ok(());
2351                        }
2352                    }
2353
2354                    let val_reg = self.alloc_register();
2355                    let done_reg = self.alloc_register();
2356                    self.emit(Opcode::IteratorNext);
2357                    self.emit_u16(val_reg);
2358                    self.emit_u16(done_reg);
2359                    self.emit_u16(iter_reg);
2360
2361                    self.emit(Opcode::JumpIfNot);
2362                    self.emit_u16(done_reg);
2363                    let not_done_skip = self.code.len();
2364                    self.emit_i32(0);
2365
2366                    let undef_reg = self.alloc_register();
2367                    self.emit(Opcode::LoadUndefined);
2368                    self.emit_u16(undef_reg);
2369                    self.emit(Opcode::Move);
2370                    self.emit_u16(val_reg);
2371                    self.emit_u16(undef_reg);
2372                    self.free_register(undef_reg);
2373
2374                    let after_undef = self.code.len();
2375                    self.patch_jump(not_done_skip, after_undef);
2376                    self.free_register(done_reg);
2377
2378                    if let Some(elem) = elem {
2379                        match elem {
2380                            PatternElement::Pattern(target) => {
2381                                self.gen_assignment_target_binding(target, val_reg, kind, ctx)?;
2382                            }
2383                            PatternElement::RestElement(_) => {
2384                                return Err("rest element not at end".to_string());
2385                            }
2386                            PatternElement::AssignmentPattern(ap) => {
2387                                let binding_name = match &*ap.left {
2388                                    AssignmentTarget::Identifier(name) => Some(name.as_str()),
2389                                    _ => None,
2390                                };
2391                                self.gen_default_value_binding(
2392                                    &ap.right,
2393                                    val_reg,
2394                                    binding_name,
2395                                    ctx,
2396                                )?;
2397                                self.gen_assignment_target_binding(&ap.left, val_reg, kind, ctx)?;
2398                            }
2399                        }
2400                    }
2401                    self.free_register(val_reg);
2402                }
2403                self.free_register(iter_reg);
2404                Ok(())
2405            }
2406            BindingPattern::ObjectPattern(obj) => {
2407                self.emit(Opcode::CheckObjectCoercible);
2408                self.emit_u16(value_reg);
2409                for prop in &obj.properties {
2410                    match prop {
2411                        ObjectPatternProperty::Property {
2412                            key,
2413                            value,
2414                            computed,
2415                            ..
2416                        } => {
2417                            let key_reg = if *computed {
2418                                match key {
2419                                    PropertyKey::Computed(expr) => {
2420                                        self.gen_expression(expr, ctx)?
2421                                    }
2422                                    _ => return Err("Expected computed key".to_string()),
2423                                }
2424                            } else {
2425                                match key {
2426                                    PropertyKey::Identifier(k)
2427                                    | PropertyKey::Literal(Literal::String(k, _)) => {
2428                                        let idx = self.intern_and_add_const(ctx, k);
2429                                        let kreg = self.alloc_register();
2430                                        self.emit(Opcode::LoadConst);
2431                                        self.emit_u16(kreg);
2432                                        self.emit_u32(idx);
2433                                        kreg
2434                                    }
2435                                    PropertyKey::Literal(Literal::Number(n)) => {
2436                                        let kreg = self.alloc_register();
2437                                        if *n >= i32::MIN as f64
2438                                            && *n <= i32::MAX as f64
2439                                            && n.fract() == 0.0
2440                                        {
2441                                            self.emit_int_const(kreg, *n as i32);
2442                                        } else {
2443                                            let cidx = self.add_constant(JSValue::new_float(*n));
2444                                            self.emit(Opcode::LoadConst);
2445                                            self.emit_u16(kreg);
2446                                            self.emit_u32(cidx);
2447                                        }
2448                                        kreg
2449                                    }
2450                                    _ => return Err("Invalid property key".to_string()),
2451                                }
2452                            };
2453                            let elem_reg = self.alloc_register();
2454                            self.emit(Opcode::GetProp);
2455                            self.emit_u16(elem_reg);
2456                            self.emit_u16(value_reg);
2457                            self.emit_u16(key_reg);
2458                            self.free_register(key_reg);
2459                            self.gen_assignment_target_binding(value, elem_reg, kind, ctx)?;
2460                            self.free_register(elem_reg);
2461                        }
2462                        ObjectPatternProperty::RestElement(rest) => {
2463                            let rest_reg = self.alloc_register();
2464                            self.emit(Opcode::NewObject);
2465                            self.emit_u16(rest_reg);
2466                            self.emit(Opcode::ObjectSpread);
2467                            self.emit_u16(rest_reg);
2468                            self.emit_u16(value_reg);
2469                            self.gen_assignment_target_binding(
2470                                &rest.argument,
2471                                rest_reg,
2472                                kind,
2473                                ctx,
2474                            )?;
2475                            self.free_register(rest_reg);
2476                        }
2477                    }
2478                }
2479                Ok(())
2480            }
2481            BindingPattern::AssignmentPattern(ap) => {
2482                let binding_name = match &*ap.left {
2483                    AssignmentTarget::Identifier(name) => Some(name.as_str()),
2484                    _ => None,
2485                };
2486                self.gen_default_value_binding(&ap.right, value_reg, binding_name, ctx)?;
2487                self.gen_assignment_target_binding(&ap.left, value_reg, kind, ctx)
2488            }
2489        }
2490    }
2491
2492    fn gen_assignment_target_binding(
2493        &mut self,
2494        target: &AssignmentTarget,
2495        value_reg: u16,
2496        kind: VariableKind,
2497        ctx: &mut JSContext,
2498    ) -> Result<(), String> {
2499        match target {
2500            AssignmentTarget::Identifier(name) => {
2501                if self.is_strict && Self::is_strict_reserved_word(name) {
2502                    return Err(format!(
2503                        "SyntaxError: '{}' cannot be declared in strict mode code",
2504                        name
2505                    ));
2506                }
2507                let slot = if kind == VariableKind::Var {
2508                    self.declare_var(name, kind)
2509                } else {
2510                    self.lookup_var_slot(name)
2511                        .unwrap_or_else(|| self.declare_var(name, kind))
2512                };
2513                if slot != value_reg {
2514                    self.emit(Opcode::Move);
2515                    self.emit_u16(slot);
2516                    self.emit_u16(value_reg);
2517                }
2518                // Mark as initialized so CheckTdz is not emitted
2519                for scope in self.scope_stack.iter_mut().rev() {
2520                    if let Some(entry) = scope.get_mut(name) {
2521                        entry.initialized = true;
2522                        break;
2523                    }
2524                }
2525                if kind == VariableKind::Var
2526                    && self.is_global_scope()
2527                    && !(self.is_strict && self.is_eval)
2528                {
2529                    let atom = ctx.intern(name);
2530                    let idx = self.add_constant(JSValue::new_string(atom));
2531                    self.emit(Opcode::SetGlobal);
2532                    self.emit_u32(idx);
2533                    self.emit_u16(slot);
2534                }
2535                Ok(())
2536            }
2537            AssignmentTarget::ArrayPattern(arr) => self.gen_binding_pattern(
2538                &BindingPattern::ArrayPattern(arr.clone()),
2539                value_reg,
2540                kind,
2541                ctx,
2542            ),
2543            AssignmentTarget::ObjectPattern(obj) => self.gen_binding_pattern(
2544                &BindingPattern::ObjectPattern(obj.clone()),
2545                value_reg,
2546                kind,
2547                ctx,
2548            ),
2549            AssignmentTarget::AssignmentPattern(ap) => {
2550                let binding_name = match &*ap.left {
2551                    AssignmentTarget::Identifier(name) => Some(name.as_str()),
2552                    _ => None,
2553                };
2554                self.gen_default_value_binding(&ap.right, value_reg, binding_name, ctx)?;
2555                self.gen_assignment_target_binding(&ap.left, value_reg, kind, ctx)
2556            }
2557            _ => Err("Unsupported target in binding pattern".to_string()),
2558        }
2559    }
2560
2561    fn gen_default_value_binding(
2562        &mut self,
2563        default_expr: &Expression,
2564        value_reg: u16,
2565        binding_name: Option<&str>,
2566        ctx: &mut JSContext,
2567    ) -> Result<(), String> {
2568        let cmp_reg = self.alloc_register();
2569        self.emit(Opcode::LoadUndefined);
2570        self.emit_u16(cmp_reg);
2571
2572        self.emit(Opcode::StrictNeq);
2573        self.emit_u16(cmp_reg);
2574        self.emit_u16(value_reg);
2575        self.emit_u16(cmp_reg);
2576
2577        self.emit(Opcode::JumpIf);
2578        self.emit_u16(cmp_reg);
2579        let skip_patch = self.code.len();
2580        self.emit_i32(0);
2581        self.free_register(cmp_reg);
2582
2583        let default_reg = if let Some(name) = binding_name {
2584            self.gen_expression_with_assign_name(default_expr, name, ctx)?
2585        } else {
2586            self.gen_expression(default_expr, ctx)?
2587        };
2588        if default_reg != value_reg {
2589            self.emit(Opcode::Move);
2590            self.emit_u16(value_reg);
2591            self.emit_u16(default_reg);
2592        }
2593        self.free_register(default_reg);
2594
2595        let after_pos = self.code.len();
2596        self.patch_jump(skip_patch, after_pos);
2597
2598        Ok(())
2599    }
2600
2601    fn predeclare_binding_pattern(&mut self, pattern: &BindingPattern, kind: VariableKind) {
2602        match pattern {
2603            BindingPattern::Identifier(name) => {
2604                let _ = self.declare_var(name, kind);
2605            }
2606            BindingPattern::ArrayPattern(arr) => {
2607                for elem in &arr.elements {
2608                    if let Some(elem) = elem {
2609                        match elem {
2610                            PatternElement::Pattern(AssignmentTarget::Identifier(name)) => {
2611                                let _ = self.declare_var(name, kind);
2612                            }
2613                            PatternElement::Pattern(AssignmentTarget::ArrayPattern(arr)) => {
2614                                self.predeclare_binding_pattern(
2615                                    &BindingPattern::ArrayPattern(arr.clone()),
2616                                    kind,
2617                                );
2618                            }
2619                            PatternElement::Pattern(AssignmentTarget::ObjectPattern(obj)) => {
2620                                self.predeclare_binding_pattern(
2621                                    &BindingPattern::ObjectPattern(obj.clone()),
2622                                    kind,
2623                                );
2624                            }
2625                            PatternElement::Pattern(AssignmentTarget::AssignmentPattern(ap)) => {
2626                                self.predeclare_binding_pattern(
2627                                    &BindingPattern::AssignmentPattern(ap.clone()),
2628                                    kind,
2629                                );
2630                            }
2631                            PatternElement::RestElement(rest) => {
2632                                self.predeclare_assignment_target(&rest.argument, kind);
2633                            }
2634                            PatternElement::AssignmentPattern(ap) => {
2635                                self.predeclare_assignment_target(&ap.left, kind);
2636                            }
2637                            _ => {}
2638                        }
2639                    }
2640                }
2641            }
2642            BindingPattern::ObjectPattern(obj) => {
2643                for prop in &obj.properties {
2644                    match prop {
2645                        ObjectPatternProperty::Property { value, .. } => {
2646                            self.predeclare_assignment_target(value, kind);
2647                        }
2648                        ObjectPatternProperty::RestElement(rest) => {
2649                            self.predeclare_assignment_target(&rest.argument, kind);
2650                        }
2651                    }
2652                }
2653            }
2654            BindingPattern::AssignmentPattern(ap) => {
2655                self.predeclare_assignment_target(&ap.left, kind);
2656            }
2657        }
2658    }
2659
2660    fn first_binding_name(&self, target: &AssignmentTarget) -> Option<String> {
2661        match target {
2662            AssignmentTarget::Identifier(name) => Some(name.clone()),
2663            AssignmentTarget::ArrayPattern(arr) => arr.elements.first().and_then(|e| {
2664                if let Some(pat) = e {
2665                    match pat {
2666                        PatternElement::Pattern(p) => self.first_binding_name(p),
2667                        PatternElement::RestElement(r) => self.first_binding_name(&r.argument),
2668                        PatternElement::AssignmentPattern(ap) => self.first_binding_name(&ap.left),
2669                    }
2670                } else {
2671                    None
2672                }
2673            }),
2674            AssignmentTarget::ObjectPattern(obj) => {
2675                obj.properties.first().and_then(|prop| match prop {
2676                    ObjectPatternProperty::Property { value, .. } => self.first_binding_name(value),
2677                    ObjectPatternProperty::RestElement(r) => self.first_binding_name(&r.argument),
2678                })
2679            }
2680            AssignmentTarget::AssignmentPattern(ap) => self.first_binding_name(&ap.left),
2681            _ => None,
2682        }
2683    }
2684
2685    fn predeclare_assignment_target(&mut self, target: &AssignmentTarget, kind: VariableKind) {
2686        match target {
2687            AssignmentTarget::Identifier(name) => {
2688                let _ = self.declare_var(name, kind);
2689            }
2690            AssignmentTarget::ArrayPattern(arr) => {
2691                self.predeclare_binding_pattern(&BindingPattern::ArrayPattern(arr.clone()), kind);
2692            }
2693            AssignmentTarget::ObjectPattern(obj) => {
2694                self.predeclare_binding_pattern(&BindingPattern::ObjectPattern(obj.clone()), kind);
2695            }
2696            AssignmentTarget::AssignmentPattern(ap) => {
2697                self.predeclare_assignment_target(&ap.left, kind);
2698            }
2699            AssignmentTarget::RestElement(rest) => {
2700                self.predeclare_assignment_target(&rest.argument, kind);
2701            }
2702            _ => {}
2703        }
2704    }
2705
2706    fn lookup_var_slot_for_pattern(&self, pattern: &BindingPattern) -> Option<u16> {
2707        match pattern {
2708            BindingPattern::Identifier(name) => self.lookup_var_slot(name),
2709            _ => None,
2710        }
2711    }
2712
2713    fn gen_variable_declaration(
2714        &mut self,
2715        decl: &VariableDeclaration,
2716        ctx: &mut JSContext,
2717    ) -> Result<(), String> {
2718        for d in &decl.declarations {
2719            if decl.kind == VariableKind::Const && d.init.is_none() {
2720                return Err("SyntaxError: Missing initializer in const declaration".to_string());
2721            }
2722
2723            if let Some(init) = &d.init {
2724                let reg = self.gen_expression_with_name(init, &d.id, ctx)?;
2725                self.gen_binding_pattern(&d.id, reg, decl.kind, ctx)?;
2726                self.free_register(reg);
2727            } else {
2728                if decl.kind == VariableKind::Var {
2729                    self.predeclare_binding_pattern(&d.id, VariableKind::Var);
2730                    if self.is_global_scope() && !(self.is_strict && self.is_eval) {
2731                        if let BindingPattern::Identifier(name) = &d.id {
2732                            let slot = self.lookup_var_slot(name).unwrap();
2733                            let atom = ctx.intern(name);
2734                            let idx = self.add_constant(JSValue::new_string(atom));
2735                            if self.is_eval {
2736                                self.emit(Opcode::InitGlobalVar);
2737                                self.emit_u32(idx);
2738                                self.emit_u16(slot);
2739                            } else {
2740                                self.emit(Opcode::DefineGlobal);
2741                                self.emit_u32(idx);
2742                                self.emit_u16(slot);
2743                            }
2744                        }
2745                    }
2746                } else {
2747                    if let Some(slot) = self.lookup_var_slot_for_pattern(&d.id) {
2748                        self.emit(Opcode::LoadUndefined);
2749                        self.emit_u16(slot);
2750                    }
2751
2752                    if let BindingPattern::Identifier(name) = &d.id {
2753                        for scope in self.scope_stack.iter_mut().rev() {
2754                            if let Some(entry) = scope.get_mut(name) {
2755                                entry.initialized = true;
2756                                break;
2757                            }
2758                        }
2759                    }
2760                }
2761            }
2762        }
2763        Ok(())
2764    }
2765
2766    fn emit_load_undefined(&mut self) -> u16 {
2767        let r = self.alloc_register();
2768        self.emit(Opcode::LoadUndefined);
2769        self.emit_u16(r);
2770        r
2771    }
2772
2773    fn unwrap_completion(&mut self, val: Option<u16>) -> u16 {
2774        match val {
2775            Some(r) => r,
2776            None => self.emit_load_undefined(),
2777        }
2778    }
2779
2780    fn gen_statement_as_expression(
2781        &mut self,
2782        stmt: &ASTNode,
2783        ctx: &mut JSContext,
2784    ) -> Result<Option<u16>, String> {
2785        match stmt {
2786            ASTNode::ExpressionStatement(es) => Ok(Some(self.gen_expression(&es.expression, ctx)?)),
2787            ASTNode::IfStatement(if_stmt) => {
2788                Ok(Some(self.gen_if_statement_as_expression(if_stmt, ctx)?))
2789            }
2790            ASTNode::BlockStatement(block) => {
2791                Ok(Some(self.gen_block_statement_as_expression(block, ctx)?))
2792            }
2793            ASTNode::TryStatement(try_stmt) => {
2794                Ok(Some(self.gen_try_statement_as_expression(try_stmt, ctx)?))
2795            }
2796            ASTNode::SwitchStatement(switch_stmt) => Ok(Some(
2797                self.gen_switch_statement_as_expression(switch_stmt, ctx)?,
2798            )),
2799            ASTNode::VariableDeclaration(_)
2800            | ASTNode::FunctionDeclaration(_)
2801            | ASTNode::ClassDeclaration(_) => {
2802                self.gen_statement(stmt, ctx)?;
2803                Ok(None)
2804            }
2805            ASTNode::ForStatement(f_stmt) => {
2806                let reg = self.alloc_register();
2807                self.emit(Opcode::LoadUndefined);
2808                self.emit_u16(reg);
2809                self.gen_for_statement_with_result(f_stmt, ctx, Some(reg))?;
2810                Ok(Some(reg))
2811            }
2812            ASTNode::ForInStatement(fi_stmt) => {
2813                let reg = self.alloc_register();
2814                self.emit(Opcode::LoadUndefined);
2815                self.emit_u16(reg);
2816                self.gen_for_in_statement_with_result(fi_stmt, ctx, Some(reg))?;
2817                Ok(Some(reg))
2818            }
2819            ASTNode::ForOfStatement(fo_stmt) => {
2820                let reg = self.alloc_register();
2821                self.emit(Opcode::LoadUndefined);
2822                self.emit_u16(reg);
2823                self.gen_for_of_statement_with_result(fo_stmt, ctx, Some(reg))?;
2824                Ok(Some(reg))
2825            }
2826            ASTNode::WhileStatement(w_stmt) => {
2827                let reg = self.alloc_register();
2828                self.emit(Opcode::LoadUndefined);
2829                self.emit_u16(reg);
2830                self.gen_while_statement_with_result(w_stmt, ctx, Some(reg))?;
2831                Ok(Some(reg))
2832            }
2833            ASTNode::DoWhileStatement(dw_stmt) => {
2834                let reg = self.alloc_register();
2835                self.emit(Opcode::LoadUndefined);
2836                self.emit_u16(reg);
2837                self.gen_do_while_statement_with_result(dw_stmt, ctx, Some(reg))?;
2838                Ok(Some(reg))
2839            }
2840            ASTNode::ReturnStatement(_)
2841            | ASTNode::ThrowStatement(_)
2842            | ASTNode::BreakStatement(_)
2843            | ASTNode::ContinueStatement(_) => {
2844                self.gen_statement(stmt, ctx)?;
2845                Ok(None)
2846            }
2847            ASTNode::LabelledStatement(labeled) => {
2848                self.pending_label = Some(labeled.label.clone());
2849                self.breakable_frames.push(BreakableFrame {
2850                    label: Some(labeled.label.clone()),
2851                    break_patches: Vec::new(),
2852                    continue_patches: Vec::new(),
2853                    continue_target: None,
2854                });
2855                let result = self.gen_statement_as_expression(&labeled.body, ctx);
2856                let frame = self.breakable_frames.pop().unwrap();
2857                let end_pos = self.code.len();
2858                for patch in frame.break_patches {
2859                    self.patch_jump(patch, end_pos);
2860                }
2861                for patch in frame.continue_patches {
2862                    if let Some(ct) = frame.continue_target {
2863                        self.patch_jump(patch, ct);
2864                    }
2865                }
2866                self.pending_label = None;
2867                result
2868            }
2869            ASTNode::WithStatement(_) | ASTNode::EmptyStatement | ASTNode::DebuggerStatement => {
2870                Ok(None)
2871            }
2872            _ => Ok(None),
2873        }
2874    }
2875
2876    fn gen_block_statement_as_expression(
2877        &mut self,
2878        block: &BlockStatement,
2879        ctx: &mut JSContext,
2880    ) -> Result<u16, String> {
2881        let result_reg = self.alloc_register();
2882        self.emit(Opcode::LoadUndefined);
2883        self.emit_u16(result_reg);
2884        self.gen_block_with_target(block, ctx, result_reg)?;
2885        Ok(result_reg)
2886    }
2887
2888    fn gen_block_with_target(
2889        &mut self,
2890        block: &BlockStatement,
2891        ctx: &mut JSContext,
2892        target_reg: u16,
2893    ) -> Result<(), String> {
2894        self.push_scope();
2895        self.pre_scan_let_const(&block.body)?;
2896        self.emit_tdz_for_block(&block.body)?;
2897
2898        for (i, s) in block.body.iter().enumerate() {
2899            if let Some(&line) = block.lines.get(i) {
2900                self.emit_line(line);
2901            }
2902            self.gen_statement_with_target(s, ctx, target_reg)?;
2903        }
2904        self.pop_scope();
2905        Ok(())
2906    }
2907
2908    fn gen_statement_with_target(
2909        &mut self,
2910        stmt: &ASTNode,
2911        ctx: &mut JSContext,
2912        target_reg: u16,
2913    ) -> Result<(), String> {
2914        match stmt {
2915            ASTNode::BlockStatement(block) => {
2916                self.push_scope();
2917                self.pre_scan_let_const(&block.body)?;
2918                self.emit_tdz_for_block(&block.body)?;
2919                for (i, s) in block.body.iter().enumerate() {
2920                    if let Some(&line) = block.lines.get(i) {
2921                        self.emit_line(line);
2922                    }
2923                    self.gen_statement_with_target(s, ctx, target_reg)?;
2924                }
2925                self.pop_scope();
2926            }
2927            ASTNode::ExpressionStatement(es) => {
2928                let reg = self.gen_expression(&es.expression, ctx)?;
2929                if reg != target_reg {
2930                    self.emit(Opcode::Move);
2931                    self.emit_u16(target_reg);
2932                    self.emit_u16(reg);
2933                }
2934                self.free_register(reg);
2935            }
2936            ASTNode::IfStatement(if_stmt) => {
2937                self.gen_if_statement_with_target(if_stmt, ctx, target_reg)?;
2938            }
2939            ASTNode::BreakStatement(_) | ASTNode::ContinueStatement(_) => {
2940                self.gen_statement(stmt, ctx)?;
2941            }
2942            ASTNode::ReturnStatement(ret) => {
2943                if let Some(ref arg) = ret.argument {
2944                    let reg = self.gen_expression(arg, ctx)?;
2945                    self.emit(Opcode::Return);
2946                    self.emit_u16(reg);
2947                } else {
2948                    self.emit(Opcode::LoadUndefined);
2949                    self.emit_u16(0);
2950                    self.emit(Opcode::Return);
2951                    self.emit_u16(0);
2952                }
2953            }
2954            ASTNode::ThrowStatement(throw) => {
2955                let reg = self.gen_expression(&throw.argument, ctx)?;
2956                self.emit(Opcode::Throw);
2957                self.emit_u16(reg);
2958            }
2959            ASTNode::WhileStatement(w_stmt) => {
2960                self.gen_while_statement_with_result(w_stmt, ctx, Some(target_reg))?;
2961            }
2962            ASTNode::DoWhileStatement(dw_stmt) => {
2963                self.gen_do_while_statement_with_result(dw_stmt, ctx, Some(target_reg))?;
2964            }
2965            ASTNode::SwitchStatement(sw_stmt) => {
2966                self.emit(Opcode::LoadUndefined);
2967                self.emit_u16(target_reg);
2968                self.gen_switch_statement_with_result(sw_stmt, ctx, Some(target_reg))?;
2969            }
2970            ASTNode::ForInStatement(fi_stmt) => {
2971                self.gen_for_in_statement_with_result(fi_stmt, ctx, Some(target_reg))?;
2972            }
2973            ASTNode::ForOfStatement(fo_stmt) => {
2974                self.gen_for_of_statement_with_result(fo_stmt, ctx, Some(target_reg))?;
2975            }
2976            ASTNode::TryStatement(try_stmt) => {
2977                self.gen_try_statement(try_stmt, ctx, Some(target_reg))?;
2978            }
2979            _ => {
2980                if let Some(reg) = self.gen_statement_as_expression(stmt, ctx)? {
2981                    if reg != target_reg {
2982                        self.emit(Opcode::Move);
2983                        self.emit_u16(target_reg);
2984                        self.emit_u16(reg);
2985                        self.free_register(reg);
2986                    }
2987                } else if !matches!(
2988                    stmt,
2989                    ASTNode::VariableDeclaration(_)
2990                        | ASTNode::FunctionDeclaration(_)
2991                        | ASTNode::ClassDeclaration(_)
2992                ) {
2993                    self.gen_statement(stmt, ctx)?;
2994                }
2995            }
2996        }
2997        Ok(())
2998    }
2999
3000    fn gen_if_statement_as_expression(
3001        &mut self,
3002        stmt: &IfStatement,
3003        ctx: &mut JSContext,
3004    ) -> Result<u16, String> {
3005        if self.p2_fold_profile.allow_if_bool() {
3006            if let Expression::Literal(Literal::Boolean(test_bool)) = &stmt.test {
3007                if *test_bool {
3008                    return self
3009                        .gen_statement_as_expression(&stmt.consequent, ctx)
3010                        .map(|r| self.unwrap_completion(r));
3011                } else if let Some(alt) = &stmt.alternate {
3012                    return self
3013                        .gen_statement_as_expression(alt, ctx)
3014                        .map(|r| self.unwrap_completion(r));
3015                } else {
3016                    return Ok(self.emit_load_undefined());
3017                }
3018            }
3019        }
3020
3021        let test_reg = self.gen_expression(&stmt.test, ctx)?;
3022
3023        if self.opt_branch_result_prealloc {
3024            let result = if self.reserved_slots.contains(&test_reg) {
3025                let r = self.alloc_register();
3026                self.emit(Opcode::Move);
3027                self.emit_u16(r);
3028                self.emit_u16(test_reg);
3029                r
3030            } else {
3031                test_reg
3032            };
3033
3034            self.emit(Opcode::JumpIfNot);
3035            self.emit_u16(test_reg);
3036            let else_jump = self.code.len();
3037            self.emit_i32(0);
3038
3039            let cons_opt = self.gen_statement_as_expression(&stmt.consequent, ctx)?;
3040            let cons = self.unwrap_completion(cons_opt);
3041            if cons != result {
3042                self.emit(Opcode::Move);
3043                self.emit_u16(result);
3044                self.emit_u16(cons);
3045                self.free_register(cons);
3046            }
3047
3048            self.emit(Opcode::Jump);
3049            let end_jump = self.code.len();
3050            self.emit_i32(0);
3051
3052            let else_pos = self.code.len();
3053            self.patch_jump(else_jump, else_pos);
3054
3055            let alt = if let Some(alt_stmt) = &stmt.alternate {
3056                let alt_opt = self.gen_statement_as_expression(alt_stmt, ctx)?;
3057                self.unwrap_completion(alt_opt)
3058            } else {
3059                self.emit_load_undefined()
3060            };
3061            if alt != result {
3062                self.emit(Opcode::Move);
3063                self.emit_u16(result);
3064                self.emit_u16(alt);
3065                self.free_register(alt);
3066            }
3067
3068            let end_pos = self.code.len();
3069            self.patch_jump(end_jump, end_pos);
3070            return Ok(result);
3071        }
3072
3073        self.emit(Opcode::JumpIfNot);
3074        self.emit_u16(test_reg);
3075        let else_jump = self.code.len();
3076        self.emit_i32(0);
3077
3078        let cons_opt = self.gen_statement_as_expression(&stmt.consequent, ctx)?;
3079        let cons = self.unwrap_completion(cons_opt);
3080
3081        let result = if self.reserved_slots.contains(&test_reg) && cons != test_reg {
3082            let r = self.alloc_register();
3083            self.emit(Opcode::Move);
3084            self.emit_u16(r);
3085            self.emit_u16(cons);
3086            self.free_register(cons);
3087            r
3088        } else if cons != test_reg {
3089            self.emit(Opcode::Move);
3090            self.emit_u16(test_reg);
3091            self.emit_u16(cons);
3092            self.free_register(cons);
3093            test_reg
3094        } else {
3095            test_reg
3096        };
3097
3098        self.emit(Opcode::Jump);
3099        let end_jump = self.code.len();
3100        self.emit_i32(0);
3101
3102        let else_pos = self.code.len();
3103        self.patch_jump(else_jump, else_pos);
3104
3105        let alt = if let Some(alt_stmt) = &stmt.alternate {
3106            let alt_opt = self.gen_statement_as_expression(alt_stmt, ctx)?;
3107            self.unwrap_completion(alt_opt)
3108        } else {
3109            self.emit_load_undefined()
3110        };
3111        if self.reserved_slots.contains(&test_reg) && alt != result && alt != test_reg {
3112            self.emit(Opcode::Move);
3113            self.emit_u16(result);
3114            self.emit_u16(alt);
3115            self.free_register(alt);
3116        } else if alt != result && alt != test_reg {
3117            self.emit(Opcode::Move);
3118            self.emit_u16(result);
3119            self.emit_u16(alt);
3120            self.free_register(alt);
3121        }
3122
3123        let end_pos = self.code.len();
3124        self.patch_jump(end_jump, end_pos);
3125
3126        Ok(result)
3127    }
3128
3129    fn gen_if_statement_with_target(
3130        &mut self,
3131        stmt: &IfStatement,
3132        ctx: &mut JSContext,
3133        target_reg: u16,
3134    ) -> Result<(), String> {
3135        if self.p2_fold_profile.allow_if_bool() {
3136            if let Expression::Literal(Literal::Boolean(test_bool)) = &stmt.test {
3137                if *test_bool {
3138                    self.emit(Opcode::LoadUndefined);
3139                    self.emit_u16(target_reg);
3140                    self.gen_statement_with_target(&stmt.consequent, ctx, target_reg)?;
3141                } else if let Some(alt) = &stmt.alternate {
3142                    self.emit(Opcode::LoadUndefined);
3143                    self.emit_u16(target_reg);
3144                    self.gen_statement_with_target(alt, ctx, target_reg)?;
3145                }
3146                return Ok(());
3147            }
3148        }
3149        if self.p2_fold_profile.allow_aggressive_truthy() {
3150            if let Expression::Literal(lit) = &stmt.test {
3151                if let Some(truthy) = Self::literal_truthiness(lit) {
3152                    if truthy {
3153                        self.emit(Opcode::LoadUndefined);
3154                        self.emit_u16(target_reg);
3155                        self.gen_statement_with_target(&stmt.consequent, ctx, target_reg)?;
3156                    } else if let Some(alt) = &stmt.alternate {
3157                        self.emit(Opcode::LoadUndefined);
3158                        self.emit_u16(target_reg);
3159                        self.gen_statement_with_target(alt, ctx, target_reg)?;
3160                    }
3161                    return Ok(());
3162                }
3163            }
3164        }
3165        let test_reg = self.gen_expression(&stmt.test, ctx)?;
3166        self.emit(Opcode::JumpIfNot);
3167        self.emit_u16(test_reg);
3168        self.free_register(test_reg);
3169        let else_jump = self.code.len();
3170        self.emit_i32(0);
3171
3172        self.emit(Opcode::LoadUndefined);
3173        self.emit_u16(target_reg);
3174        self.gen_statement_with_target(&stmt.consequent, ctx, target_reg)?;
3175
3176        let has_else = stmt.alternate.is_some();
3177        let end_jump = if has_else {
3178            self.emit(Opcode::Jump);
3179            let pos = self.code.len();
3180            self.emit_i32(0);
3181            Some(pos)
3182        } else {
3183            None
3184        };
3185
3186        let else_pos = self.code.len();
3187        self.patch_jump(else_jump, else_pos);
3188
3189        if let Some(alt) = &stmt.alternate {
3190            self.emit(Opcode::LoadUndefined);
3191            self.emit_u16(target_reg);
3192            self.gen_statement_with_target(alt, ctx, target_reg)?;
3193        }
3194
3195        if let Some(end_jump_pos) = end_jump {
3196            let end_pos = self.code.len();
3197            self.patch_jump(end_jump_pos, end_pos);
3198        }
3199        Ok(())
3200    }
3201
3202    fn gen_try_statement_as_expression(
3203        &mut self,
3204        stmt: &TryStatement,
3205        ctx: &mut JSContext,
3206    ) -> Result<u16, String> {
3207        let reg = self.alloc_register();
3208        self.emit(Opcode::LoadUndefined);
3209        self.emit_u16(reg);
3210        self.gen_try_statement(stmt, ctx, Some(reg))?;
3211        Ok(reg)
3212    }
3213
3214    fn gen_switch_statement_as_expression(
3215        &mut self,
3216        stmt: &SwitchStatement,
3217        ctx: &mut JSContext,
3218    ) -> Result<u16, String> {
3219        let reg = self.alloc_register();
3220        self.emit(Opcode::LoadUndefined);
3221        self.emit_u16(reg);
3222        self.gen_switch_statement_with_result(stmt, ctx, Some(reg))?;
3223        Ok(reg)
3224    }
3225
3226    fn gen_if_statement(&mut self, stmt: &IfStatement, ctx: &mut JSContext) -> Result<(), String> {
3227        if self.p2_fold_profile.allow_if_bool() {
3228            if let Expression::Literal(Literal::Boolean(test_bool)) = &stmt.test {
3229                if *test_bool {
3230                    self.gen_statement(&stmt.consequent, ctx)?;
3231                } else if let Some(alt) = &stmt.alternate {
3232                    self.gen_statement(alt, ctx)?;
3233                }
3234                return Ok(());
3235            }
3236        }
3237
3238        if self.p2_fold_profile.allow_aggressive_truthy() {
3239            if let Expression::Literal(lit) = &stmt.test {
3240                if let Some(truthy) = Self::literal_truthiness(lit) {
3241                    if truthy {
3242                        self.gen_statement(&stmt.consequent, ctx)?;
3243                    } else if let Some(alt) = &stmt.alternate {
3244                        self.gen_statement(alt, ctx)?;
3245                    }
3246                    return Ok(());
3247                }
3248            }
3249        }
3250
3251        let test_reg = self.gen_expression(&stmt.test, ctx)?;
3252        self.emit(Opcode::JumpIfNot);
3253        self.emit_u16(test_reg);
3254        self.free_register(test_reg);
3255        let else_jump = self.code.len();
3256        self.emit_i32(0);
3257
3258        self.gen_statement(&stmt.consequent, ctx)?;
3259
3260        let has_else = stmt.alternate.is_some();
3261        let end_jump = if has_else {
3262            self.emit(Opcode::Jump);
3263            let pos = self.code.len();
3264            self.emit_i32(0);
3265            Some(pos)
3266        } else {
3267            None
3268        };
3269
3270        let else_pos = self.code.len();
3271        self.patch_jump(else_jump, else_pos);
3272
3273        if let Some(alt) = &stmt.alternate {
3274            self.gen_statement(alt, ctx)?;
3275        }
3276
3277        if let Some(end_jump_pos) = end_jump {
3278            let end_pos = self.code.len();
3279            self.patch_jump(end_jump_pos, end_pos);
3280        }
3281
3282        Ok(())
3283    }
3284
3285    fn gen_while_statement(
3286        &mut self,
3287        stmt: &WhileStatement,
3288        ctx: &mut JSContext,
3289    ) -> Result<(), String> {
3290        self.gen_while_statement_with_result(stmt, ctx, None)
3291    }
3292
3293    fn gen_while_statement_with_result(
3294        &mut self,
3295        stmt: &WhileStatement,
3296        ctx: &mut JSContext,
3297        result_reg: Option<u16>,
3298    ) -> Result<(), String> {
3299        let loop_start = self.code.len();
3300
3301        let break_jump = if self.opt_fused_cmp_jump {
3302            if let Expression::BinaryExpression(bin) = &stmt.test {
3303                if let Some(fused) = Self::fused_op_from_binary_cmp(&bin.op, false) {
3304                    let left = self.gen_expression(&bin.left, ctx)?;
3305                    let right = self.gen_expression(&bin.right, ctx)?;
3306                    self.emit(fused);
3307                    self.emit_u16(left);
3308                    self.emit_u16(right);
3309                    self.free_register(left);
3310                    self.free_register(right);
3311                    let p = self.code.len();
3312                    self.emit_i32(0);
3313                    p
3314                } else {
3315                    let test_reg = self.gen_expression(&stmt.test, ctx)?;
3316                    self.emit(Opcode::JumpIfNot);
3317                    self.emit_u16(test_reg);
3318                    self.free_register(test_reg);
3319                    let p = self.code.len();
3320                    self.emit_i32(0);
3321                    p
3322                }
3323            } else {
3324                let test_reg = self.gen_expression(&stmt.test, ctx)?;
3325                self.emit(Opcode::JumpIfNot);
3326                self.emit_u16(test_reg);
3327                self.free_register(test_reg);
3328                let p = self.code.len();
3329                self.emit_i32(0);
3330                p
3331            }
3332        } else {
3333            let test_reg = self.gen_expression(&stmt.test, ctx)?;
3334            self.emit(Opcode::JumpIfNot);
3335            self.emit_u16(test_reg);
3336            self.free_register(test_reg);
3337            let p = self.code.len();
3338            self.emit_i32(0);
3339            p
3340        };
3341
3342        self.breakable_frames.push(BreakableFrame {
3343            label: None,
3344            break_patches: vec![break_jump],
3345            continue_patches: Vec::new(),
3346            continue_target: Some(loop_start),
3347        });
3348
3349        if let Some(v_reg) = result_reg {
3350            self.gen_statement_with_target(&stmt.body, ctx, v_reg)?;
3351        } else {
3352            self.gen_statement(&stmt.body, ctx)?;
3353        }
3354
3355        let frame = self.breakable_frames.pop().unwrap();
3356
3357        self.emit_backward_jump(loop_start);
3358
3359        let end_pos = self.code.len();
3360        for patch in frame.break_patches {
3361            self.patch_jump(patch, end_pos);
3362        }
3363        for patch in frame.continue_patches {
3364            self.patch_jump(patch, frame.continue_target.unwrap());
3365        }
3366
3367        Ok(())
3368    }
3369
3370    fn gen_for_statement(
3371        &mut self,
3372        stmt: &ForStatement,
3373        ctx: &mut JSContext,
3374    ) -> Result<(), String> {
3375        self.gen_for_statement_with_result(stmt, ctx, None)
3376    }
3377
3378    fn gen_for_statement_with_result(
3379        &mut self,
3380        stmt: &ForStatement,
3381        ctx: &mut JSContext,
3382        result_reg: Option<u16>,
3383    ) -> Result<(), String> {
3384        let needs_block_scope = if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
3385            decl.kind != VariableKind::Var
3386        } else {
3387            false
3388        };
3389
3390        if needs_block_scope {
3391            self.push_scope();
3392            if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
3393                for d in &decl.declarations {
3394                    self.pre_scan_binding(&d.id, decl.kind);
3395                }
3396                for d in &decl.declarations {
3397                    self.emit_tdz_for_binding(&d.id)?;
3398                }
3399            }
3400        }
3401
3402        if let Some(init) = &stmt.init {
3403            match init {
3404                ForInit::VariableDeclaration(decl) => {
3405                    self.gen_variable_declaration(decl, ctx)?;
3406                }
3407                ForInit::Expression(expr) => {
3408                    let r = self.gen_expression(expr, ctx)?;
3409                    self.free_register(r);
3410                }
3411            }
3412        }
3413
3414        // Emit ResetPerIterVar for per-iteration bindings (let variables)
3415        // BEFORE the first test, as per spec CreatePerIterationEnvironment step
3416        if needs_block_scope {
3417            if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
3418                for d in &decl.declarations {
3419                    if let BindingPattern::Identifier(name) = &d.id {
3420                        if let Some(slot) = self.lookup_var_slot(name) {
3421                            self.emit(Opcode::ResetPerIterVar);
3422                            self.emit_u16(slot);
3423                        }
3424                    }
3425                }
3426            }
3427        }
3428
3429        let loop_start = self.code.len();
3430
3431        let break_jump = if let Some(test) = &stmt.test {
3432            if self.opt_fused_cmp_jump {
3433                if let Expression::BinaryExpression(bin) = test {
3434                    if let Some(fused) = Self::fused_op_from_binary_cmp(&bin.op, false) {
3435                        let left = self.gen_expression(&bin.left, ctx)?;
3436                        let right = self.gen_expression(&bin.right, ctx)?;
3437                        self.emit(fused);
3438                        self.emit_u16(left);
3439                        self.emit_u16(right);
3440                        self.free_register(left);
3441                        self.free_register(right);
3442                        let bj = self.code.len();
3443                        self.emit_i32(0);
3444                        bj
3445                    } else {
3446                        let test_reg = self.gen_expression(test, ctx)?;
3447                        self.emit(Opcode::JumpIfNot);
3448                        self.emit_u16(test_reg);
3449                        self.free_register(test_reg);
3450                        let bj = self.code.len();
3451                        self.emit_i32(0);
3452                        bj
3453                    }
3454                } else {
3455                    let test_reg = self.gen_expression(test, ctx)?;
3456                    self.emit(Opcode::JumpIfNot);
3457                    self.emit_u16(test_reg);
3458                    self.free_register(test_reg);
3459                    let bj = self.code.len();
3460                    self.emit_i32(0);
3461                    bj
3462                }
3463            } else {
3464                let test_reg = self.gen_expression(test, ctx)?;
3465                self.emit(Opcode::JumpIfNot);
3466                self.emit_u16(test_reg);
3467                self.free_register(test_reg);
3468                let bj = self.code.len();
3469                self.emit_i32(0);
3470                bj
3471            }
3472        } else {
3473            0
3474        };
3475
3476        let loop_start_for_continue = loop_start;
3477        self.breakable_frames.push(BreakableFrame {
3478            label: self.pending_label.take(),
3479            break_patches: if stmt.test.is_some() {
3480                vec![break_jump]
3481            } else {
3482                Vec::new()
3483            },
3484            continue_patches: Vec::new(),
3485            continue_target: Some(loop_start_for_continue),
3486        });
3487
3488        if let Some(v_reg) = result_reg {
3489            self.gen_statement_with_target(&stmt.body, ctx, v_reg)?;
3490        } else {
3491            self.gen_statement(&stmt.body, ctx)?;
3492        }
3493
3494        // Emit ResetPerIterVar for per-iteration bindings (let variables)
3495        // This must happen BEFORE the update expression per the spec
3496        if needs_block_scope {
3497            if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
3498                for d in &decl.declarations {
3499                    if let BindingPattern::Identifier(name) = &d.id {
3500                        if let Some(slot) = self.lookup_var_slot(name) {
3501                            self.emit(Opcode::ResetPerIterVar);
3502                            self.emit_u16(slot);
3503                        }
3504                    }
3505                }
3506            }
3507        }
3508
3509        let update_pos = self.code.len();
3510        if let Some(update) = &stmt.update {
3511            let r = self.gen_expression(update, ctx)?;
3512            self.free_register(r);
3513        }
3514        if let Some(frame) = self.breakable_frames.last_mut() {
3515            frame.continue_target = Some(update_pos);
3516        }
3517
3518        let frame = self.breakable_frames.pop().unwrap();
3519
3520        self.emit_backward_jump(loop_start);
3521
3522        let end_pos = self.code.len();
3523        for patch in frame.break_patches {
3524            self.patch_jump(patch, end_pos);
3525        }
3526        for patch in frame.continue_patches {
3527            self.patch_jump(patch, frame.continue_target.unwrap());
3528        }
3529
3530        if needs_block_scope {
3531            self.pop_scope();
3532        }
3533
3534        Ok(())
3535    }
3536
3537    fn gen_for_in_statement(
3538        &mut self,
3539        stmt: &ForInStatement,
3540        ctx: &mut JSContext,
3541    ) -> Result<(), String> {
3542        self.gen_for_in_statement_with_result(stmt, ctx, None)
3543    }
3544
3545    fn gen_for_in_statement_with_result(
3546        &mut self,
3547        stmt: &ForInStatement,
3548        ctx: &mut JSContext,
3549        result_reg: Option<u16>,
3550    ) -> Result<(), String> {
3551        let needs_block_scope = match &stmt.left {
3552            ForInOfLeft::VariableDeclaration(decl) => decl.kind != VariableKind::Var,
3553            _ => false,
3554        };
3555
3556        if needs_block_scope {
3557            self.push_scope();
3558            if let ForInOfLeft::VariableDeclaration(decl) = &stmt.left {
3559                if let Some(first) = decl.declarations.first() {
3560                    self.pre_scan_binding(&first.id, decl.kind);
3561                    self.emit_tdz_for_binding(&first.id)?;
3562                }
3563            }
3564        }
3565
3566        let has_complex_left = match &stmt.left {
3567            ForInOfLeft::VariableDeclaration(decl) => decl
3568                .declarations
3569                .first()
3570                .map_or(false, |d| !matches!(d.id, BindingPattern::Identifier(_))),
3571            ForInOfLeft::AssignmentTarget(at) => !matches!(at, AssignmentTarget::Identifier(_)),
3572        };
3573
3574        let var_slot = if has_complex_left {
3575            let tmp_slot = self.alloc_register();
3576            self.free_register(tmp_slot);
3577            tmp_slot
3578        } else {
3579            match &stmt.left {
3580                ForInOfLeft::VariableDeclaration(decl) => {
3581                    if let Some(first) = decl.declarations.first() {
3582                        if let BindingPattern::Identifier(name) = &first.id {
3583                            if decl.kind == VariableKind::Var {
3584                                self.declare_var(name, VariableKind::Var)
3585                            } else {
3586                                self.lookup_var_slot(name).unwrap_or(u16::MAX)
3587                            }
3588                        } else {
3589                            u16::MAX
3590                        }
3591                    } else {
3592                        u16::MAX
3593                    }
3594                }
3595                ForInOfLeft::AssignmentTarget(AssignmentTarget::Identifier(name)) => {
3596                    if let Some(slot) = self.lookup_var_slot(name) {
3597                        slot
3598                    } else if !self.is_strict {
3599                        self.declare_var(name, VariableKind::Var)
3600                    } else {
3601                        return Err(format!("for-in: undefined variable {}", name));
3602                    }
3603                }
3604                _ => 255,
3605            }
3606        };
3607
3608        let obj_reg = self.gen_expression(&stmt.right, ctx)?;
3609
3610        let keys_reg = self.alloc_register();
3611        self.emit(Opcode::GetPropertyNames);
3612        self.emit_u16(keys_reg);
3613        self.emit_u16(obj_reg);
3614        self.free_register(obj_reg);
3615
3616        let idx_reg = self.alloc_register();
3617        self.emit_int_const(idx_reg, 0);
3618
3619        let loop_start = self.code.len();
3620
3621        let len_key_reg = self.alloc_register();
3622        let len_atom = ctx.common_atoms.length;
3623        let len_const = self.add_constant(JSValue::new_string(len_atom));
3624        self.emit(Opcode::LoadConst);
3625        self.emit_u16(len_key_reg);
3626        self.emit_u32(len_const);
3627        let len_val_reg = self.alloc_register();
3628        self.emit(Opcode::GetProp);
3629        self.emit_u16(len_val_reg);
3630        self.emit_u16(keys_reg);
3631        self.emit_u16(len_key_reg);
3632        self.free_register(len_key_reg);
3633
3634        let cmp_reg = self.alloc_register();
3635        self.emit(Opcode::Lt);
3636        self.emit_u16(cmp_reg);
3637        self.emit_u16(idx_reg);
3638        self.emit_u16(len_val_reg);
3639        self.free_register(len_val_reg);
3640
3641        self.emit(Opcode::JumpIfNot);
3642        self.emit_u16(cmp_reg);
3643        let exit_patch = self.code.len();
3644        self.emit_i32(0);
3645        self.free_register(cmp_reg);
3646
3647        self.breakable_frames.push(BreakableFrame {
3648            label: self.pending_label.take(),
3649            break_patches: Vec::new(),
3650            continue_patches: Vec::new(),
3651            continue_target: Some(0),
3652        });
3653
3654        let key_val_reg = self.alloc_register();
3655        self.emit(Opcode::GetField);
3656        self.emit_u16(key_val_reg);
3657        self.emit_u16(keys_reg);
3658        self.emit_u16(idx_reg);
3659
3660        // Clear sync map for per-iteration let bindings BEFORE assigning.
3661        if needs_block_scope {
3662            if let Some(slot) = self.scope_stack.last().and_then(|scope| {
3663                scope
3664                    .iter()
3665                    .find(|(_, e)| e.kind != VariableKind::Var)
3666                    .map(|(_, e)| e.slot)
3667            }) {
3668                self.emit(Opcode::ResetPerIterVar);
3669                self.emit_u16(slot);
3670            }
3671        }
3672        if has_complex_left {
3673            match &stmt.left {
3674                ForInOfLeft::VariableDeclaration(decl) => {
3675                    if let Some(first) = decl.declarations.first() {
3676                        self.gen_binding_pattern(&first.id, key_val_reg, decl.kind, ctx)?;
3677                    } else {
3678                        return Err("for-in without variable".to_string());
3679                    }
3680                }
3681                ForInOfLeft::AssignmentTarget(at) => {
3682                    self.gen_assignment_target_destructure(at, key_val_reg, ctx)?;
3683                }
3684            };
3685        } else {
3686            let loop_var_name = match &stmt.left {
3687                ForInOfLeft::VariableDeclaration(decl) => decl.declarations.first().and_then(|d| {
3688                    if let BindingPattern::Identifier(n) = &d.id {
3689                        Some(n.clone())
3690                    } else {
3691                        None
3692                    }
3693                }),
3694                ForInOfLeft::AssignmentTarget(AssignmentTarget::Identifier(n)) => Some(n.clone()),
3695                _ => None,
3696            };
3697            if var_slot != key_val_reg {
3698                self.emit(Opcode::Move);
3699                self.emit_u16(var_slot);
3700                self.emit_u16(key_val_reg);
3701            }
3702
3703            // Mark the variable as initialized so closures in the body don't
3704            // incorrectly emit CheckTdz
3705            if let Some(ref name) = loop_var_name {
3706                for scope in self.scope_stack.iter_mut().rev() {
3707                    if let Some(entry) = scope.get_mut(name) {
3708                        entry.initialized = true;
3709                        break;
3710                    }
3711                }
3712            }
3713
3714            if let Some(ref name) = loop_var_name {
3715                if self.is_global_var(name) {
3716                    let atom = ctx.intern(name);
3717                    let idx = self.add_constant(JSValue::new_string(atom));
3718                    let src = if var_slot != key_val_reg {
3719                        var_slot
3720                    } else {
3721                        key_val_reg
3722                    };
3723                    self.emit(Opcode::SetGlobal);
3724                    self.emit_u32(idx);
3725                    self.emit_u16(src);
3726                }
3727            }
3728        }
3729        self.free_register(key_val_reg);
3730
3731        if let Some(v_reg) = result_reg {
3732            self.gen_statement_with_target(&stmt.body, ctx, v_reg)?;
3733        } else {
3734            self.gen_statement(&stmt.body, ctx)?;
3735        }
3736
3737        let continue_target = self.code.len();
3738        if let Some(frame) = self.breakable_frames.last_mut() {
3739            frame.continue_target = Some(continue_target);
3740        }
3741
3742        self.emit(Opcode::IncLocal);
3743        self.emit_u16(idx_reg);
3744
3745        self.emit_backward_jump(loop_start);
3746
3747        let frame = self.breakable_frames.pop().unwrap();
3748        let end_pos = self.code.len();
3749
3750        self.patch_jump(exit_patch, end_pos);
3751
3752        for patch in frame.break_patches {
3753            self.patch_jump(patch, end_pos);
3754        }
3755
3756        for patch in frame.continue_patches {
3757            self.patch_jump(patch, continue_target);
3758        }
3759
3760        self.free_register(keys_reg);
3761        self.free_register(idx_reg);
3762
3763        if needs_block_scope {
3764            self.pop_scope();
3765        }
3766
3767        Ok(())
3768    }
3769
3770    fn gen_for_of_statement(
3771        &mut self,
3772        stmt: &ForOfStatement,
3773        ctx: &mut JSContext,
3774    ) -> Result<(), String> {
3775        self.gen_for_of_statement_with_result(stmt, ctx, None)
3776    }
3777
3778    fn gen_for_of_statement_with_result(
3779        &mut self,
3780        stmt: &ForOfStatement,
3781        ctx: &mut JSContext,
3782        result_reg: Option<u16>,
3783    ) -> Result<(), String> {
3784        let needs_block_scope = match &stmt.left {
3785            ForInOfLeft::VariableDeclaration(decl) => decl.kind != VariableKind::Var,
3786            _ => false,
3787        };
3788
3789        if needs_block_scope {
3790            self.push_scope();
3791            if let ForInOfLeft::VariableDeclaration(decl) = &stmt.left {
3792                if let Some(first) = decl.declarations.first() {
3793                    self.pre_scan_binding(&first.id, decl.kind);
3794                    self.emit_tdz_for_binding(&first.id)?;
3795                }
3796            }
3797        }
3798
3799        let iterable_reg = self.gen_expression(&stmt.right, ctx)?;
3800        let iter_reg = self.alloc_register();
3801        self.emit(Opcode::GetIterator);
3802        self.emit_u16(iter_reg);
3803        self.emit_u16(iterable_reg);
3804        self.free_register(iterable_reg);
3805
3806        let loop_start = self.code.len();
3807
3808        let val_reg = self.alloc_register();
3809        let done_reg = self.alloc_register();
3810        self.emit(Opcode::IteratorNext);
3811        self.emit_u16(val_reg);
3812        self.emit_u16(done_reg);
3813        self.emit_u16(iter_reg);
3814
3815        self.emit(Opcode::JumpIf);
3816        self.emit_u16(done_reg);
3817        let exit_patch = self.code.len();
3818        self.emit_i32(0);
3819        self.free_register(done_reg);
3820
3821        self.breakable_frames.push(BreakableFrame {
3822            label: self.pending_label.take(),
3823            break_patches: Vec::new(),
3824            continue_patches: Vec::new(),
3825            continue_target: Some(0),
3826        });
3827
3828        match &stmt.left {
3829            ForInOfLeft::VariableDeclaration(decl) => {
3830                if let Some(first) = decl.declarations.first() {
3831                    self.gen_binding_pattern(&first.id, val_reg, decl.kind, ctx)?;
3832                } else {
3833                    return Err("for-of without variable".to_string());
3834                }
3835            }
3836            ForInOfLeft::AssignmentTarget(target) => {
3837                self.gen_assignment_target_destructure(target, val_reg, ctx)?;
3838            }
3839        }
3840        self.free_register(val_reg);
3841
3842        if let Some(v_reg) = result_reg {
3843            self.gen_statement_with_target(&stmt.body, ctx, v_reg)?;
3844        } else {
3845            self.gen_statement(&stmt.body, ctx)?;
3846        }
3847
3848        let continue_target = self.code.len();
3849        if let Some(frame) = self.breakable_frames.last_mut() {
3850            frame.continue_target = Some(continue_target);
3851        }
3852
3853        self.emit_backward_jump(loop_start);
3854
3855        let frame = self.breakable_frames.pop().unwrap();
3856        let end_pos = self.code.len();
3857
3858        self.patch_jump(exit_patch, end_pos);
3859
3860        for patch in frame.break_patches {
3861            self.patch_jump(patch, end_pos);
3862        }
3863        for patch in frame.continue_patches {
3864            self.patch_jump(patch, continue_target);
3865        }
3866
3867        self.free_register(iter_reg);
3868
3869        if needs_block_scope {
3870            self.pop_scope();
3871        }
3872
3873        Ok(())
3874    }
3875
3876    fn gen_do_while_statement(
3877        &mut self,
3878        stmt: &DoWhileStatement,
3879        ctx: &mut JSContext,
3880    ) -> Result<(), String> {
3881        self.gen_do_while_statement_with_result(stmt, ctx, None)
3882    }
3883
3884    fn gen_do_while_statement_with_result(
3885        &mut self,
3886        stmt: &DoWhileStatement,
3887        ctx: &mut JSContext,
3888        result_reg: Option<u16>,
3889    ) -> Result<(), String> {
3890        let loop_start = self.code.len();
3891
3892        self.breakable_frames.push(BreakableFrame {
3893            label: self.pending_label.take(),
3894            break_patches: Vec::new(),
3895            continue_patches: Vec::new(),
3896            continue_target: Some(0),
3897        });
3898
3899        if let Some(v_reg) = result_reg {
3900            self.gen_statement_with_target(&stmt.body, ctx, v_reg)?;
3901        } else {
3902            self.gen_statement(&stmt.body, ctx)?;
3903        }
3904
3905        let continue_target = self.code.len();
3906        if let Some(frame) = self.breakable_frames.last_mut() {
3907            frame.continue_target = Some(continue_target);
3908        }
3909
3910        let test_reg = self.gen_expression(&stmt.test, ctx)?;
3911        self.emit_conditional_backward_jump(test_reg, loop_start, true);
3912        self.free_register(test_reg);
3913
3914        let frame = self.breakable_frames.pop().unwrap();
3915
3916        let end_pos = self.code.len();
3917        for patch in frame.break_patches {
3918            self.patch_jump(patch, end_pos);
3919        }
3920        for patch in frame.continue_patches {
3921            self.patch_jump(patch, frame.continue_target.unwrap());
3922        }
3923
3924        Ok(())
3925    }
3926
3927    fn gen_switch_statement(
3928        &mut self,
3929        stmt: &SwitchStatement,
3930        ctx: &mut JSContext,
3931    ) -> Result<(), String> {
3932        self.gen_switch_statement_with_result(stmt, ctx, None)
3933    }
3934
3935    fn gen_switch_statement_with_result(
3936        &mut self,
3937        stmt: &SwitchStatement,
3938        ctx: &mut JSContext,
3939        result_reg: Option<u16>,
3940    ) -> Result<(), String> {
3941        // Evaluate the switch expression in the enclosing scope (BEFORE the
3942        // CaseBlock lexical environment is created).
3943        let disc = self.gen_expression(&stmt.discriminant, ctx)?;
3944
3945        // Create the lexical scope for the entire CaseBlock. Per spec, this
3946        // is done BEFORE case selector evaluation so that `let`/`const`
3947        // declarations inside cases are visible (as TDZ) during selector
3948        // evaluation.
3949        self.push_scope();
3950        for case in &stmt.cases {
3951            self.pre_scan_let_const(&case.consequent)?;
3952            self.emit_tdz_for_block(&case.consequent)?;
3953        }
3954
3955        let mut test_jumps: Vec<(usize, usize)> = Vec::new();
3956        for (case_idx, case) in stmt.cases.iter().enumerate() {
3957            if let Some(test_expr) = &case.test {
3958                let test_reg = self.gen_expression(test_expr, ctx)?;
3959                let eq_reg = self.alloc_register();
3960                self.emit(Opcode::StrictEq);
3961                self.emit_u16(eq_reg);
3962                self.emit_u16(disc);
3963                self.emit_u16(test_reg);
3964                self.free_register(test_reg);
3965                self.emit(Opcode::JumpIf);
3966                self.emit_u16(eq_reg);
3967                test_jumps.push((self.code.len(), case_idx));
3968                self.emit_i32(0);
3969                self.free_register(eq_reg);
3970            }
3971        }
3972
3973        self.emit(Opcode::Jump);
3974        let default_jump_placeholder = self.code.len();
3975        self.emit_i32(0);
3976
3977        self.breakable_frames.push(BreakableFrame {
3978            label: self.pending_label.take(),
3979            break_patches: Vec::new(),
3980            continue_patches: Vec::new(),
3981            continue_target: None,
3982        });
3983
3984        let mut body_starts = vec![0usize; stmt.cases.len()];
3985        for (case_idx, case) in stmt.cases.iter().enumerate() {
3986            body_starts[case_idx] = self.code.len();
3987            if let Some(v_reg) = result_reg {
3988                for s in &case.consequent {
3989                    self.gen_statement_with_target(s, ctx, v_reg)?;
3990                }
3991            } else {
3992                for s in &case.consequent {
3993                    self.gen_statement(s, ctx)?;
3994                }
3995            }
3996        }
3997        self.pop_scope();
3998
3999        let frame = self.breakable_frames.pop().unwrap();
4000        let end_pos = self.code.len();
4001
4002        for patch in frame.break_patches {
4003            self.patch_jump(patch, end_pos);
4004        }
4005
4006        let default_case_idx = stmt.cases.iter().position(|c| c.test.is_none());
4007        let default_target = default_case_idx
4008            .map(|idx| body_starts[idx])
4009            .unwrap_or(end_pos);
4010        self.patch_jump(default_jump_placeholder, default_target);
4011
4012        for (placeholder, case_idx) in test_jumps {
4013            self.patch_jump(placeholder, body_starts[case_idx]);
4014        }
4015
4016        self.free_register(disc);
4017        Ok(())
4018    }
4019
4020    fn gen_throw_statement(
4021        &mut self,
4022        stmt: &ThrowStatement,
4023        ctx: &mut JSContext,
4024    ) -> Result<(), String> {
4025        let reg = self.gen_expression(&stmt.argument, ctx)?;
4026        self.emit(Opcode::Throw);
4027        self.emit_u16(reg);
4028        self.free_register(reg);
4029        Ok(())
4030    }
4031
4032    fn gen_try_statement(
4033        &mut self,
4034        stmt: &TryStatement,
4035        ctx: &mut JSContext,
4036        result_reg: Option<u16>,
4037    ) -> Result<(), String> {
4038        self.emit(Opcode::Try);
4039        let catch_pc_pos = self.code.len();
4040        self.emit_i32(0);
4041        let finally_pc_pos = self.code.len();
4042        self.emit_i32(0);
4043
4044        self.push_scope();
4045        self.pre_scan_let_const(&stmt.block.body)?;
4046        self.emit_tdz_for_block(&stmt.block.body)?;
4047        if let Some(v_reg) = result_reg {
4048            for s in &stmt.block.body {
4049                self.gen_statement_with_target(s, ctx, v_reg)?;
4050            }
4051        } else {
4052            for s in &stmt.block.body {
4053                self.gen_statement(s, ctx)?;
4054            }
4055        }
4056        self.pop_scope();
4057
4058        self.emit(Opcode::Catch);
4059
4060        self.emit(Opcode::Jump);
4061        let end_jump_pos = self.code.len();
4062        self.emit_i32(0);
4063
4064        let has_catch = stmt.handler.is_some();
4065        let mut after_catch_jump_pos: Option<usize> = None;
4066        let has_finally = stmt.finalizer.is_some();
4067        let mut catch_inner_finally_placeholder: Option<usize> = None;
4068
4069        if let Some(handler) = &stmt.handler {
4070            let catch_start = self.code.len();
4071
4072            let catch_bytes = (catch_start as i32).to_le_bytes();
4073            self.code[catch_pc_pos..catch_pc_pos + 4].copy_from_slice(&catch_bytes);
4074
4075            // Wrap catch body in Try-Finally so exceptions inside catch run finally
4076            if has_finally {
4077                self.emit(Opcode::Try);
4078                let _catch_inner_catch_pc = self.code.len();
4079                self.emit_i32(0); // placeholder for catch_pc (set to finally_start)
4080                catch_inner_finally_placeholder = Some(self.code.len());
4081                self.emit_i32(0); // placeholder for finally_pc (set to finally_start)
4082            }
4083
4084            self.push_scope();
4085            if let Some(param) = &handler.param {
4086                if self.is_strict {
4087                    if let BindingPattern::Identifier(name) = param {
4088                        if name == "eval" || name == "arguments" {
4089                            return Err(format!(
4090                                "SyntaxError: Catch variable may not be '{}' in strict mode",
4091                                name
4092                            ));
4093                        }
4094                    }
4095                }
4096                match param {
4097                    BindingPattern::Identifier(name) => {
4098                        self.pre_scan_binding(param, VariableKind::Let);
4099                        self.emit_tdz_for_binding(param)?;
4100                        let slot = self.declare_var(name, VariableKind::Let);
4101                        if slot != 0 {
4102                            self.emit(Opcode::Move);
4103                            self.emit_u16(slot);
4104                            self.emit_u16(0);
4105                        }
4106
4107                        if self.is_global_var(name) {
4108                            let atom = ctx.intern(name);
4109                            let idx = self.add_constant(JSValue::new_string(atom));
4110                            self.emit(Opcode::SetGlobal);
4111                            self.emit_u32(idx);
4112                            self.emit_u16(0);
4113                        }
4114                    }
4115                    _ => self.gen_binding_pattern(param, 0, VariableKind::Let, ctx)?,
4116                }
4117            }
4118            self.pre_scan_let_const(&handler.body.body)?;
4119            self.emit_tdz_for_block(&handler.body.body)?;
4120            if let Some(v_reg) = result_reg {
4121                for s in &handler.body.body {
4122                    self.gen_statement_with_target(s, ctx, v_reg)?;
4123                }
4124            } else {
4125                for s in &handler.body.body {
4126                    self.gen_statement(s, ctx)?;
4127                }
4128            }
4129            self.pop_scope();
4130
4131            if has_finally {
4132                self.emit(Opcode::Catch);
4133            }
4134
4135            self.emit(Opcode::Jump);
4136            after_catch_jump_pos = Some(self.code.len());
4137            self.emit_i32(0);
4138        }
4139
4140        let finally_start = if let Some(finalizer) = &stmt.finalizer {
4141            let fs = self.code.len();
4142
4143            // Patch the catch-inner handler's placeholders
4144            if let Some(fp_placeholder) = catch_inner_finally_placeholder {
4145                let finally_bytes = (fs as i32).to_le_bytes();
4146                self.code[fp_placeholder - 4..fp_placeholder].copy_from_slice(&finally_bytes);
4147                self.code[fp_placeholder..fp_placeholder + 4].copy_from_slice(&finally_bytes);
4148            }
4149
4150            let finally_bytes = (fs as i32).to_le_bytes();
4151            self.code[finally_pc_pos..finally_pc_pos + 4].copy_from_slice(&finally_bytes);
4152
4153            if !has_catch {
4154                self.code[catch_pc_pos..catch_pc_pos + 4].copy_from_slice(&finally_bytes);
4155            }
4156
4157            self.patch_jump(end_jump_pos, fs);
4158
4159            self.emit(Opcode::Finally);
4160
4161            // Save the prior completion value before running finally body.
4162            // Per spec: if finally completes normally, the prior completion
4163            // (from try or catch) wins — not the finally body's value.
4164            let saved_prior = result_reg.map(|_| self.alloc_register());
4165            if let Some(sr) = saved_prior {
4166                self.emit(Opcode::Move);
4167                self.emit_u16(sr);
4168                self.emit_u16(result_reg.unwrap());
4169            }
4170
4171            // Set result to undefined before the finally body so that
4172            // abrupt completions (break/continue/return/throw) inside
4173            // the finally result in an empty completion value.
4174            if let Some(v_reg) = result_reg {
4175                self.emit(Opcode::LoadUndefined);
4176                self.emit_u16(v_reg);
4177            }
4178
4179            self.push_scope();
4180            self.pre_scan_let_const(&finalizer.body)?;
4181            self.emit_tdz_for_block(&finalizer.body)?;
4182            if let Some(v_reg) = result_reg {
4183                for s in &finalizer.body {
4184                    self.gen_statement_with_target(s, ctx, v_reg)?;
4185                }
4186            } else {
4187                for s in &finalizer.body {
4188                    self.gen_statement(s, ctx)?;
4189                }
4190            }
4191            self.pop_scope();
4192            self.emit(Opcode::EndFinally);
4193
4194            // Restore the prior completion — any non-abrupt writes to v_reg
4195            // by the finally body are discarded per spec.
4196            if let Some(sr) = saved_prior {
4197                self.emit(Opcode::Move);
4198                self.emit_u16(result_reg.unwrap());
4199                self.emit_u16(sr);
4200                self.free_register(sr);
4201            }
4202
4203            fs
4204        } else {
4205            0
4206        };
4207
4208        let end_pos = self.code.len();
4209
4210        if !has_finally {
4211            self.patch_jump(end_jump_pos, end_pos);
4212        }
4213
4214        if let Some(pos) = after_catch_jump_pos {
4215            let catch_jump_target = if has_finally { finally_start } else { end_pos };
4216            self.patch_jump(pos, catch_jump_target);
4217        }
4218
4219        Ok(())
4220    }
4221
4222    fn gen_function_like_with_name(
4223        &mut self,
4224        expr: &Expression,
4225        name: &str,
4226        ctx: &mut JSContext,
4227    ) -> Result<u16, String> {
4228        match expr {
4229            Expression::FunctionExpression(func) => self.gen_function_expression(
4230                &func.params,
4231                &func.body,
4232                Some(name),
4233                ctx,
4234                func.generator,
4235                func.is_async,
4236                false,
4237            ),
4238            Expression::ArrowFunction(arrow) => {
4239                let body = match &arrow.body {
4240                    ArrowBody::Expression(expr) => BlockStatement {
4241                        body: vec![ASTNode::ReturnStatement(ReturnStatement {
4242                            argument: Some((**expr).clone()),
4243                        })],
4244                        lines: vec![],
4245                    },
4246                    ArrowBody::Block(block) => block.clone(),
4247                };
4248                self.gen_function_expression(
4249                    &arrow.params,
4250                    &body,
4251                    Some(name),
4252                    ctx,
4253                    false,
4254                    arrow.is_async,
4255                    true,
4256                )
4257            }
4258            Expression::ClassExpression(ce) => {
4259                let class_slot = self.alloc_register();
4260                self.gen_class_body(class_slot, name, &ce.super_class, &ce.body, ctx)?;
4261                Ok(class_slot)
4262            }
4263            _ => unreachable!(),
4264        }
4265    }
4266
4267    fn gen_expression_with_name(
4268        &mut self,
4269        expr: &Expression,
4270        binding: &BindingPattern,
4271        ctx: &mut JSContext,
4272    ) -> Result<u16, String> {
4273        let needs_name = match binding {
4274            BindingPattern::Identifier(binding_name) => match expr {
4275                Expression::FunctionExpression(func) if func.name.is_none() => {
4276                    Some(binding_name.as_str())
4277                }
4278                Expression::ArrowFunction(_) => Some(binding_name.as_str()),
4279                Expression::ClassExpression(ce) if ce.name.is_none() => Some(binding_name.as_str()),
4280                _ => None,
4281            },
4282            _ => None,
4283        };
4284        if let Some(name) = needs_name {
4285            self.gen_function_like_with_name(expr, name, ctx)
4286        } else {
4287            self.gen_expression(expr, ctx)
4288        }
4289    }
4290
4291    fn gen_expression_with_assign_name(
4292        &mut self,
4293        expr: &Expression,
4294        target_name: &str,
4295        ctx: &mut JSContext,
4296    ) -> Result<u16, String> {
4297        let needs_name = match expr {
4298            Expression::FunctionExpression(func) if func.name.is_none() => Some(target_name),
4299            Expression::ArrowFunction(_) => Some(target_name),
4300            Expression::ClassExpression(ce) if ce.name.is_none() => Some(target_name),
4301            _ => None,
4302        };
4303        if let Some(name) = needs_name {
4304            self.gen_function_like_with_name(expr, name, ctx)
4305        } else {
4306            self.gen_expression(expr, ctx)
4307        }
4308    }
4309
4310    fn gen_expression(&mut self, expr: &Expression, ctx: &mut JSContext) -> Result<u16, String> {
4311        match expr {
4312            Expression::Identifier(id) => {
4313                if id.name == "arguments"
4314                    && !self.is_script_level
4315                    && !self.is_arrow
4316                    && self.lookup_var("arguments").is_none()
4317                {
4318                    self.function_uses_arguments = true;
4319                    let r = self.alloc_register();
4320                    self.emit(Opcode::GetArguments);
4321                    self.emit_u16(r);
4322                    return Ok(r);
4323                }
4324                match self.resolve_var(&id.name, ctx) {
4325                    Some(VarLocation::Local(slot)) => {
4326                        if self.is_global_var(&id.name) {
4327                            let atom = ctx.intern(&id.name);
4328                            let idx = self.add_constant(JSValue::new_string(atom));
4329                            let r = self.alloc_register();
4330                            self.emit(Opcode::GetGlobal);
4331                            self.emit_u16(r);
4332                            self.emit_u32(idx);
4333                            return Ok(r);
4334                        }
4335
4336                        if let Some(entry) = self.lookup_var(&id.name) {
4337                            if entry.kind != VariableKind::Var && !entry.initialized {
4338                                self.emit(Opcode::CheckTdz);
4339                                self.emit_u16(slot);
4340                            }
4341                        }
4342                        Ok(slot)
4343                    }
4344                    Some(VarLocation::Upvalue(slot)) => {
4345                        let r = self.alloc_register();
4346                        self.emit(Opcode::GetUpvalue);
4347                        self.emit_u16(r);
4348                        self.emit_u16(slot);
4349
4350                        // Check TDZ for upvalues: check parent_vars since the variable
4351                        // is not in the nested function's own scope
4352                        let mut needs_tdz = false;
4353                        if let Some(entry) = self.lookup_var(&id.name) {
4354                            needs_tdz = entry.kind != VariableKind::Var && !entry.initialized;
4355                        } else if let Some(parent_var) = self.parent_vars.get(&id.name) {
4356                            // Only check TDZ for direct scope variables (not inherited upvalues).
4357                            // Inherited upvalues may come from pre-declared let bindings
4358                            // that are initialized after the closure is created.
4359                            if !parent_var.is_inherited {
4360                                // Check if this variable is in an explicitly pushed scope
4361                                // (like for-in/for-of heads) vs the root function scope.
4362                                // Variables in the root scope that are pre-declared by
4363                                // pre_scan_let_const should not trigger TDZ for closures
4364                                // created before the let statement.
4365                                needs_tdz =
4366                                    parent_var.kind != VariableKind::Var && !parent_var.initialized;
4367                            }
4368                        }
4369                        if needs_tdz {
4370                            self.emit(Opcode::CheckTdz);
4371                            self.emit_u16(r);
4372                        }
4373                        Ok(r)
4374                    }
4375                    None => {
4376                        let atom = ctx.intern(&id.name);
4377                        let idx = self.add_constant(JSValue::new_string(atom));
4378                        let r = self.alloc_register();
4379                        self.emit(Opcode::GetGlobal);
4380                        self.emit_u16(r);
4381                        self.emit_u32(idx);
4382                        // Emit CheckRef for closed lexical variables, unless
4383                        // suppress_ref_error is set (typeof operator).
4384                        let is_closed =
4385                            self.parent_vars.get(&id.name).map_or(false, |pv| pv.closed);
4386                        if (is_closed && !self.suppress_ref_error)
4387                            || (!self.suppress_ref_error && self.is_strict)
4388                        {
4389                            self.emit(Opcode::CheckRef);
4390                            self.emit_u16(r);
4391                            self.emit_u32(idx);
4392                        }
4393                        Ok(r)
4394                    }
4395                }
4396            }
4397            Expression::Literal(lit) => {
4398                let r = self.alloc_register();
4399                match lit {
4400                    Literal::Number(n) => {
4401                        if *n >= i32::MIN as f64 && *n <= i32::MAX as f64 && n.fract() == 0.0 {
4402                            let val = *n as i32;
4403                            if val >= i8::MIN as i32 && val <= i8::MAX as i32 {
4404                                self.emit(Opcode::LoadInt8);
4405                                self.emit_u16(r);
4406                                self.emit_u8(val as u8);
4407                            } else {
4408                                self.emit_int_const(r, val);
4409                            }
4410                        } else {
4411                            let idx = self.add_constant(JSValue::new_float(*n));
4412                            self.emit(Opcode::LoadConst);
4413                            self.emit_u16(r);
4414                            self.emit_u32(idx);
4415                        }
4416                    }
4417                    Literal::LegacyOctal(n) => {
4418                        if self.is_strict {
4419                            return Err(
4420                                "SyntaxError: Octal literals are not allowed in strict mode"
4421                                    .to_string(),
4422                            );
4423                        }
4424                        if *n >= i32::MIN as i64 && *n <= i32::MAX as i64 {
4425                            let val = *n as i32;
4426                            if val >= i8::MIN as i32 && val <= i8::MAX as i32 {
4427                                self.emit(Opcode::LoadInt8);
4428                                self.emit_u16(r);
4429                                self.emit_u8(val as u8);
4430                            } else {
4431                                self.emit_int_const(r, val);
4432                            }
4433                        } else {
4434                            let idx = self.add_constant(JSValue::new_float(*n as f64));
4435                            self.emit(Opcode::LoadConst);
4436                            self.emit_u16(r);
4437                            self.emit_u32(idx);
4438                        }
4439                    }
4440                    Literal::String(s, _) => {
4441                        let idx = self.intern_and_add_const(ctx, s);
4442                        self.emit(Opcode::LoadConst);
4443                        self.emit_u16(r);
4444                        self.emit_u32(idx);
4445                    }
4446                    Literal::Boolean(b) => {
4447                        if *b {
4448                            self.emit(Opcode::LoadTrue);
4449                        } else {
4450                            self.emit(Opcode::LoadFalse);
4451                        }
4452                        self.emit_u16(r);
4453                    }
4454                    Literal::Null => {
4455                        self.emit(Opcode::LoadNull);
4456                        self.emit_u16(r);
4457                    }
4458                    Literal::Undefined => {
4459                        self.emit(Opcode::LoadUndefined);
4460                        self.emit_u16(r);
4461                    }
4462                    Literal::BigInt(s) => {
4463                        let atom = ctx.atom_table_mut().intern(s);
4464                        let idx = self.add_constant(JSValue::new_string(atom));
4465                        self.emit(Opcode::LoadConst);
4466                        self.emit_u16(r);
4467                        self.emit_u32(idx);
4468
4469                        let bigint_atom = ctx.common_atoms.bigint;
4470                        let bigint_idx = self.add_constant(JSValue::new_string(bigint_atom));
4471                        let func_reg = self.alloc_register();
4472                        self.emit(Opcode::GetGlobal);
4473                        self.emit_u16(func_reg);
4474                        self.emit_u32(bigint_idx);
4475                        self.emit(Opcode::Call);
4476                        self.emit_u16(r);
4477                        self.emit_u16(func_reg);
4478                        self.emit_u16(1);
4479                        self.emit_u16(r);
4480                        self.free_register(func_reg);
4481                    }
4482                }
4483                Ok(r)
4484            }
4485            Expression::BinaryExpression(bin) => self.gen_binary_expression(bin, ctx),
4486            Expression::LogicalExpression(logical) => self.gen_logical_expression(logical, ctx),
4487            Expression::UnaryExpression(unary) => self.gen_unary_expression(unary, ctx),
4488            Expression::UpdateExpression(update) => self.gen_update_expression(update, ctx),
4489            Expression::AssignmentExpression(assign) => self.gen_assignment_expression(assign, ctx),
4490            Expression::ConditionalExpression(cond) => self.gen_conditional_expression(cond, ctx),
4491            Expression::MemberExpression(member) => self.gen_member_expression(member, ctx),
4492            Expression::OptionalMemberExpression(opt) => {
4493                let obj_reg = self.gen_expression(&opt.object, ctx)?;
4494                let result = self.alloc_register();
4495
4496                let undef_reg = self.alloc_register();
4497                self.emit(Opcode::LoadUndefined);
4498                self.emit_u16(undef_reg);
4499                let is_undef = self.alloc_register();
4500                self.emit(Opcode::StrictEq);
4501                self.emit_u16(is_undef);
4502                self.emit_u16(obj_reg);
4503                self.emit_u16(undef_reg);
4504                self.free_register(undef_reg);
4505                self.emit(Opcode::JumpIf);
4506                self.emit_u16(is_undef);
4507                let short_jump = self.code.len();
4508                self.emit_i32(0);
4509                self.free_register(is_undef);
4510
4511                let null_reg = self.alloc_register();
4512                self.emit(Opcode::LoadNull);
4513                self.emit_u16(null_reg);
4514                let is_null = self.alloc_register();
4515                self.emit(Opcode::StrictEq);
4516                self.emit_u16(is_null);
4517                self.emit_u16(obj_reg);
4518                self.emit_u16(null_reg);
4519                self.free_register(null_reg);
4520                self.emit(Opcode::JumpIf);
4521                self.emit_u16(is_null);
4522                let short_jump_2 = self.code.len();
4523                self.emit_i32(0);
4524                self.free_register(is_null);
4525
4526                match &opt.property {
4527                    MemberProperty::Identifier(name) => {
4528                        let atom = ctx.intern(name);
4529                        self.emit(Opcode::GetNamedProp);
4530                        self.emit_u16(result);
4531                        self.emit_u16(obj_reg);
4532                        self.emit_u32(atom.0);
4533                    }
4534                    MemberProperty::Computed(expr) => {
4535                        let key_reg = self.gen_expression(expr, ctx)?;
4536                        self.emit(Opcode::GetField);
4537                        self.emit_u16(result);
4538                        self.emit_u16(obj_reg);
4539                        self.emit_u16(key_reg);
4540                        self.free_register(key_reg);
4541                    }
4542                    MemberProperty::PrivateIdentifier(name) => {
4543                        let key_idx = self.intern_and_add_const(ctx, name);
4544                        let key_reg = self.alloc_register();
4545                        self.emit(Opcode::LoadConst);
4546                        self.emit_u16(key_reg);
4547                        self.emit_u32(key_idx);
4548                        self.emit(Opcode::GetPrivate);
4549                        self.emit_u16(result);
4550                        self.emit_u16(obj_reg);
4551                        self.emit_u16(key_reg);
4552                        self.free_register(key_reg);
4553                    }
4554                }
4555                self.emit(Opcode::Jump);
4556                let end_jump = self.code.len();
4557                self.emit_i32(0);
4558
4559                let short_pos = self.code.len();
4560                self.patch_jump(short_jump, short_pos);
4561                self.patch_jump(short_jump_2, short_pos);
4562                self.emit(Opcode::LoadUndefined);
4563                self.emit_u16(result);
4564
4565                let end_pos = self.code.len();
4566                self.patch_jump(end_jump, end_pos);
4567                self.free_register(obj_reg);
4568                Ok(result)
4569            }
4570            Expression::CallExpression(call) => self.gen_call_expression(call, ctx),
4571            Expression::NewExpression(new) => self.gen_new_expression(new, ctx),
4572            Expression::ArrayExpression(arr) => self.gen_array_expression(arr, ctx),
4573            Expression::ObjectExpression(obj) => self.gen_object_expression(obj, ctx),
4574            Expression::TemplateLiteral(tpl) => {
4575                let first_quasi = tpl
4576                    .quasis
4577                    .first()
4578                    .map(|quasi| quasi.value.as_str())
4579                    .unwrap_or("");
4580                let result = self.alloc_register();
4581                let first_idx = self.intern_and_add_const(ctx, first_quasi);
4582                self.emit(Opcode::LoadConst);
4583                self.emit_u16(result);
4584                self.emit_u32(first_idx);
4585
4586                for (i, expr) in tpl.expressions.iter().enumerate() {
4587                    let expr_reg = self.gen_expression(expr, ctx)?;
4588                    self.emit(Opcode::Add);
4589                    self.emit_u16(result);
4590                    self.emit_u16(result);
4591                    self.emit_u16(expr_reg);
4592                    self.free_register(expr_reg);
4593
4594                    if let Some(quasi) = tpl.quasis.get(i + 1) {
4595                        let quasi_idx = self.intern_and_add_const(ctx, &quasi.value);
4596                        let quasi_reg = self.alloc_register();
4597                        self.emit(Opcode::LoadConst);
4598                        self.emit_u16(quasi_reg);
4599                        self.emit_u32(quasi_idx);
4600                        self.emit(Opcode::Add);
4601                        self.emit_u16(result);
4602                        self.emit_u16(result);
4603                        self.emit_u16(quasi_reg);
4604                        self.free_register(quasi_reg);
4605                    }
4606                }
4607
4608                Ok(result)
4609            }
4610            Expression::FunctionExpression(func) => self.gen_function_expression(
4611                &func.params,
4612                &func.body,
4613                func.name.as_deref(),
4614                ctx,
4615                func.generator,
4616                func.is_async,
4617                false,
4618            ),
4619            Expression::ArrowFunction(arrow) => {
4620                let body = match &arrow.body {
4621                    ArrowBody::Expression(expr) => BlockStatement {
4622                        body: vec![ASTNode::ReturnStatement(ReturnStatement {
4623                            argument: Some((**expr).clone()),
4624                        })],
4625                        lines: vec![],
4626                    },
4627                    ArrowBody::Block(block) => block.clone(),
4628                };
4629                self.gen_function_expression(
4630                    &arrow.params,
4631                    &body,
4632                    None,
4633                    ctx,
4634                    false,
4635                    arrow.is_async,
4636                    true,
4637                )
4638            }
4639            Expression::This => Ok(0),
4640            Expression::Super => {
4641                let r = self.alloc_register();
4642                self.emit(Opcode::GetSuper);
4643                self.emit_u16(r);
4644                Ok(r)
4645            }
4646            Expression::PrivateIdentifier(name) => {
4647                let idx = self.intern_and_add_const(ctx, name);
4648                let r = self.alloc_register();
4649                self.emit(Opcode::LoadConst);
4650                self.emit_u16(r);
4651                self.emit_u32(idx);
4652                Ok(r)
4653            }
4654            Expression::YieldExpression(yield_expr) => {
4655                if yield_expr.delegate {
4656                    return Err("yield* not yet supported in register VM".to_string());
4657                }
4658                let val_reg = if let Some(arg) = &yield_expr.argument {
4659                    self.gen_expression(arg, ctx)?
4660                } else {
4661                    let r = self.alloc_register();
4662                    self.emit(Opcode::LoadUndefined);
4663                    self.emit_u16(r);
4664                    r
4665                };
4666                self.emit(Opcode::Yield);
4667                self.emit_u16(val_reg);
4668                Ok(val_reg)
4669            }
4670            Expression::AwaitExpression(await_expr) => {
4671                let val_reg = self.gen_expression(&await_expr.argument, ctx)?;
4672                self.emit(Opcode::Await);
4673                self.emit_u16(val_reg);
4674                Ok(val_reg)
4675            }
4676            Expression::RegExpLiteral(re) => {
4677                let pattern_idx = self.intern_and_add_const(ctx, &re.pattern);
4678                let flags_idx = self.intern_and_add_const(ctx, &re.flags);
4679                let dst = self.alloc_register();
4680                self.emit(Opcode::NewRegExp);
4681                self.emit_u16(dst);
4682                self.emit_u32(pattern_idx);
4683                self.emit_u32(flags_idx);
4684                Ok(dst)
4685            }
4686            Expression::SequenceExpression(seq) => {
4687                let mut last_r = None;
4688                for (i, e) in seq.expressions.iter().enumerate() {
4689                    let r = self.gen_expression(e, ctx)?;
4690                    if i == seq.expressions.len() - 1 {
4691                        last_r = Some(r);
4692                    } else {
4693                        self.free_register(r);
4694                    }
4695                }
4696
4697                Ok(last_r.unwrap_or_else(|| {
4698                    let r = self.alloc_register();
4699                    self.emit(Opcode::LoadUndefined);
4700                    self.emit_u16(r);
4701                    r
4702                }))
4703            }
4704            Expression::ClassExpression(ce) => {
4705                let class_name = ce.name.as_deref().unwrap_or("");
4706                let class_slot = self.alloc_register();
4707                self.gen_class_body(class_slot, class_name, &ce.super_class, &ce.body, ctx)?;
4708                Ok(class_slot)
4709            }
4710            _ => {
4711                let r = self.alloc_register();
4712                self.emit(Opcode::LoadUndefined);
4713                self.emit_u16(r);
4714                Ok(r)
4715            }
4716        }
4717    }
4718
4719    fn gen_binary_expression(
4720        &mut self,
4721        bin: &BinaryExpression,
4722        ctx: &mut JSContext,
4723    ) -> Result<u16, String> {
4724        let left = self.gen_expression(&bin.left, ctx)?;
4725
4726        let right_imm8 = if let Expression::Literal(Literal::Number(n)) = &*bin.right {
4727            if n.fract() == 0.0 {
4728                let v = *n as i64;
4729                if v >= i8::MIN as i64 && v <= i8::MAX as i64 {
4730                    Some(v as i8)
4731                } else {
4732                    None
4733                }
4734            } else {
4735                None
4736            }
4737        } else {
4738            None
4739        };
4740
4741        if let Some(imm) = right_imm8 {
4742            match bin.op {
4743                BinaryOp::Sub => {
4744                    let dst = self.alloc_register();
4745                    self.emit(Opcode::SubImm8);
4746                    self.emit_u16(dst);
4747                    self.emit_u16(left);
4748                    self.emit_u8(imm as u8);
4749                    self.free_register(left);
4750                    return Ok(dst);
4751                }
4752                BinaryOp::Lte => {
4753                    let dst = self.alloc_register();
4754                    self.emit(Opcode::LteImm8);
4755                    self.emit_u16(dst);
4756                    self.emit_u16(left);
4757                    self.emit_u8(imm as u8);
4758                    self.free_register(left);
4759                    return Ok(dst);
4760                }
4761                _ => {}
4762            }
4763        }
4764
4765        let right = self.gen_expression(&bin.right, ctx)?;
4766
4767        let op = match bin.op {
4768            BinaryOp::Add => Opcode::Add,
4769            BinaryOp::Sub => Opcode::Sub,
4770            BinaryOp::Mul => Opcode::Mul,
4771            BinaryOp::Div => Opcode::Div,
4772            BinaryOp::Mod => Opcode::Mod,
4773            BinaryOp::Pow => Opcode::Pow,
4774            BinaryOp::BitAnd => Opcode::BitAnd,
4775            BinaryOp::BitOr => Opcode::BitOr,
4776            BinaryOp::BitXor => Opcode::BitXor,
4777            BinaryOp::Shl => Opcode::Shl,
4778            BinaryOp::Shr => Opcode::Shr,
4779            BinaryOp::UShr => Opcode::UShr,
4780            BinaryOp::Lt => Opcode::Lt,
4781            BinaryOp::Lte => Opcode::Lte,
4782            BinaryOp::Gt => Opcode::Gt,
4783            BinaryOp::Gte => Opcode::Gte,
4784            BinaryOp::Eq => Opcode::Eq,
4785            BinaryOp::Neq => Opcode::Neq,
4786            BinaryOp::StrictEq => Opcode::StrictEq,
4787            BinaryOp::StrictNeq => Opcode::StrictNeq,
4788            BinaryOp::In => {
4789                if matches!(&*bin.left, Expression::PrivateIdentifier(_)) {
4790                    Opcode::HasPrivate
4791                } else {
4792                    Opcode::HasProperty
4793                }
4794            }
4795            BinaryOp::InstanceOf => Opcode::InstanceOf,
4796        };
4797
4798        let dst = self.alloc_register();
4799        self.emit(op);
4800        self.emit_u16(dst);
4801
4802        if bin.op == BinaryOp::In {
4803            self.emit_u16(right);
4804            self.emit_u16(left);
4805        } else {
4806            self.emit_u16(left);
4807            self.emit_u16(right);
4808        }
4809
4810        self.free_register(left);
4811        self.free_register(right);
4812        Ok(dst)
4813    }
4814
4815    fn gen_logical_expression(
4816        &mut self,
4817        logical: &LogicalExpression,
4818        ctx: &mut JSContext,
4819    ) -> Result<u16, String> {
4820        if self.p2_fold_profile.allow_logical_bool_left() {
4821            if let Expression::Literal(Literal::Boolean(left_bool)) = &*logical.left {
4822                match logical.op {
4823                    LogicalOp::And => {
4824                        if *left_bool {
4825                            return self.gen_expression(&logical.right, ctx);
4826                        }
4827                        let dst = self.alloc_register();
4828                        self.emit_bool_const(dst, false);
4829                        return Ok(dst);
4830                    }
4831                    LogicalOp::Or => {
4832                        if *left_bool {
4833                            let dst = self.alloc_register();
4834                            self.emit_bool_const(dst, true);
4835                            return Ok(dst);
4836                        }
4837                        return self.gen_expression(&logical.right, ctx);
4838                    }
4839                    LogicalOp::NullishCoalescing => {}
4840                }
4841            }
4842        }
4843
4844        if self.p2_fold_profile.allow_aggressive_truthy() {
4845            if let Expression::Literal(lit) = &*logical.left {
4846                if let Some(left_truthy) = Self::literal_truthiness(lit) {
4847                    match logical.op {
4848                        LogicalOp::And => {
4849                            if left_truthy {
4850                                return self.gen_expression(&logical.right, ctx);
4851                            }
4852                            return self.gen_expression(&logical.left, ctx);
4853                        }
4854                        LogicalOp::Or => {
4855                            if left_truthy {
4856                                return self.gen_expression(&logical.left, ctx);
4857                            }
4858                            return self.gen_expression(&logical.right, ctx);
4859                        }
4860                        LogicalOp::NullishCoalescing => {}
4861                    }
4862                }
4863            }
4864        }
4865
4866        if self.p2_fold_profile.allow_nullish_literal_left() {
4867            if let LogicalOp::NullishCoalescing = logical.op {
4868                if let Expression::Literal(lit) = &*logical.left {
4869                    return match lit {
4870                        Literal::Null | Literal::Undefined => {
4871                            self.gen_expression(&logical.right, ctx)
4872                        }
4873                        _ => self.gen_expression(&logical.left, ctx),
4874                    };
4875                }
4876            }
4877        }
4878
4879        match logical.op {
4880            LogicalOp::And => {
4881                if self.opt_branch_result_prealloc {
4882                    let left = self.gen_expression(&logical.left, ctx)?;
4883                    let result = if self.reserved_slots.contains(&left) {
4884                        let dst = self.alloc_register();
4885                        self.emit(Opcode::Move);
4886                        self.emit_u16(dst);
4887                        self.emit_u16(left);
4888                        dst
4889                    } else {
4890                        left
4891                    };
4892
4893                    self.emit(Opcode::JumpIfNot);
4894                    self.emit_u16(left);
4895                    let short_jump = self.code.len();
4896                    self.emit_i32(0);
4897
4898                    let right = self.gen_expression(&logical.right, ctx)?;
4899                    if right != result {
4900                        self.emit(Opcode::Move);
4901                        self.emit_u16(result);
4902                        self.emit_u16(right);
4903                        self.free_register(right);
4904                    }
4905
4906                    let end_pos = self.code.len();
4907                    self.patch_jump(short_jump, end_pos);
4908                    return Ok(result);
4909                }
4910
4911                let left = self.gen_expression(&logical.left, ctx)?;
4912                self.emit(Opcode::JumpIfNot);
4913                self.emit_u16(left);
4914                let short_jump = self.code.len();
4915                self.emit_i32(0);
4916
4917                let right = self.gen_expression(&logical.right, ctx)?;
4918
4919                let result = if self.reserved_slots.contains(&left) && right != left {
4920                    let dst = self.alloc_register();
4921                    self.emit(Opcode::Move);
4922                    self.emit_u16(dst);
4923                    self.emit_u16(right);
4924                    self.free_register(right);
4925                    dst
4926                } else if right != left {
4927                    self.emit(Opcode::Move);
4928                    self.emit_u16(left);
4929                    self.emit_u16(right);
4930                    self.free_register(right);
4931                    left
4932                } else {
4933                    left
4934                };
4935
4936                if result != left {
4937                    self.emit(Opcode::Jump);
4938                    let end_jump = self.code.len();
4939                    self.emit_i32(0);
4940                    let patch_pos = self.code.len();
4941                    self.patch_jump(short_jump, patch_pos);
4942                    self.emit(Opcode::Move);
4943                    self.emit_u16(result);
4944                    self.emit_u16(left);
4945                    let end_pos = self.code.len();
4946                    self.patch_jump(end_jump, end_pos);
4947                } else {
4948                    let end_pos = self.code.len();
4949                    self.patch_jump(short_jump, end_pos);
4950                }
4951                Ok(result)
4952            }
4953            LogicalOp::Or => {
4954                if self.opt_branch_result_prealloc {
4955                    let left = self.gen_expression(&logical.left, ctx)?;
4956                    let result = if self.reserved_slots.contains(&left) {
4957                        let dst = self.alloc_register();
4958                        self.emit(Opcode::Move);
4959                        self.emit_u16(dst);
4960                        self.emit_u16(left);
4961                        dst
4962                    } else {
4963                        left
4964                    };
4965
4966                    self.emit(Opcode::JumpIf);
4967                    self.emit_u16(left);
4968                    let short_jump = self.code.len();
4969                    self.emit_i32(0);
4970
4971                    let right = self.gen_expression(&logical.right, ctx)?;
4972                    if right != result {
4973                        self.emit(Opcode::Move);
4974                        self.emit_u16(result);
4975                        self.emit_u16(right);
4976                        self.free_register(right);
4977                    }
4978
4979                    let end_pos = self.code.len();
4980                    self.patch_jump(short_jump, end_pos);
4981                    return Ok(result);
4982                }
4983
4984                let left = self.gen_expression(&logical.left, ctx)?;
4985                self.emit(Opcode::JumpIf);
4986                self.emit_u16(left);
4987                let short_jump = self.code.len();
4988                self.emit_i32(0);
4989
4990                let right = self.gen_expression(&logical.right, ctx)?;
4991
4992                let result = if self.reserved_slots.contains(&left) && right != left {
4993                    let dst = self.alloc_register();
4994                    self.emit(Opcode::Move);
4995                    self.emit_u16(dst);
4996                    self.emit_u16(right);
4997                    self.free_register(right);
4998                    dst
4999                } else if right != left {
5000                    self.emit(Opcode::Move);
5001                    self.emit_u16(left);
5002                    self.emit_u16(right);
5003                    self.free_register(right);
5004                    left
5005                } else {
5006                    left
5007                };
5008
5009                if result != left {
5010                    self.emit(Opcode::Jump);
5011                    let end_jump = self.code.len();
5012                    self.emit_i32(0);
5013                    let patch_pos = self.code.len();
5014                    self.patch_jump(short_jump, patch_pos);
5015                    self.emit(Opcode::Move);
5016                    self.emit_u16(result);
5017                    self.emit_u16(left);
5018                    let end_pos = self.code.len();
5019                    self.patch_jump(end_jump, end_pos);
5020                } else {
5021                    let end_pos = self.code.len();
5022                    self.patch_jump(short_jump, end_pos);
5023                }
5024                Ok(result)
5025            }
5026            LogicalOp::NullishCoalescing => {
5027                let left = self.gen_expression(&logical.left, ctx)?;
5028                let result = if self.reserved_slots.contains(&left) {
5029                    let dst = self.alloc_register();
5030                    self.emit(Opcode::Move);
5031                    self.emit_u16(dst);
5032                    self.emit_u16(left);
5033                    dst
5034                } else {
5035                    left
5036                };
5037
5038                let eval_right_jump = if self.opt_jump_if_nullish {
5039                    self.emit(Opcode::JumpIfNullish);
5040                    self.emit_u16(left);
5041                    let patch = self.code.len();
5042                    self.emit_i32(0);
5043                    patch
5044                } else {
5045                    let check_reg = self.alloc_register();
5046                    self.emit(Opcode::LoadNull);
5047                    self.emit_u16(check_reg);
5048                    self.emit(Opcode::Eq);
5049                    self.emit_u16(check_reg);
5050                    self.emit_u16(left);
5051                    self.emit_u16(check_reg);
5052                    self.emit(Opcode::JumpIf);
5053                    self.emit_u16(check_reg);
5054                    let patch = self.code.len();
5055                    self.emit_i32(0);
5056                    self.free_register(check_reg);
5057                    patch
5058                };
5059
5060                self.emit(Opcode::Jump);
5061                let end_jump = self.code.len();
5062                self.emit_i32(0);
5063
5064                let eval_right_pos = self.code.len();
5065                self.patch_jump(eval_right_jump, eval_right_pos);
5066                let right = self.gen_expression(&logical.right, ctx)?;
5067                if right != result {
5068                    self.emit(Opcode::Move);
5069                    self.emit_u16(result);
5070                    self.emit_u16(right);
5071                    self.free_register(right);
5072                }
5073
5074                let end_pos = self.code.len();
5075                self.patch_jump(end_jump, end_pos);
5076                Ok(result)
5077            }
5078        }
5079    }
5080
5081    fn gen_unary_expression(
5082        &mut self,
5083        unary: &UnaryExpression,
5084        ctx: &mut JSContext,
5085    ) -> Result<u16, String> {
5086        let saved = self.suppress_ref_error;
5087        if matches!(unary.op, UnaryOp::TypeOf) {
5088            if matches!(&*unary.argument, Expression::Identifier(_)) {
5089                self.suppress_ref_error = true;
5090            }
5091        }
5092        if matches!(unary.op, UnaryOp::Delete) {
5093            if matches!(&*unary.argument, Expression::Identifier(_)) {
5094                self.suppress_ref_error = true;
5095            }
5096        }
5097        if matches!(unary.op, UnaryOp::Delete) {
5098            self.suppress_ref_error = saved;
5099            return self.gen_delete_expression(0, &unary.argument, ctx);
5100        }
5101        let arg = self.gen_expression(&unary.argument, ctx)?;
5102        self.suppress_ref_error = saved;
5103        match unary.op {
5104            UnaryOp::Minus => {
5105                let dst = self.alloc_register();
5106                self.emit(Opcode::Neg);
5107                self.emit_u16(dst);
5108                self.emit_u16(arg);
5109                self.free_register(arg);
5110                return Ok(dst);
5111            }
5112            UnaryOp::Not => {
5113                let dst = self.alloc_register();
5114                self.emit(Opcode::Not);
5115                self.emit_u16(dst);
5116                self.emit_u16(arg);
5117                self.free_register(arg);
5118                return Ok(dst);
5119            }
5120            UnaryOp::TypeOf => {
5121                let dst = self.alloc_register();
5122                self.emit(Opcode::TypeOf);
5123                self.emit_u16(dst);
5124                self.emit_u16(arg);
5125                self.free_register(arg);
5126                return Ok(dst);
5127            }
5128            UnaryOp::BitNot => {
5129                let dst = self.alloc_register();
5130                self.emit(Opcode::BitNot);
5131                self.emit_u16(dst);
5132                self.emit_u16(arg);
5133                self.free_register(arg);
5134                return Ok(dst);
5135            }
5136            UnaryOp::Plus => {
5137                let dst = self.alloc_register();
5138                self.emit(Opcode::Pos);
5139                self.emit_u16(dst);
5140                self.emit_u16(arg);
5141                self.free_register(arg);
5142                return Ok(dst);
5143            }
5144            UnaryOp::Void => {
5145                let dst = self.alloc_register();
5146                self.emit(Opcode::LoadUndefined);
5147                self.emit_u16(dst);
5148                self.free_register(arg);
5149                return Ok(dst);
5150            }
5151            UnaryOp::Delete => {
5152                return self.gen_delete_expression(arg, &unary.argument, ctx);
5153            }
5154        }
5155    }
5156
5157    fn gen_delete_expression(
5158        &mut self,
5159        arg_reg: u16,
5160        arg_expr: &Expression,
5161        ctx: &mut JSContext,
5162    ) -> Result<u16, String> {
5163        match arg_expr {
5164            Expression::Identifier(id) => {
5165                self.free_register(arg_reg);
5166                let dst = self.alloc_register();
5167                if id.name == "arguments" && !self.is_script_level && !self.is_arrow {
5168                    self.emit(Opcode::LoadFalse);
5169                    self.emit_u16(dst);
5170                } else if self.resolve_var(&id.name, ctx).is_some() {
5171                    self.emit(Opcode::LoadFalse);
5172                    self.emit_u16(dst);
5173                } else {
5174                    let atom = ctx.intern(&id.name);
5175                    let idx = self.add_constant(JSValue::new_string(atom));
5176                    self.emit(Opcode::DeleteGlobal);
5177                    self.emit_u16(dst);
5178                    self.emit_u32(idx);
5179                }
5180                Ok(dst)
5181            }
5182            Expression::MemberExpression(member) => {
5183                if matches!(&*member.object, Expression::Super) {
5184                    if let MemberProperty::Computed(expr) = &member.property {
5185                        self.gen_expression(expr, ctx)?;
5186                    }
5187                    self.free_register(arg_reg);
5188                    let msg_idx = self.add_constant(JSValue::new_string(
5189                        ctx.intern("Cannot delete super reference"),
5190                    ));
5191                    self.emit(Opcode::ThrowReferenceError);
5192                    self.emit_u32(msg_idx);
5193                    let dst = self.alloc_register();
5194                    self.emit(Opcode::LoadUndefined);
5195                    self.emit_u16(dst);
5196                    return Ok(dst);
5197                }
5198                let obj = self.gen_expression(&member.object, ctx)?;
5199                let dst = self.alloc_register();
5200                match &member.property {
5201                    MemberProperty::Identifier(name) => {
5202                        let key_idx = self.intern_and_add_const(ctx, name);
5203                        let key = self.alloc_register();
5204                        self.emit(Opcode::LoadConst);
5205                        self.emit_u16(key);
5206                        self.emit_u32(key_idx);
5207                        self.emit(Opcode::DeleteProp);
5208                        self.emit_u16(dst);
5209                        self.emit_u16(obj);
5210                        self.emit_u16(key);
5211                        self.free_register(key);
5212                    }
5213                    MemberProperty::Computed(expr) => {
5214                        let key = self.gen_expression(expr, ctx)?;
5215                        self.emit(Opcode::DeleteProp);
5216                        self.emit_u16(dst);
5217                        self.emit_u16(obj);
5218                        self.emit_u16(key);
5219                    }
5220                    MemberProperty::PrivateIdentifier(name) => {
5221                        let key_idx = self.intern_and_add_const(ctx, name);
5222                        let key = self.alloc_register();
5223                        self.emit(Opcode::LoadConst);
5224                        self.emit_u16(key);
5225                        self.emit_u32(key_idx);
5226                        self.emit(Opcode::DeleteProp);
5227                        self.emit_u16(dst);
5228                        self.emit_u16(obj);
5229                        self.emit_u16(key);
5230                        self.free_register(key);
5231                    }
5232                }
5233                self.free_register(obj);
5234                Ok(dst)
5235            }
5236            Expression::Literal(Literal::Number(n)) if n.is_nan() || n.is_infinite() => {
5237                self.free_register(arg_reg);
5238                let name = if n.is_nan() { "NaN" } else { "Infinity" };
5239                let atom = ctx.intern(name);
5240                let idx = self.add_constant(JSValue::new_string(atom));
5241                let dst = self.alloc_register();
5242                self.emit(Opcode::DeleteGlobal);
5243                self.emit_u16(dst);
5244                self.emit_u32(idx);
5245                Ok(dst)
5246            }
5247            _ => {
5248                self.free_register(arg_reg);
5249                let dst = self.alloc_register();
5250                self.emit(Opcode::LoadTrue);
5251                self.emit_u16(dst);
5252                Ok(dst)
5253            }
5254        }
5255    }
5256
5257    fn gen_update_expression(
5258        &mut self,
5259        update: &UpdateExpression,
5260        ctx: &mut JSContext,
5261    ) -> Result<u16, String> {
5262        match &*update.argument {
5263            Expression::Identifier(id) => {
5264                if let Some(entry) = self.lookup_var(&id.name) {
5265                    if entry.kind == VariableKind::Const {
5266                        return Err(format!(
5267                            "TypeError: Assignment to constant variable '{}'",
5268                            id.name
5269                        ));
5270                    }
5271                }
5272                match self.resolve_var(&id.name, ctx) {
5273                    Some(VarLocation::Local(slot)) => {
5274                        if let Some(entry) = self.lookup_var(&id.name) {
5275                            if entry.kind != VariableKind::Var && !entry.initialized {
5276                                self.emit(Opcode::CheckTdz);
5277                                self.emit_u16(slot);
5278                            }
5279                        }
5280                        let op = match update.op {
5281                            UpdateOp::Increment => Opcode::IncLocal,
5282                            UpdateOp::Decrement => Opcode::DecLocal,
5283                        };
5284
5285                        if update.prefix {
5286                            self.emit(op);
5287                            self.emit_u16(slot);
5288                            if self.is_global_var(&id.name) {
5289                                let atom = ctx.intern(&id.name);
5290                                let idx = self.add_constant(JSValue::new_string(atom));
5291                                self.emit(Opcode::SetGlobal);
5292                                self.emit_u32(idx);
5293                                self.emit_u16(slot);
5294                            }
5295                            Ok(slot)
5296                        } else {
5297                            let old_val = self.alloc_register();
5298                            self.emit(Opcode::Move);
5299                            self.emit_u16(old_val);
5300                            self.emit_u16(slot);
5301                            self.emit(Opcode::Pos);
5302                            self.emit_u16(old_val);
5303                            self.emit_u16(old_val);
5304                            self.emit(op);
5305                            self.emit_u16(slot);
5306                            if self.is_global_var(&id.name) {
5307                                let atom = ctx.intern(&id.name);
5308                                let idx = self.add_constant(JSValue::new_string(atom));
5309                                self.emit(Opcode::SetGlobal);
5310                                self.emit_u32(idx);
5311                                self.emit_u16(slot);
5312                            }
5313                            Ok(old_val)
5314                        }
5315                    }
5316                    Some(VarLocation::Upvalue(slot)) => {
5317                        let temp = self.alloc_register();
5318                        self.emit(Opcode::GetUpvalue);
5319                        self.emit_u16(temp);
5320                        self.emit_u16(slot);
5321
5322                        if let Some(entry) = self.lookup_var(&id.name) {
5323                            if entry.kind != VariableKind::Var && !entry.initialized {
5324                                self.emit(Opcode::CheckTdz);
5325                                self.emit_u16(temp);
5326                            }
5327                        }
5328
5329                        let op = match update.op {
5330                            UpdateOp::Increment => Opcode::IncLocal,
5331                            UpdateOp::Decrement => Opcode::DecLocal,
5332                        };
5333
5334                        if update.prefix {
5335                            self.emit(op);
5336                            self.emit_u16(temp);
5337                            self.emit(Opcode::SetUpvalue);
5338                            self.emit_u16(slot);
5339                            self.emit_u16(temp);
5340                            Ok(temp)
5341                        } else {
5342                            let old_val = self.alloc_register();
5343                            self.emit(Opcode::Move);
5344                            self.emit_u16(old_val);
5345                            self.emit_u16(temp);
5346                            self.emit(op);
5347                            self.emit_u16(temp);
5348                            self.emit(Opcode::SetUpvalue);
5349                            self.emit_u16(slot);
5350                            self.emit_u16(temp);
5351                            Ok(old_val)
5352                        }
5353                    }
5354                    None => {
5355                        let atom = ctx.intern(&id.name);
5356                        let const_idx = self.add_constant(JSValue::new_string(atom));
5357                        let r = self.alloc_register();
5358                        self.emit(Opcode::GetGlobal);
5359                        self.emit_u16(r);
5360                        self.emit_u32(const_idx);
5361                        let op = match update.op {
5362                            UpdateOp::Increment => Opcode::IncLocal,
5363                            UpdateOp::Decrement => Opcode::DecLocal,
5364                        };
5365                        if update.prefix {
5366                            self.emit(op);
5367                            self.emit_u16(r);
5368                            self.emit(Opcode::SetGlobal);
5369                            self.emit_u32(const_idx);
5370                            self.emit_u16(r);
5371                            Ok(r)
5372                        } else {
5373                            let old = self.alloc_register();
5374                            self.emit(Opcode::Move);
5375                            self.emit_u16(old);
5376                            self.emit_u16(r);
5377                            self.emit(op);
5378                            self.emit_u16(r);
5379                            self.emit(Opcode::SetGlobal);
5380                            self.emit_u32(const_idx);
5381                            self.emit_u16(r);
5382                            self.free_register(r);
5383                            Ok(old)
5384                        }
5385                    }
5386                }
5387            }
5388            Expression::MemberExpression(member) => {
5389                let obj = self.gen_expression(&member.object, ctx)?;
5390                let op = match update.op {
5391                    UpdateOp::Increment => Opcode::IncLocal,
5392                    UpdateOp::Decrement => Opcode::DecLocal,
5393                };
5394
5395                match &member.property {
5396                    MemberProperty::Identifier(name) => {
5397                        let atom = ctx.intern(name);
5398                        let tmp = self.alloc_register();
5399                        self.emit(Opcode::GetNamedProp);
5400                        self.emit_u16(tmp);
5401                        self.emit_u16(obj);
5402                        self.emit_u32(atom.0);
5403
5404                        if update.prefix {
5405                            self.emit(op);
5406                            self.emit_u16(tmp);
5407                            self.emit(Opcode::SetNamedProp);
5408                            self.emit_u16(obj);
5409                            self.emit_u16(tmp);
5410                            self.emit_u32(atom.0);
5411                            self.free_register(obj);
5412                            Ok(tmp)
5413                        } else {
5414                            let old_val = self.alloc_register();
5415                            self.emit(Opcode::Move);
5416                            self.emit_u16(old_val);
5417                            self.emit_u16(tmp);
5418                            self.emit(Opcode::Pos);
5419                            self.emit_u16(old_val);
5420                            self.emit_u16(old_val);
5421                            self.emit(op);
5422                            self.emit_u16(tmp);
5423                            self.emit(Opcode::SetNamedProp);
5424                            self.emit_u16(obj);
5425                            self.emit_u16(tmp);
5426                            self.emit_u32(atom.0);
5427                            self.free_register(obj);
5428                            self.free_register(tmp);
5429                            Ok(old_val)
5430                        }
5431                    }
5432                    MemberProperty::Computed(expr) => {
5433                        let key = self.gen_expression(expr, ctx)?;
5434
5435                        let tmp = self.alloc_register();
5436                        self.emit(Opcode::GetField);
5437                        self.emit_u16(tmp);
5438                        self.emit_u16(obj);
5439                        self.emit_u16(key);
5440
5441                        if update.prefix {
5442                            self.emit(op);
5443                            self.emit_u16(tmp);
5444                            self.emit(Opcode::SetProp);
5445                            self.emit_u16(obj);
5446                            self.emit_u16(key);
5447                            self.emit_u16(tmp);
5448                            self.free_register(obj);
5449                            self.free_register(key);
5450                            Ok(tmp)
5451                        } else {
5452                            let old_val = self.alloc_register();
5453                            self.emit(Opcode::Move);
5454                            self.emit_u16(old_val);
5455                            self.emit_u16(tmp);
5456                            self.emit(op);
5457                            self.emit_u16(tmp);
5458                            self.emit(Opcode::SetField);
5459                            self.emit_u16(obj);
5460                            self.emit_u16(key);
5461                            self.emit_u16(tmp);
5462                            self.free_register(obj);
5463                            self.free_register(key);
5464                            self.free_register(tmp);
5465                            Ok(old_val)
5466                        }
5467                    }
5468                    MemberProperty::PrivateIdentifier(_) => {
5469                        self.free_register(obj);
5470                        let r = self.alloc_register();
5471                        self.emit(Opcode::LoadUndefined);
5472                        self.emit_u16(r);
5473                        Ok(r)
5474                    }
5475                }
5476            }
5477            _ => {
5478                let r = self.alloc_register();
5479                self.emit(Opcode::LoadUndefined);
5480                self.emit_u16(r);
5481                Ok(r)
5482            }
5483        }
5484    }
5485
5486    fn assign_op_to_reg_opcode(op: &AssignOp) -> Option<Opcode> {
5487        match op {
5488            AssignOp::AddAssign => Some(Opcode::Add),
5489            AssignOp::SubAssign => Some(Opcode::Sub),
5490            AssignOp::MulAssign => Some(Opcode::Mul),
5491            AssignOp::DivAssign => Some(Opcode::Div),
5492            AssignOp::ModAssign => Some(Opcode::Mod),
5493            AssignOp::PowAssign => Some(Opcode::Pow),
5494            AssignOp::BitAndAssign => Some(Opcode::BitAnd),
5495            AssignOp::BitOrAssign => Some(Opcode::BitOr),
5496            AssignOp::BitXorAssign => Some(Opcode::BitXor),
5497            AssignOp::ShlAssign => Some(Opcode::Shl),
5498            AssignOp::ShrAssign => Some(Opcode::Shr),
5499            AssignOp::UShrAssign => Some(Opcode::UShr),
5500            _ => None,
5501        }
5502    }
5503
5504    fn gen_assignment_expression(
5505        &mut self,
5506        assign: &AssignmentExpression,
5507        ctx: &mut JSContext,
5508    ) -> Result<u16, String> {
5509        let is_compound = Self::assign_op_to_reg_opcode(&assign.op);
5510
5511        match &assign.left {
5512            AssignmentTarget::Identifier(name) => {
5513                if self.is_strict && Self::is_strict_reserved_word(name) {
5514                    return Err(format!(
5515                        "SyntaxError: Assignment to '{}' in strict mode is not allowed",
5516                        name
5517                    ));
5518                }
5519                if let Some(entry) = self.lookup_var(name) {
5520                    if entry.kind == VariableKind::Const {
5521                        return Err(format!(
5522                            "TypeError: Assignment to constant variable '{}'",
5523                            name
5524                        ));
5525                    }
5526                }
5527
5528                if matches!(
5529                    assign.op,
5530                    AssignOp::LogicalAndAssign
5531                        | AssignOp::LogicalOrAssign
5532                        | AssignOp::NullishAssign
5533                ) {
5534                    let resolved = self.resolve_var(name, ctx);
5535                    let global_atom = ctx.intern(name);
5536                    let global_idx = self.add_constant(JSValue::new_string(global_atom));
5537
5538                    let current = match resolved {
5539                        Some(VarLocation::Local(slot)) => slot,
5540                        Some(VarLocation::Upvalue(slot)) => {
5541                            let tmp = self.alloc_register();
5542                            self.emit(Opcode::GetUpvalue);
5543                            self.emit_u16(tmp);
5544                            self.emit_u16(slot);
5545                            tmp
5546                        }
5547                        None => {
5548                            let tmp = self.alloc_register();
5549                            self.emit(Opcode::GetGlobal);
5550                            self.emit_u16(tmp);
5551                            self.emit_u32(global_idx);
5552                            tmp
5553                        }
5554                    };
5555
5556                    let jump_to_end = match assign.op {
5557                        AssignOp::LogicalAndAssign => {
5558                            self.emit(Opcode::JumpIfNot);
5559                            self.emit_u16(current);
5560                            let patch = self.code.len();
5561                            self.emit_i32(0);
5562                            patch
5563                        }
5564                        AssignOp::LogicalOrAssign => {
5565                            self.emit(Opcode::JumpIf);
5566                            self.emit_u16(current);
5567                            let patch = self.code.len();
5568                            self.emit_i32(0);
5569                            patch
5570                        }
5571                        AssignOp::NullishAssign => {
5572                            let null_reg = self.alloc_register();
5573                            self.emit(Opcode::LoadNull);
5574                            self.emit_u16(null_reg);
5575                            let cmp_reg = self.alloc_register();
5576                            self.emit(Opcode::StrictEq);
5577                            self.emit_u16(cmp_reg);
5578                            self.emit_u16(current);
5579                            self.emit_u16(null_reg);
5580                            self.emit(Opcode::JumpIf);
5581                            self.emit_u16(cmp_reg);
5582                            let jump_to_assign = self.code.len();
5583                            self.emit_i32(0);
5584
5585                            let undef_reg = self.alloc_register();
5586                            self.emit(Opcode::LoadUndefined);
5587                            self.emit_u16(undef_reg);
5588                            self.emit(Opcode::StrictEq);
5589                            self.emit_u16(cmp_reg);
5590                            self.emit_u16(current);
5591                            self.emit_u16(undef_reg);
5592                            self.emit(Opcode::JumpIfNot);
5593                            self.emit_u16(cmp_reg);
5594                            let jump_to_end = self.code.len();
5595                            self.emit_i32(0);
5596
5597                            let assign_pos = self.code.len();
5598                            self.patch_jump(jump_to_assign, assign_pos);
5599
5600                            self.free_register(undef_reg);
5601                            self.free_register(cmp_reg);
5602                            self.free_register(null_reg);
5603                            jump_to_end
5604                        }
5605                        _ => unreachable!(),
5606                    };
5607
5608                    let rhs = self.gen_expression(&assign.right, ctx)?;
5609                    if current != rhs {
5610                        self.emit(Opcode::Move);
5611                        self.emit_u16(current);
5612                        self.emit_u16(rhs);
5613                    }
5614
5615                    match resolved {
5616                        Some(VarLocation::Local(slot)) => {
5617                            if slot != current {
5618                                self.emit(Opcode::Move);
5619                                self.emit_u16(slot);
5620                                self.emit_u16(current);
5621                            }
5622                            if self.is_global_var(name) {
5623                                let idx = self.add_constant(JSValue::new_string(ctx.intern(name)));
5624                                if self.is_eval {
5625                                    self.emit(Opcode::SetGlobalVar);
5626                                } else {
5627                                    self.emit(Opcode::SetGlobal);
5628                                }
5629                                self.emit_u32(idx);
5630                                self.emit_u16(slot);
5631                            }
5632                        }
5633                        Some(VarLocation::Upvalue(slot)) => {
5634                            self.emit(Opcode::SetUpvalue);
5635                            self.emit_u16(slot);
5636                            self.emit_u16(current);
5637                        }
5638                        None => {
5639                            let idx = self.add_constant(JSValue::new_string(ctx.intern(name)));
5640                            if self.is_eval {
5641                                self.emit(Opcode::SetGlobalVar);
5642                            } else {
5643                                self.emit(Opcode::SetGlobal);
5644                            }
5645                            self.emit_u32(idx);
5646                            self.emit_u16(current);
5647                        }
5648                    }
5649
5650                    let end_pos = self.code.len();
5651                    self.patch_jump(jump_to_end, end_pos);
5652                    if current != rhs {
5653                        self.free_register(rhs);
5654                    }
5655                    return Ok(current);
5656                }
5657
5658                let val = self.gen_expression_with_assign_name(&assign.right, name, ctx)?;
5659                match self.resolve_var(name, ctx) {
5660                    Some(VarLocation::Local(slot)) => {
5661                        // Check TDZ for let/const variables on assignment
5662                        if let Some(entry) = self.lookup_var(name) {
5663                            if entry.kind != VariableKind::Var && !entry.initialized {
5664                                self.emit(Opcode::CheckTdz);
5665                                self.emit_u16(slot);
5666                            }
5667                        }
5668                        if let Some(opcode) = is_compound {
5669                            self.emit(opcode);
5670                            self.emit_u16(slot);
5671                            self.emit_u16(slot);
5672                            self.emit_u16(val);
5673                        } else if slot != val {
5674                            self.emit(Opcode::Move);
5675                            self.emit_u16(slot);
5676                            self.emit_u16(val);
5677                        }
5678
5679                        if self.is_global_var(name) {
5680                            let atom = ctx.intern(name);
5681                            let idx = self.add_constant(JSValue::new_string(atom));
5682                            if self.is_eval {
5683                                self.emit(Opcode::SetGlobalVar);
5684                            } else {
5685                                self.emit(Opcode::SetGlobal);
5686                            }
5687                            self.emit_u32(idx);
5688                            self.emit_u16(slot);
5689                        }
5690                        Ok(slot)
5691                    }
5692                    Some(VarLocation::Upvalue(slot)) => {
5693                        // Check TDZ for upvalue let/const variables
5694                        let mut needs_tdz = false;
5695                        if let Some(entry) = self.lookup_var(name) {
5696                            needs_tdz = entry.kind != VariableKind::Var && !entry.initialized;
5697                        } else if let Some(parent_var) = self.parent_vars.get(name) {
5698                            if !parent_var.is_inherited {
5699                                needs_tdz =
5700                                    parent_var.kind != VariableKind::Var && !parent_var.initialized;
5701                            }
5702                        }
5703                        if let Some(opcode) = is_compound {
5704                            let tmp = self.alloc_register();
5705                            self.emit(Opcode::GetUpvalue);
5706                            self.emit_u16(tmp);
5707                            self.emit_u16(slot);
5708                            if needs_tdz {
5709                                self.emit(Opcode::CheckTdz);
5710                                self.emit_u16(tmp);
5711                            }
5712                            self.emit(opcode);
5713                            self.emit_u16(tmp);
5714                            self.emit_u16(tmp);
5715                            self.emit_u16(val);
5716                            self.emit(Opcode::SetUpvalue);
5717                            self.emit_u16(slot);
5718                            self.emit_u16(tmp);
5719                            self.free_register(tmp);
5720                        } else {
5721                            if needs_tdz {
5722                                let tmp = self.alloc_register();
5723                                self.emit(Opcode::GetUpvalue);
5724                                self.emit_u16(tmp);
5725                                self.emit_u16(slot);
5726                                self.emit(Opcode::CheckTdz);
5727                                self.emit_u16(tmp);
5728                                self.free_register(tmp);
5729                            }
5730                            self.emit(Opcode::SetUpvalue);
5731                            self.emit_u16(slot);
5732                            self.emit_u16(val);
5733                        }
5734                        Ok(val)
5735                    }
5736                    None => {
5737                        let atom = ctx.intern(name);
5738                        let idx = self.add_constant(JSValue::new_string(atom));
5739                        if let Some(opcode) = is_compound {
5740                            let tmp = self.alloc_register();
5741                            self.emit(Opcode::GetGlobal);
5742                            self.emit_u16(tmp);
5743                            self.emit_u32(idx);
5744                            if !self.suppress_ref_error {
5745                                self.emit(Opcode::CheckRef);
5746                                self.emit_u16(tmp);
5747                                self.emit_u32(idx);
5748                            }
5749                            self.emit(opcode);
5750                            self.emit_u16(tmp);
5751                            self.emit_u16(tmp);
5752                            self.emit_u16(val);
5753                            self.emit(Opcode::SetGlobal);
5754                            self.emit_u32(idx);
5755                            self.emit_u16(tmp);
5756                            self.free_register(tmp);
5757                            Ok(val)
5758                        } else {
5759                            self.emit(Opcode::SetGlobal);
5760                            self.emit_u32(idx);
5761                            self.emit_u16(val);
5762                            Ok(val)
5763                        }
5764                    }
5765                }
5766            }
5767            AssignmentTarget::MemberExpression(member) => {
5768                let obj = self.gen_expression(&member.object, ctx)?;
5769                let val = self.gen_expression(&assign.right, ctx)?;
5770
5771                let mut result_reg = val;
5772                match &member.property {
5773                    MemberProperty::Identifier(name) => {
5774                        let atom = ctx.intern(name);
5775                        if let Some(opcode) = is_compound {
5776                            let tmp = self.alloc_register();
5777                            self.emit(Opcode::GetNamedProp);
5778                            self.emit_u16(tmp);
5779                            self.emit_u16(obj);
5780                            self.emit_u32(atom.0);
5781                            self.emit(opcode);
5782                            self.emit_u16(tmp);
5783                            self.emit_u16(tmp);
5784                            self.emit_u16(val);
5785                            self.emit(Opcode::SetNamedProp);
5786                            self.emit_u16(obj);
5787                            self.emit_u16(tmp);
5788                            self.emit_u32(atom.0);
5789                            self.free_register(val);
5790                            result_reg = tmp;
5791                        } else {
5792                            self.emit(Opcode::SetNamedProp);
5793                            self.emit_u16(obj);
5794                            self.emit_u16(val);
5795                            self.emit_u32(atom.0);
5796                        }
5797                    }
5798                    MemberProperty::Computed(expr) => {
5799                        let key = self.gen_expression(expr, ctx)?;
5800                        if let Some(opcode) = is_compound {
5801                            let tmp = self.alloc_register();
5802                            self.emit(Opcode::GetField);
5803                            self.emit_u16(tmp);
5804                            self.emit_u16(obj);
5805                            self.emit_u16(key);
5806                            self.emit(opcode);
5807                            self.emit_u16(tmp);
5808                            self.emit_u16(tmp);
5809                            self.emit_u16(val);
5810                            self.emit(Opcode::SetField);
5811                            self.emit_u16(obj);
5812                            self.emit_u16(key);
5813                            self.emit_u16(tmp);
5814                            self.free_register(val);
5815                            result_reg = tmp;
5816                        } else {
5817                            self.emit(Opcode::SetField);
5818                            self.emit_u16(obj);
5819                            self.emit_u16(key);
5820                            self.emit_u16(val);
5821                        }
5822                        self.free_register(key);
5823                    }
5824                    MemberProperty::PrivateIdentifier(name) => {
5825                        let key_idx = self.intern_and_add_const(ctx, name);
5826                        let key = self.alloc_register();
5827                        self.emit(Opcode::LoadConst);
5828                        self.emit_u16(key);
5829                        self.emit_u32(key_idx);
5830                        if let Some(opcode) = is_compound {
5831                            let tmp = self.alloc_register();
5832                            self.emit(Opcode::GetPrivate);
5833                            self.emit_u16(tmp);
5834                            self.emit_u16(obj);
5835                            self.emit_u16(key);
5836                            self.emit(opcode);
5837                            self.emit_u16(tmp);
5838                            self.emit_u16(tmp);
5839                            self.emit_u16(val);
5840                            self.emit(Opcode::SetPrivate);
5841                            self.emit_u16(obj);
5842                            self.emit_u16(key);
5843                            self.emit_u16(tmp);
5844                            self.free_register(val);
5845                            result_reg = tmp;
5846                        } else {
5847                            self.emit(Opcode::SetPrivate);
5848                            self.emit_u16(obj);
5849                            self.emit_u16(key);
5850                            self.emit_u16(val);
5851                        }
5852                        self.free_register(key);
5853                    }
5854                }
5855                self.free_register(obj);
5856                Ok(result_reg)
5857            }
5858            _ => {
5859                let val = self.gen_expression(&assign.right, ctx)?;
5860                self.gen_assignment_target_destructure(&assign.left, val, ctx)?;
5861                Ok(val)
5862            }
5863        }
5864    }
5865
5866    fn gen_assignment_target_destructure(
5867        &mut self,
5868        target: &AssignmentTarget,
5869        value_reg: u16,
5870        ctx: &mut JSContext,
5871    ) -> Result<(), String> {
5872        match target {
5873            AssignmentTarget::Identifier(name) => match self.resolve_var(name, ctx) {
5874                Some(VarLocation::Local(slot)) => {
5875                    if slot != value_reg {
5876                        self.emit(Opcode::Move);
5877                        self.emit_u16(slot);
5878                        self.emit_u16(value_reg);
5879                    }
5880                    if self.is_global_var(name) {
5881                        let global_idx = self.intern_and_add_const(ctx, name);
5882                        self.emit(Opcode::SetGlobal);
5883                        self.emit_u32(global_idx);
5884                        self.emit_u16(slot);
5885                    }
5886                    Ok(())
5887                }
5888                Some(VarLocation::Upvalue(slot)) => {
5889                    self.emit(Opcode::SetUpvalue);
5890                    self.emit_u16(slot);
5891                    self.emit_u16(value_reg);
5892                    Ok(())
5893                }
5894                None => {
5895                    let global_idx = self.intern_and_add_const(ctx, name);
5896                    self.emit(Opcode::SetGlobal);
5897                    self.emit_u32(global_idx);
5898                    self.emit_u16(value_reg);
5899                    Ok(())
5900                }
5901            },
5902            AssignmentTarget::MemberExpression(member) => {
5903                let obj = self.gen_expression(&member.object, ctx)?;
5904                match &member.property {
5905                    MemberProperty::Identifier(name) => {
5906                        let atom = ctx.intern(name);
5907                        self.emit(Opcode::SetNamedProp);
5908                        self.emit_u16(obj);
5909                        self.emit_u16(value_reg);
5910                        self.emit_u32(atom.0);
5911                    }
5912                    MemberProperty::Computed(expr) => {
5913                        let key = self.gen_expression(expr, ctx)?;
5914                        self.emit(Opcode::SetField);
5915                        self.emit_u16(obj);
5916                        self.emit_u16(key);
5917                        self.emit_u16(value_reg);
5918                        self.free_register(key);
5919                    }
5920                    MemberProperty::PrivateIdentifier(name) => {
5921                        let key_idx = self.intern_and_add_const(ctx, name);
5922                        let key = self.alloc_register();
5923                        self.emit(Opcode::LoadConst);
5924                        self.emit_u16(key);
5925                        self.emit_u32(key_idx);
5926                        self.emit(Opcode::SetPrivate);
5927                        self.emit_u16(obj);
5928                        self.emit_u16(key);
5929                        self.emit_u16(value_reg);
5930                        self.free_register(key);
5931                    }
5932                }
5933                self.free_register(obj);
5934                Ok(())
5935            }
5936            AssignmentTarget::ComputedMember(obj_expr, key_expr) => {
5937                let obj = self.gen_expression(obj_expr, ctx)?;
5938                let key = self.gen_expression(key_expr, ctx)?;
5939                self.emit(Opcode::SetField);
5940                self.emit_u16(obj);
5941                self.emit_u16(key);
5942                self.emit_u16(value_reg);
5943                self.free_register(key);
5944                self.free_register(obj);
5945                Ok(())
5946            }
5947            AssignmentTarget::ArrayPattern(arr) => {
5948                self.gen_array_destructuring_target(arr, value_reg, ctx)
5949            }
5950            AssignmentTarget::ObjectPattern(obj) => {
5951                self.gen_object_destructuring_target(obj, value_reg, ctx)
5952            }
5953            AssignmentTarget::AssignmentPattern(ap) => {
5954                self.gen_default_value_assignment(&ap.right, value_reg, ctx)?;
5955                self.gen_assignment_target_destructure(&ap.left, value_reg, ctx)
5956            }
5957            AssignmentTarget::RestElement(_) => {
5958                Err("Rest element not valid in assignment target".to_string())
5959            }
5960            AssignmentTarget::OptionalMemberExpression(_) => {
5961                Err("Optional member expression not valid in assignment target".to_string())
5962            }
5963        }
5964    }
5965
5966    fn gen_array_destructuring_target(
5967        &mut self,
5968        arr: &ArrayPattern,
5969        value_reg: u16,
5970        ctx: &mut JSContext,
5971    ) -> Result<(), String> {
5972        let iter_reg = self.alloc_register();
5973        self.emit(Opcode::GetIterator);
5974        self.emit_u16(iter_reg);
5975        self.emit_u16(value_reg);
5976
5977        let len = arr.elements.len();
5978        for (i, elem) in arr.elements.iter().enumerate() {
5979            if i == len - 1 {
5980                if let Some(PatternElement::RestElement(rest)) = elem {
5981                    let rest_reg = self.alloc_register();
5982                    self.emit(Opcode::NewArray);
5983                    self.emit_u16(rest_reg);
5984                    self.emit_u16(0);
5985                    let loop_start = self.code.len();
5986                    let val_for_rest = self.alloc_register();
5987                    let done_for_rest = self.alloc_register();
5988                    self.emit(Opcode::IteratorNext);
5989                    self.emit_u16(val_for_rest);
5990                    self.emit_u16(done_for_rest);
5991                    self.emit_u16(iter_reg);
5992                    self.emit(Opcode::JumpIf);
5993                    self.emit_u16(done_for_rest);
5994                    let exit_patch = self.code.len();
5995                    self.emit_i32(0);
5996                    self.free_register(done_for_rest);
5997                    self.emit(Opcode::ArrayPush);
5998                    self.emit_u16(rest_reg);
5999                    self.emit_u16(val_for_rest);
6000                    self.free_register(val_for_rest);
6001                    self.emit_backward_jump(loop_start);
6002                    let end_pos = self.code.len();
6003                    self.patch_jump(exit_patch, end_pos);
6004                    self.gen_assignment_target_destructure(&rest.argument, rest_reg, ctx)?;
6005                    self.free_register(rest_reg);
6006                    self.free_register(iter_reg);
6007                    return Ok(());
6008                }
6009            }
6010
6011            let val_reg = self.alloc_register();
6012            let done_reg = self.alloc_register();
6013            self.emit(Opcode::IteratorNext);
6014            self.emit_u16(val_reg);
6015            self.emit_u16(done_reg);
6016            self.emit_u16(iter_reg);
6017
6018            self.emit(Opcode::JumpIfNot);
6019            self.emit_u16(done_reg);
6020            let not_done_skip = self.code.len();
6021            self.emit_i32(0);
6022
6023            let undef_reg = self.alloc_register();
6024            self.emit(Opcode::LoadUndefined);
6025            self.emit_u16(undef_reg);
6026            self.emit(Opcode::Move);
6027            self.emit_u16(val_reg);
6028            self.emit_u16(undef_reg);
6029            self.free_register(undef_reg);
6030
6031            let after_undef = self.code.len();
6032            self.patch_jump(not_done_skip, after_undef);
6033            self.free_register(done_reg);
6034
6035            if let Some(elem) = elem {
6036                match elem {
6037                    PatternElement::Pattern(target) => {
6038                        self.gen_assignment_target_destructure(target, val_reg, ctx)?;
6039                    }
6040                    PatternElement::RestElement(_) => {
6041                        return Err("rest element not at end".to_string());
6042                    }
6043                    PatternElement::AssignmentPattern(ap) => {
6044                        self.gen_default_value_assignment(&ap.right, val_reg, ctx)?;
6045                        self.gen_assignment_target_destructure(&ap.left, val_reg, ctx)?;
6046                    }
6047                }
6048            }
6049            self.free_register(val_reg);
6050        }
6051        self.free_register(iter_reg);
6052        Ok(())
6053    }
6054
6055    fn gen_object_destructuring_target(
6056        &mut self,
6057        obj: &ObjectPattern,
6058        value_reg: u16,
6059        ctx: &mut JSContext,
6060    ) -> Result<(), String> {
6061        for prop in &obj.properties {
6062            match prop {
6063                ObjectPatternProperty::Property {
6064                    key,
6065                    value,
6066                    computed,
6067                    ..
6068                } => {
6069                    let key_reg = if *computed {
6070                        match key {
6071                            PropertyKey::Computed(expr) => self.gen_expression(expr, ctx)?,
6072                            _ => return Err("Expected computed key".to_string()),
6073                        }
6074                    } else {
6075                        match key {
6076                            PropertyKey::Identifier(k)
6077                            | PropertyKey::Literal(Literal::String(k, _)) => {
6078                                let idx = self.intern_and_add_const(ctx, k);
6079                                let kreg = self.alloc_register();
6080                                self.emit(Opcode::LoadConst);
6081                                self.emit_u16(kreg);
6082                                self.emit_u32(idx);
6083                                kreg
6084                            }
6085                            PropertyKey::Literal(Literal::Number(n)) => {
6086                                let kreg = self.alloc_register();
6087                                if *n >= i32::MIN as f64
6088                                    && *n <= i32::MAX as f64
6089                                    && n.fract() == 0.0
6090                                {
6091                                    self.emit_int_const(kreg, *n as i32);
6092                                } else {
6093                                    let cidx = self.add_constant(JSValue::new_float(*n));
6094                                    self.emit(Opcode::LoadConst);
6095                                    self.emit_u16(kreg);
6096                                    self.emit_u32(cidx);
6097                                }
6098                                kreg
6099                            }
6100                            _ => return Err("Invalid property key".to_string()),
6101                        }
6102                    };
6103                    let elem_reg = self.alloc_register();
6104                    self.emit(Opcode::GetProp);
6105                    self.emit_u16(elem_reg);
6106                    self.emit_u16(value_reg);
6107                    self.emit_u16(key_reg);
6108                    self.free_register(key_reg);
6109                    self.gen_assignment_target_destructure(value, elem_reg, ctx)?;
6110                    self.free_register(elem_reg);
6111                }
6112                ObjectPatternProperty::RestElement(rest) => {
6113                    let rest_reg = self.alloc_register();
6114                    self.emit(Opcode::NewObject);
6115                    self.emit_u16(rest_reg);
6116                    self.emit(Opcode::ObjectSpread);
6117                    self.emit_u16(rest_reg);
6118                    self.emit_u16(value_reg);
6119                    self.gen_assignment_target_destructure(&rest.argument, rest_reg, ctx)?;
6120                    self.free_register(rest_reg);
6121                }
6122            }
6123        }
6124        Ok(())
6125    }
6126
6127    fn gen_default_value_assignment(
6128        &mut self,
6129        default_expr: &Expression,
6130        value_reg: u16,
6131        ctx: &mut JSContext,
6132    ) -> Result<(), String> {
6133        let cmp_reg = self.alloc_register();
6134        self.emit(Opcode::LoadUndefined);
6135        self.emit_u16(cmp_reg);
6136
6137        self.emit(Opcode::StrictNeq);
6138        self.emit_u16(cmp_reg);
6139        self.emit_u16(value_reg);
6140        self.emit_u16(cmp_reg);
6141
6142        self.emit(Opcode::JumpIf);
6143        self.emit_u16(cmp_reg);
6144        let skip_patch = self.code.len();
6145        self.emit_i32(0);
6146        self.free_register(cmp_reg);
6147
6148        let default_reg = self.gen_expression(default_expr, ctx)?;
6149        if default_reg != value_reg {
6150            self.emit(Opcode::Move);
6151            self.emit_u16(value_reg);
6152            self.emit_u16(default_reg);
6153        }
6154        self.free_register(default_reg);
6155
6156        let after_pos = self.code.len();
6157        self.patch_jump(skip_patch, after_pos);
6158
6159        Ok(())
6160    }
6161
6162    fn gen_conditional_expression(
6163        &mut self,
6164        cond: &ConditionalExpression,
6165        ctx: &mut JSContext,
6166    ) -> Result<u16, String> {
6167        if self.p2_fold_profile.allow_ternary_bool() {
6168            if let Expression::Literal(Literal::Boolean(test_bool)) = &*cond.test {
6169                if *test_bool {
6170                    return self.gen_expression(&cond.consequent, ctx);
6171                }
6172                return self.gen_expression(&cond.alternate, ctx);
6173            }
6174        }
6175
6176        if self.p2_fold_profile.allow_aggressive_truthy() {
6177            if let Expression::Literal(lit) = &*cond.test {
6178                if let Some(truthy) = Self::literal_truthiness(lit) {
6179                    if truthy {
6180                        return self.gen_expression(&cond.consequent, ctx);
6181                    }
6182                    return self.gen_expression(&cond.alternate, ctx);
6183                }
6184            }
6185        }
6186
6187        let test = self.gen_expression(&cond.test, ctx)?;
6188
6189        if self.opt_branch_result_prealloc {
6190            let result = if self.reserved_slots.contains(&test) {
6191                let dst = self.alloc_register();
6192                self.emit(Opcode::Move);
6193                self.emit_u16(dst);
6194                self.emit_u16(test);
6195                dst
6196            } else {
6197                test
6198            };
6199
6200            self.emit(Opcode::JumpIfNot);
6201            self.emit_u16(test);
6202            let else_jump = self.code.len();
6203            self.emit_i32(0);
6204
6205            let cons = self.gen_expression(&cond.consequent, ctx)?;
6206            if cons != result {
6207                self.emit(Opcode::Move);
6208                self.emit_u16(result);
6209                self.emit_u16(cons);
6210                self.free_register(cons);
6211            }
6212
6213            self.emit(Opcode::Jump);
6214            let end_jump = self.code.len();
6215            self.emit_i32(0);
6216
6217            let else_pos = self.code.len();
6218            self.patch_jump(else_jump, else_pos);
6219
6220            let alt = self.gen_expression(&cond.alternate, ctx)?;
6221            if alt != result {
6222                self.emit(Opcode::Move);
6223                self.emit_u16(result);
6224                self.emit_u16(alt);
6225                self.free_register(alt);
6226            }
6227
6228            let end_pos = self.code.len();
6229            self.patch_jump(end_jump, end_pos);
6230            return Ok(result);
6231        }
6232
6233        self.emit(Opcode::JumpIfNot);
6234        self.emit_u16(test);
6235        let else_jump = self.code.len();
6236        self.emit_i32(0);
6237
6238        let cons = self.gen_expression(&cond.consequent, ctx)?;
6239
6240        let result = if self.reserved_slots.contains(&test) && cons != test {
6241            let dst = self.alloc_register();
6242            self.emit(Opcode::Move);
6243            self.emit_u16(dst);
6244            self.emit_u16(cons);
6245            self.free_register(cons);
6246            dst
6247        } else if cons != test {
6248            self.emit(Opcode::Move);
6249            self.emit_u16(test);
6250            self.emit_u16(cons);
6251            self.free_register(cons);
6252            test
6253        } else {
6254            test
6255        };
6256
6257        self.emit(Opcode::Jump);
6258        let end_jump = self.code.len();
6259        self.emit_i32(0);
6260
6261        let else_pos = self.code.len();
6262        self.patch_jump(else_jump, else_pos);
6263
6264        let alt = self.gen_expression(&cond.alternate, ctx)?;
6265        if self.reserved_slots.contains(&test) && alt != result && alt != test {
6266            self.emit(Opcode::Move);
6267            self.emit_u16(result);
6268            self.emit_u16(alt);
6269            self.free_register(alt);
6270        } else if alt != result && alt != test {
6271            self.emit(Opcode::Move);
6272            self.emit_u16(result);
6273            self.emit_u16(alt);
6274            self.free_register(alt);
6275        }
6276
6277        let end_pos = self.code.len();
6278        self.patch_jump(end_jump, end_pos);
6279
6280        Ok(result)
6281    }
6282
6283    fn gen_member_expression(
6284        &mut self,
6285        member: &MemberExpression,
6286        ctx: &mut JSContext,
6287    ) -> Result<u16, String> {
6288        let obj = self.gen_expression(&member.object, ctx)?;
6289
6290        if matches!(&*member.object, Expression::Super) && !self.in_static_method {
6291            let proto_key = self.alloc_register();
6292            let proto_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.prototype));
6293            self.emit(Opcode::LoadConst);
6294            self.emit_u16(proto_key);
6295            self.emit_u32(proto_key_idx);
6296            self.emit(Opcode::GetProp);
6297            self.emit_u16(obj);
6298            self.emit_u16(obj);
6299            self.emit_u16(proto_key);
6300            self.free_register(proto_key);
6301        }
6302        let dst = self.alloc_register();
6303        match &member.property {
6304            MemberProperty::Identifier(name) => {
6305                let atom = ctx.intern(name);
6306                self.emit(Opcode::GetNamedProp);
6307                self.emit_u16(dst);
6308                self.emit_u16(obj);
6309                self.emit_u32(atom.0);
6310            }
6311            MemberProperty::Computed(expr) => {
6312                let key = self.gen_expression(expr, ctx)?;
6313                self.emit(Opcode::GetField);
6314                self.emit_u16(dst);
6315                self.emit_u16(obj);
6316                self.emit_u16(key);
6317            }
6318            MemberProperty::PrivateIdentifier(name) => {
6319                let key_idx = self.intern_and_add_const(ctx, name);
6320                let key = self.alloc_register();
6321                self.emit(Opcode::LoadConst);
6322                self.emit_u16(key);
6323                self.emit_u32(key_idx);
6324                self.emit(Opcode::GetPrivate);
6325                self.emit_u16(dst);
6326                self.emit_u16(obj);
6327                self.emit_u16(key);
6328                self.free_register(key);
6329            }
6330        }
6331        self.free_register(obj);
6332        Ok(dst)
6333    }
6334
6335    fn gen_call_expression(
6336        &mut self,
6337        call: &CallExpression,
6338        ctx: &mut JSContext,
6339    ) -> Result<u16, String> {
6340        if self.opt_tiny_inline {
6341            if let Some(reg) = self.try_emit_tiny_inline_call(call, ctx)? {
6342                return Ok(reg);
6343            }
6344        }
6345
6346        if self.opt_fused_getprop_call {
6347            if let Expression::MemberExpression(member) = &*call.callee {
6348                if !call
6349                    .arguments
6350                    .iter()
6351                    .any(|arg| matches!(arg, Argument::Spread(_)))
6352                {
6353                    if let MemberProperty::Identifier(name) = &member.property {
6354                        let is_namespace = if let Expression::Identifier(ident) = &*member.object {
6355                            matches!(
6356                                ident.name.as_str(),
6357                                "Math"
6358                                    | "Object"
6359                                    | "JSON"
6360                                    | "Number"
6361                                    | "String"
6362                                    | "Array"
6363                                    | "RegExp"
6364                                    | "Error"
6365                                    | "Function"
6366                                    | "Promise"
6367                                    | "Symbol"
6368                                    | "Map"
6369                                    | "Set"
6370                                    | "WeakMap"
6371                                    | "WeakSet"
6372                                    | "BigInt"
6373                                    | "Reflect"
6374                                    | "Proxy"
6375                                    | "console"
6376                            )
6377                        } else {
6378                            false
6379                        };
6380
6381                        if !is_namespace {
6382                            let obj_reg = self.gen_expression(&member.object, ctx)?;
6383                            let atom = ctx.intern(name);
6384
6385                            let mut arg_regs = Vec::new();
6386                            for arg in &call.arguments {
6387                                if let Argument::Expression(expr) = arg {
6388                                    arg_regs.push(self.gen_expression(expr, ctx)?);
6389                                }
6390                            }
6391
6392                            let dst = self.alloc_register();
6393                            self.emit(Opcode::CallNamedMethod);
6394                            self.emit_u16(dst);
6395                            self.emit_u16(obj_reg);
6396                            self.emit_u32(atom.0);
6397                            self.emit_u16(arg_regs.len() as u16);
6398                            for r in &arg_regs {
6399                                self.emit_u16(*r);
6400                            }
6401
6402                            self.free_register(obj_reg);
6403                            for r in arg_regs {
6404                                self.free_register(r);
6405                            }
6406                            return Ok(dst);
6407                        }
6408                    }
6409                }
6410            }
6411        }
6412
6413        let is_self_recursive_call = matches!(&*call.callee, Expression::Identifier(id)
6414            if self.current_function_name.as_ref().map_or(false, |n| n == &id.name)
6415                && self.lookup_var_slot(&id.name).is_none()
6416                && !self.upvalue_slots.contains_key(&id.name));
6417
6418        let has_spread = call
6419            .arguments
6420            .iter()
6421            .any(|arg| matches!(arg, Argument::Spread(_)));
6422
6423        if is_self_recursive_call && !has_spread {
6424            let mut arg_regs = Vec::new();
6425            for arg in &call.arguments {
6426                if let Argument::Expression(expr) = arg {
6427                    arg_regs.push(self.gen_expression(expr, ctx)?);
6428                }
6429            }
6430            let dst = self.alloc_register();
6431            if arg_regs.len() == 1 {
6432                self.emit(Opcode::CallCurrent1);
6433                self.emit_u16(dst);
6434                self.emit_u16(arg_regs[0]);
6435            } else {
6436                self.emit(Opcode::CallCurrent);
6437                self.emit_u16(dst);
6438                self.emit_u16(arg_regs.len() as u16);
6439                for r in &arg_regs {
6440                    self.emit_u16(*r);
6441                }
6442            }
6443            for r in arg_regs {
6444                self.free_register(r);
6445            }
6446            return Ok(dst);
6447        }
6448
6449        if !has_spread {
6450            if let Expression::MemberExpression(member) = &*call.callee {
6451                if let Expression::Identifier(ident) = &*member.object {
6452                    if ident.name == "Math" {
6453                        if let MemberProperty::Identifier(name) = &member.property {
6454                            let math_op = match name.as_str() {
6455                                "sin" if call.arguments.len() == 1 => Some(Opcode::MathSin),
6456                                "cos" if call.arguments.len() == 1 => Some(Opcode::MathCos),
6457                                "sqrt" if call.arguments.len() == 1 => Some(Opcode::MathSqrt),
6458                                "abs" if call.arguments.len() == 1 => Some(Opcode::MathAbs),
6459                                "floor" if call.arguments.len() == 1 => Some(Opcode::MathFloor),
6460                                "ceil" if call.arguments.len() == 1 => Some(Opcode::MathCeil),
6461                                "round" if call.arguments.len() == 1 => Some(Opcode::MathRound),
6462                                "pow" if call.arguments.len() == 2 => Some(Opcode::MathPow),
6463                                "min" if call.arguments.len() == 2 => Some(Opcode::MathMin),
6464                                "max" if call.arguments.len() == 2 => Some(Opcode::MathMax),
6465                                _ => None,
6466                            };
6467                            if let Some(op) = math_op {
6468                                let mut arg_regs = Vec::new();
6469                                for arg in &call.arguments {
6470                                    if let Argument::Expression(expr) = arg {
6471                                        arg_regs.push(self.gen_expression(expr, ctx)?);
6472                                    }
6473                                }
6474                                let dst = self.alloc_register();
6475                                self.emit(op);
6476                                self.emit_u16(dst);
6477                                for r in &arg_regs {
6478                                    self.emit_u16(*r);
6479                                }
6480                                for r in arg_regs {
6481                                    self.free_register(r);
6482                                }
6483                                return Ok(dst);
6484                            }
6485                        }
6486                    }
6487                }
6488            }
6489        }
6490
6491        let (func, obj, namespace_obj_reg) = match &*call.callee {
6492            Expression::MemberExpression(member) => {
6493                let obj_reg = self.gen_expression(&member.object, ctx)?;
6494                let func_reg = match &member.property {
6495                    MemberProperty::Identifier(name) => {
6496                        let atom = ctx.intern(name);
6497                        let f = self.alloc_register();
6498                        self.emit(Opcode::GetNamedProp);
6499                        self.emit_u16(f);
6500                        self.emit_u16(obj_reg);
6501                        self.emit_u32(atom.0);
6502                        f
6503                    }
6504                    MemberProperty::PrivateIdentifier(name) => {
6505                        let key_idx = self.intern_and_add_const(ctx, name);
6506                        let key = self.alloc_register();
6507                        self.emit(Opcode::LoadConst);
6508                        self.emit_u16(key);
6509                        self.emit_u32(key_idx);
6510                        let f = self.alloc_register();
6511                        self.emit(Opcode::GetPrivate);
6512                        self.emit_u16(f);
6513                        self.emit_u16(obj_reg);
6514                        self.emit_u16(key);
6515                        self.free_register(key);
6516                        f
6517                    }
6518                    MemberProperty::Computed(expr) => {
6519                        let key = self.gen_expression(expr, ctx)?;
6520                        let f = self.alloc_register();
6521                        self.emit(Opcode::GetField);
6522                        self.emit_u16(f);
6523                        self.emit_u16(obj_reg);
6524                        self.emit_u16(key);
6525                        self.free_register(key);
6526                        f
6527                    }
6528                };
6529                let is_ns = matches!(&*member.object, Expression::Identifier(ident)
6530                if matches!(ident.name.as_str(),
6531                    "Math" | "JSON" | "Reflect" | "console"
6532                ));
6533                if is_ns {
6534                    (func_reg, None, Some(obj_reg))
6535                } else {
6536                    (func_reg, Some(obj_reg), None)
6537                }
6538            }
6539            other => {
6540                let func_reg = self.gen_expression(other, ctx)?;
6541                (func_reg, None, None)
6542            }
6543        };
6544
6545        let dst = self.alloc_register();
6546        if has_spread {
6547            let arr_reg = self.alloc_register();
6548            self.emit(Opcode::NewArray);
6549            self.emit_u16(arr_reg);
6550            self.emit_u16(0);
6551            for arg in &call.arguments {
6552                match arg {
6553                    Argument::Expression(expr) => {
6554                        let val = self.gen_expression(expr, ctx)?;
6555                        self.emit(Opcode::ArrayPush);
6556                        self.emit_u16(arr_reg);
6557                        self.emit_u16(val);
6558                        self.free_register(val);
6559                    }
6560                    Argument::Spread(expr) => {
6561                        let val = self.gen_expression(expr, ctx)?;
6562                        self.emit(Opcode::ArrayExtend);
6563                        self.emit_u16(arr_reg);
6564                        self.emit_u16(val);
6565                        self.free_register(val);
6566                    }
6567                }
6568            }
6569            if let Some(obj_reg) = obj {
6570                self.emit(Opcode::CallMethodSpread);
6571                self.emit_u16(dst);
6572                self.emit_u16(obj_reg);
6573                self.emit_u16(func);
6574                self.emit_u16(arr_reg);
6575            } else {
6576                self.emit(Opcode::CallSpread);
6577                self.emit_u16(dst);
6578                self.emit_u16(func);
6579                self.emit_u16(arr_reg);
6580            }
6581            if let Some(obj_reg) = obj {
6582                self.free_register(obj_reg);
6583            }
6584            if let Some(reg) = namespace_obj_reg {
6585                self.free_register(reg);
6586            }
6587            self.free_register(func);
6588            self.free_register(arr_reg);
6589        } else {
6590            let mut arg_regs = Vec::new();
6591            for arg in &call.arguments {
6592                match arg {
6593                    Argument::Expression(expr) => {
6594                        arg_regs.push(self.gen_expression(expr, ctx)?);
6595                    }
6596                    _ => {}
6597                }
6598            }
6599            if let Some(obj_reg) = obj {
6600                self.emit(Opcode::CallMethod);
6601                self.emit_u16(dst);
6602                self.emit_u16(obj_reg);
6603                self.emit_u16(func);
6604                self.emit_u16(arg_regs.len() as u16);
6605                for r in &arg_regs {
6606                    self.emit_u16(*r);
6607                }
6608            } else {
6609                match arg_regs.len() {
6610                    0 => {
6611                        self.emit(Opcode::Call0);
6612                        self.emit_u16(dst);
6613                        self.emit_u16(func);
6614                    }
6615                    1 => {
6616                        self.emit(Opcode::Call1);
6617                        self.emit_u16(dst);
6618                        self.emit_u16(func);
6619                        self.emit_u16(arg_regs[0]);
6620                    }
6621                    2 => {
6622                        if self.opt_call23 {
6623                            self.emit(Opcode::Call2);
6624                            self.emit_u16(dst);
6625                            self.emit_u16(func);
6626                            self.emit_u16(arg_regs[0]);
6627                            self.emit_u16(arg_regs[1]);
6628                        } else {
6629                            self.emit(Opcode::Call);
6630                            self.emit_u16(dst);
6631                            self.emit_u16(func);
6632                            self.emit_u16(2);
6633                            self.emit_u16(arg_regs[0]);
6634                            self.emit_u16(arg_regs[1]);
6635                        }
6636                    }
6637                    3 => {
6638                        if self.opt_call23 {
6639                            self.emit(Opcode::Call3);
6640                            self.emit_u16(dst);
6641                            self.emit_u16(func);
6642                            self.emit_u16(arg_regs[0]);
6643                            self.emit_u16(arg_regs[1]);
6644                            self.emit_u16(arg_regs[2]);
6645                        } else {
6646                            self.emit(Opcode::Call);
6647                            self.emit_u16(dst);
6648                            self.emit_u16(func);
6649                            self.emit_u16(3);
6650                            self.emit_u16(arg_regs[0]);
6651                            self.emit_u16(arg_regs[1]);
6652                            self.emit_u16(arg_regs[2]);
6653                        }
6654                    }
6655                    _ => {
6656                        self.emit(Opcode::Call);
6657                        self.emit_u16(dst);
6658                        self.emit_u16(func);
6659                        self.emit_u16(arg_regs.len() as u16);
6660                        for r in &arg_regs {
6661                            self.emit_u16(*r);
6662                        }
6663                    }
6664                }
6665            }
6666            if let Some(obj_reg) = obj {
6667                self.free_register(obj_reg);
6668            }
6669            if let Some(reg) = namespace_obj_reg {
6670                self.free_register(reg);
6671            }
6672            self.free_register(func);
6673            for r in arg_regs {
6674                self.free_register(r);
6675            }
6676        }
6677        Ok(dst)
6678    }
6679
6680    fn gen_new_expression(
6681        &mut self,
6682        new: &NewExpression,
6683        ctx: &mut JSContext,
6684    ) -> Result<u16, String> {
6685        let func = self.gen_expression(&new.callee, ctx)?;
6686        let has_spread = new
6687            .arguments
6688            .iter()
6689            .any(|arg| matches!(arg, Argument::Spread(_)));
6690        let dst = self.alloc_register();
6691        if has_spread {
6692            let arr_reg = self.alloc_register();
6693            self.emit(Opcode::NewArray);
6694            self.emit_u16(arr_reg);
6695            self.emit_u16(0);
6696            for arg in &new.arguments {
6697                match arg {
6698                    Argument::Expression(expr) => {
6699                        let val = self.gen_expression(expr, ctx)?;
6700                        self.emit(Opcode::ArrayPush);
6701                        self.emit_u16(arr_reg);
6702                        self.emit_u16(val);
6703                        self.free_register(val);
6704                    }
6705                    Argument::Spread(expr) => {
6706                        let val = self.gen_expression(expr, ctx)?;
6707                        self.emit(Opcode::ArrayExtend);
6708                        self.emit_u16(arr_reg);
6709                        self.emit_u16(val);
6710                        self.free_register(val);
6711                    }
6712                }
6713            }
6714            self.emit(Opcode::CallNewSpread);
6715            self.emit_u16(dst);
6716            self.emit_u16(func);
6717            self.emit_u16(arr_reg);
6718            self.free_register(func);
6719            self.free_register(arr_reg);
6720        } else {
6721            let mut arg_regs = Vec::new();
6722            for arg in &new.arguments {
6723                match arg {
6724                    Argument::Expression(expr) => {
6725                        arg_regs.push(self.gen_expression(expr, ctx)?);
6726                    }
6727                    _ => {}
6728                }
6729            }
6730            self.emit(Opcode::CallNew);
6731            self.emit_u16(dst);
6732            self.emit_u16(func);
6733            self.emit_u16(arg_regs.len() as u16);
6734            for r in &arg_regs {
6735                self.emit_u16(*r);
6736            }
6737            self.free_register(func);
6738            for r in arg_regs {
6739                self.free_register(r);
6740            }
6741        }
6742        Ok(dst)
6743    }
6744
6745    fn gen_array_expression(
6746        &mut self,
6747        arr: &ArrayExpression,
6748        ctx: &mut JSContext,
6749    ) -> Result<u16, String> {
6750        let dst = self.alloc_register();
6751        let count = arr.elements.len() as u16;
6752        self.emit(Opcode::NewArray);
6753        self.emit_u16(dst);
6754        self.emit_u16(count);
6755
6756        let has_holes = arr.elements.iter().any(|e| e.is_none());
6757        let has_spread = arr
6758            .elements
6759            .iter()
6760            .any(|e| matches!(e, Some(ArrayElement::Spread(_))));
6761
6762        if !has_holes && !has_spread {
6763            for elem in &arr.elements {
6764                if let Some(ArrayElement::Expression(expr)) = elem {
6765                    let val = self.gen_expression(expr, ctx)?;
6766                    self.emit(Opcode::ArrayPush);
6767                    self.emit_u16(dst);
6768                    self.emit_u16(val);
6769                    self.free_register(val);
6770                }
6771            }
6772            return Ok(dst);
6773        }
6774
6775        let idx_reg = self.alloc_register();
6776        self.emit_int_const(idx_reg, 0);
6777
6778        for elem in &arr.elements {
6779            match elem {
6780                Some(ArrayElement::Expression(expr)) => {
6781                    let val = self.gen_expression(expr, ctx)?;
6782                    self.emit(Opcode::SetField);
6783                    self.emit_u16(dst);
6784                    self.emit_u16(idx_reg);
6785                    self.emit_u16(val);
6786                    self.free_register(val);
6787                    self.emit(Opcode::IncLocal);
6788                    self.emit_u16(idx_reg);
6789                }
6790                None => {
6791                    self.emit(Opcode::IncLocal);
6792                    self.emit_u16(idx_reg);
6793                }
6794                Some(ArrayElement::Spread(expr)) => {
6795                    let src = self.gen_expression(expr, ctx)?;
6796
6797                    let iter_reg = self.alloc_register();
6798                    self.emit(Opcode::GetIterator);
6799                    self.emit_u16(iter_reg);
6800                    self.emit_u16(src);
6801                    self.free_register(src);
6802
6803                    let loop_start = self.code.len();
6804
6805                    let val_reg = self.alloc_register();
6806                    let done_reg = self.alloc_register();
6807                    self.emit(Opcode::IteratorNext);
6808                    self.emit_u16(val_reg);
6809                    self.emit_u16(done_reg);
6810                    self.emit_u16(iter_reg);
6811
6812                    self.emit(Opcode::JumpIf);
6813                    self.emit_u16(done_reg);
6814                    let exit_patch = self.code.len();
6815                    self.emit_i32(0);
6816                    self.free_register(done_reg);
6817
6818                    self.emit(Opcode::ArrayPush);
6819                    self.emit_u16(dst);
6820                    self.emit_u16(val_reg);
6821                    self.free_register(val_reg);
6822
6823                    self.emit_backward_jump(loop_start);
6824
6825                    let end_pos = self.code.len();
6826                    self.patch_jump(exit_patch, end_pos);
6827
6828                    self.free_register(iter_reg);
6829
6830                    let len_atom = ctx.common_atoms.length;
6831                    let len_const = self.add_constant(JSValue::new_string(len_atom));
6832                    let len_key = self.alloc_register();
6833                    self.emit(Opcode::LoadConst);
6834                    self.emit_u16(len_key);
6835                    self.emit_u32(len_const);
6836                    self.emit(Opcode::GetProp);
6837                    self.emit_u16(idx_reg);
6838                    self.emit_u16(dst);
6839                    self.emit_u16(len_key);
6840                    self.free_register(len_key);
6841                }
6842            }
6843        }
6844        let len_atom = ctx.common_atoms.length;
6845        let len_const = self.add_constant(JSValue::new_string(len_atom));
6846        let len_key = self.alloc_register();
6847        self.emit(Opcode::LoadConst);
6848        self.emit_u16(len_key);
6849        self.emit_u32(len_const);
6850        self.emit(Opcode::SetProp);
6851        self.emit_u16(dst);
6852        self.emit_u16(len_key);
6853        self.emit_u16(idx_reg);
6854        self.free_register(len_key);
6855        self.free_register(idx_reg);
6856        Ok(dst)
6857    }
6858
6859    fn gen_object_expression(
6860        &mut self,
6861        obj: &ObjectExpression,
6862        ctx: &mut JSContext,
6863    ) -> Result<u16, String> {
6864        let dst = self.alloc_register();
6865        self.emit(Opcode::NewObject);
6866        self.emit_u16(dst);
6867
6868        for prop in &obj.properties {
6869            match prop {
6870                Property::Property {
6871                    key,
6872                    value,
6873                    getter,
6874                    setter,
6875                    ..
6876                } => {
6877                    if *getter || *setter {
6878                        let func_reg = self.gen_expression(value, ctx)?;
6879                        let key_reg = match key {
6880                            PropertyKey::Identifier(name)
6881                            | PropertyKey::Literal(Literal::String(name, _)) => {
6882                                let atom = ctx.intern(name);
6883                                let idx = self.add_constant(JSValue::new_string(atom));
6884                                let k = self.alloc_register();
6885                                self.emit(Opcode::LoadConst);
6886                                self.emit_u16(k);
6887                                self.emit_u32(idx);
6888                                k
6889                            }
6890                            PropertyKey::Computed(expr) => self.gen_expression(expr, ctx)?,
6891                            _ => {
6892                                self.free_register(func_reg);
6893                                continue;
6894                            }
6895                        };
6896                        let getter_reg = if *getter { func_reg } else { u16::MAX };
6897                        let setter_reg = if *setter { func_reg } else { u16::MAX };
6898                        self.emit(Opcode::DefineAccessor);
6899                        self.emit_u16(dst);
6900                        self.emit_u16(key_reg);
6901                        self.emit_u16(getter_reg);
6902                        self.emit_u16(setter_reg);
6903                        self.free_register(key_reg);
6904                        self.free_register(func_reg);
6905                        continue;
6906                    }
6907                    match key {
6908                        PropertyKey::Identifier(name)
6909                        | PropertyKey::Literal(Literal::String(name, _)) => {
6910                            let val = self.gen_expression(value, ctx)?;
6911                            let atom = ctx.intern(name);
6912                            self.emit(Opcode::SetNamedProp);
6913                            self.emit_u16(dst);
6914                            self.emit_u16(val);
6915                            self.emit_u32(atom.0);
6916                            self.free_register(val);
6917                        }
6918                        _ => {
6919                            let key_reg = match key {
6920                                PropertyKey::Literal(Literal::Number(n)) => {
6921                                    let k = self.alloc_register();
6922                                    let nv = *n;
6923                                    if nv >= i32::MIN as f64
6924                                        && nv <= i32::MAX as f64
6925                                        && nv.fract() == 0.0
6926                                    {
6927                                        self.emit_int_const(k, nv as i32);
6928                                    } else {
6929                                        let idx = self.add_constant(JSValue::new_float(nv));
6930                                        self.emit(Opcode::LoadConst);
6931                                        self.emit_u16(k);
6932                                        self.emit_u32(idx);
6933                                    }
6934                                    k
6935                                }
6936                                PropertyKey::Computed(expr) => self.gen_expression(expr, ctx)?,
6937                                _ => self.alloc_register(),
6938                            };
6939                            let val = self.gen_expression(value, ctx)?;
6940                            self.emit(Opcode::SetField);
6941                            self.emit_u16(dst);
6942                            self.emit_u16(key_reg);
6943                            self.emit_u16(val);
6944                            self.free_register(key_reg);
6945                            self.free_register(val);
6946                        }
6947                    }
6948                }
6949                Property::SpreadElement(expr) => {
6950                    let src = self.gen_expression(expr, ctx)?;
6951                    self.emit(Opcode::ObjectSpread);
6952                    self.emit_u16(dst);
6953                    self.emit_u16(src);
6954                    self.free_register(src);
6955                }
6956            }
6957        }
6958        Ok(dst)
6959    }
6960
6961    fn gen_function_expression(
6962        &mut self,
6963        params: &[Parameter],
6964        body: &BlockStatement,
6965        name: Option<&str>,
6966        ctx: &mut JSContext,
6967        is_generator: bool,
6968        is_async: bool,
6969        is_arrow: bool,
6970    ) -> Result<u16, String> {
6971        let mut nested_codegen = self.nested_codegen();
6972        nested_codegen.is_arrow = is_arrow;
6973        let mut parent_vars: FxHashMap<String, ParentVar> = FxHashMap::default();
6974        for (scope_idx, scope) in self.scope_stack.iter().enumerate() {
6975            if self
6976                .scope_is_global
6977                .get(scope_idx)
6978                .copied()
6979                .unwrap_or(false)
6980            {
6981                continue;
6982            }
6983            for (name, entry) in scope {
6984                // Skip pre-declared uninitialized variables from the root scope.
6985                // These are created by pre_scan_let_const before the actual let
6986                // statement, causing closures to incorrectly emit CheckTdz.
6987                if scope_idx == 0 && !entry.initialized {
6988                    continue;
6989                }
6990                parent_vars.insert(
6991                    name.clone(),
6992                    ParentVar {
6993                        local_idx: entry.slot,
6994                        kind: entry.kind,
6995                        initialized: entry.initialized,
6996                        is_inherited: false,
6997                        closed: false,
6998                    },
6999                );
7000            }
7001        }
7002        for (name, &slot) in &self.upvalue_slots {
7003            if !parent_vars.contains_key(name) {
7004                parent_vars.insert(
7005                    name.clone(),
7006                    ParentVar {
7007                        local_idx: slot,
7008                        kind: VariableKind::Let,
7009                        initialized: true,
7010                        is_inherited: true,
7011                        closed: false,
7012                    },
7013                );
7014            }
7015        }
7016        // Include variables from closed scopes (popped from scope_stack but
7017        // still in parent_vars with the closed flag). These are out-of-scope
7018        // let/const variables that closures should NOT access.
7019        for (name, pv) in &self.parent_vars {
7020            if pv.closed && !parent_vars.contains_key(name) {
7021                parent_vars.insert(name.clone(), *pv);
7022            }
7023        }
7024        let (bytecode, upvalues) = nested_codegen
7025            .compile_function_with_parent(params, body, ctx, parent_vars, name, false)
7026            .map_err(|e| format!("nested function compile error: {}", e))?;
7027
7028        let func_name_atom = name.map(|n| ctx.atom_table_mut().intern(n).0).unwrap_or(0);
7029        let dst = if let Some(name) = name {
7030            self.lookup_var_slot(name)
7031                .unwrap_or_else(|| self.alloc_register())
7032        } else {
7033            self.alloc_register()
7034        };
7035        self.emit_embedded_function(
7036            &bytecode,
7037            &upvalues,
7038            func_name_atom,
7039            dst,
7040            is_generator,
7041            is_async,
7042        );
7043        Ok(dst)
7044    }
7045
7046    fn gen_class_declaration(
7047        &mut self,
7048        class: &ClassDeclaration,
7049        ctx: &mut JSContext,
7050    ) -> Result<(), String> {
7051        let class_name = &class.name;
7052        let class_slot = self
7053            .lookup_var_slot(class_name)
7054            .unwrap_or_else(|| self.declare_var(class_name, VariableKind::Let));
7055        let class_name_atom =
7056            self.gen_class_body(class_slot, class_name, &class.super_class, &class.body, ctx)?;
7057        if self.is_global_scope() && !self.is_eval {
7058            let class_idx = self.add_constant(JSValue::new_string(class_name_atom));
7059            self.emit(Opcode::SetGlobal);
7060            self.emit_u32(class_idx);
7061            self.emit_u16(class_slot);
7062        }
7063        Ok(())
7064    }
7065
7066    fn gen_class_body(
7067        &mut self,
7068        class_slot: u16,
7069        class_name: &str,
7070        super_class: &Option<Box<Expression>>,
7071        body: &ClassBody,
7072        ctx: &mut JSContext,
7073    ) -> Result<crate::runtime::atom::Atom, String> {
7074        let has_super = super_class.is_some();
7075
7076        let ctor_method = body.elements.iter().find(|e| {
7077            if let ClassElement::Method(m) = e {
7078                m.kind == MethodKind::Constructor && !m.is_static
7079            } else {
7080                false
7081            }
7082        });
7083
7084        let instance_fields: Vec<_> = body
7085            .elements
7086            .iter()
7087            .filter_map(|e| {
7088                if let ClassElement::Property(p) = e {
7089                    if !p.is_static {
7090                        return Some(p.clone());
7091                    }
7092                }
7093                None
7094            })
7095            .collect();
7096
7097        let field_init_stmts: Vec<ASTNode> = instance_fields
7098            .iter()
7099            .filter_map(|f| {
7100                let value = f
7101                    .value
7102                    .clone()
7103                    .unwrap_or(Expression::Literal(Literal::Undefined));
7104                let (property, computed) = match &f.name {
7105                    PropertyKey::Identifier(n) if f.is_private => {
7106                        (MemberProperty::PrivateIdentifier(n.clone()), false)
7107                    }
7108                    PropertyKey::Identifier(n) => (MemberProperty::Identifier(n.clone()), false),
7109                    PropertyKey::Literal(Literal::String(s, _)) => {
7110                        (MemberProperty::Identifier(s.clone()), false)
7111                    }
7112                    PropertyKey::PrivateIdentifier(n) => {
7113                        (MemberProperty::PrivateIdentifier(n.clone()), false)
7114                    }
7115                    PropertyKey::Computed(expr) => (MemberProperty::Computed(expr.clone()), true),
7116                    _ => return None,
7117                };
7118                Some(ASTNode::ExpressionStatement(ExpressionStatement {
7119                    expression: Expression::AssignmentExpression(AssignmentExpression {
7120                        op: AssignOp::Assign,
7121                        left: AssignmentTarget::MemberExpression(MemberExpression {
7122                            object: Box::new(Expression::This),
7123                            property,
7124                            computed,
7125                        }),
7126                        right: Box::new(value),
7127                    }),
7128                }))
7129            })
7130            .collect();
7131
7132        let mut class_parent_vars = FxHashMap::default();
7133
7134        for (scope_idx, scope) in self.scope_stack.iter().enumerate() {
7135            if self
7136                .scope_is_global
7137                .get(scope_idx)
7138                .copied()
7139                .unwrap_or(false)
7140            {
7141                continue;
7142            }
7143            for (var_name, entry) in scope {
7144                // Skip pre-declared uninitialized variables from the root scope
7145                if scope_idx == 0 && !entry.initialized {
7146                    continue;
7147                }
7148                class_parent_vars.insert(
7149                    var_name.clone(),
7150                    ParentVar {
7151                        local_idx: entry.slot,
7152                        kind: entry.kind,
7153                        initialized: entry.initialized,
7154                        is_inherited: false,
7155                        closed: false,
7156                    },
7157                );
7158            }
7159        }
7160
7161        for (var_name, &slot) in &self.upvalue_slots {
7162            if !class_parent_vars.contains_key(var_name) {
7163                class_parent_vars.insert(
7164                    var_name.clone(),
7165                    ParentVar {
7166                        local_idx: slot,
7167                        kind: VariableKind::Let,
7168                        initialized: true,
7169                        is_inherited: true,
7170                        closed: false,
7171                    },
7172                );
7173            }
7174        }
7175
7176        if !class_name.is_empty() {
7177            class_parent_vars.insert(
7178                class_name.to_string(),
7179                ParentVar {
7180                    local_idx: class_slot,
7181                    kind: VariableKind::Let,
7182                    initialized: true,
7183                    is_inherited: false,
7184                    closed: false,
7185                },
7186            );
7187        }
7188
7189        let (ctor_bytecode, ctor_upvalues) = if let Some(ClassElement::Method(m)) = ctor_method {
7190            let mut body = m.body.clone();
7191            let insert_pos = 0;
7192            for (i, stmt) in field_init_stmts.into_iter().enumerate() {
7193                body.body.insert(insert_pos + i, stmt);
7194                body.lines.insert(insert_pos + i, 0);
7195            }
7196            let mut nested = self.nested_codegen();
7197            nested.compile_function_with_parent(
7198                &m.params,
7199                &body,
7200                ctx,
7201                class_parent_vars.clone(),
7202                None,
7203                false,
7204            )?
7205        } else {
7206            let mut default_body = Vec::new();
7207            let mut default_lines = Vec::new();
7208            if has_super {
7209                let super_call = ASTNode::ExpressionStatement(ExpressionStatement {
7210                    expression: Expression::CallExpression(CallExpression {
7211                        callee: Box::new(Expression::Super),
7212                        arguments: vec![Argument::Spread(Expression::Identifier(Identifier {
7213                            name: "arguments".to_string(),
7214                        }))],
7215                    }),
7216                });
7217                default_body.push(super_call);
7218                default_lines.push(0);
7219            }
7220            default_body.extend(field_init_stmts);
7221            default_lines.extend(vec![0; instance_fields.len()]);
7222            let mut nested = self.nested_codegen();
7223            nested.compile_function_with_parent(
7224                &[],
7225                &BlockStatement {
7226                    body: default_body,
7227                    lines: default_lines,
7228                },
7229                ctx,
7230                class_parent_vars.clone(),
7231                None,
7232                false,
7233            )?
7234        };
7235
7236        let mut instance_methods: Vec<(
7237            PropertyKey,
7238            Bytecode,
7239            Vec<(u32, u16)>,
7240            bool,
7241            bool,
7242            MethodKind,
7243            bool,
7244        )> = Vec::new();
7245        for element in &body.elements {
7246            if let ClassElement::Method(m) = element {
7247                if m.is_static || m.kind == MethodKind::Constructor {
7248                    continue;
7249                }
7250                let can_handle = match &m.name {
7251                    PropertyKey::Identifier(_)
7252                    | PropertyKey::Literal(Literal::String(_, _))
7253                    | PropertyKey::Literal(Literal::Number(_))
7254                    | PropertyKey::PrivateIdentifier(_)
7255                    | PropertyKey::Computed(_) => true,
7256                    _ => false,
7257                };
7258                if !can_handle {
7259                    continue;
7260                }
7261                let mut nested = self.nested_codegen();
7262                let (method_bc, method_upvalues) = nested.compile_function_with_parent(
7263                    &m.params,
7264                    &m.body,
7265                    ctx,
7266                    class_parent_vars.clone(),
7267                    None,
7268                    false,
7269                )?;
7270                instance_methods.push((
7271                    m.name.clone(),
7272                    method_bc,
7273                    method_upvalues,
7274                    m.generator,
7275                    m.is_async,
7276                    m.kind.clone(),
7277                    m.is_private,
7278                ));
7279            }
7280        }
7281
7282        let mut static_methods: Vec<(
7283            PropertyKey,
7284            Bytecode,
7285            Vec<(u32, u16)>,
7286            bool,
7287            bool,
7288            MethodKind,
7289            bool,
7290        )> = Vec::new();
7291        for element in &body.elements {
7292            if let ClassElement::Method(m) = element {
7293                if !m.is_static {
7294                    continue;
7295                }
7296                let can_handle = match &m.name {
7297                    PropertyKey::Identifier(_)
7298                    | PropertyKey::Literal(Literal::String(_, _))
7299                    | PropertyKey::Literal(Literal::Number(_))
7300                    | PropertyKey::PrivateIdentifier(_)
7301                    | PropertyKey::Computed(_) => true,
7302                    _ => false,
7303                };
7304                if !can_handle {
7305                    continue;
7306                }
7307                let mut nested = self.nested_codegen();
7308                nested.in_static_method = true;
7309                let (method_bc, method_upvalues) = nested.compile_function_with_parent(
7310                    &m.params,
7311                    &m.body,
7312                    ctx,
7313                    class_parent_vars.clone(),
7314                    None,
7315                    false,
7316                )?;
7317                static_methods.push((
7318                    m.name.clone(),
7319                    method_bc,
7320                    method_upvalues,
7321                    m.generator,
7322                    m.is_async,
7323                    m.kind.clone(),
7324                    m.is_private,
7325                ));
7326            }
7327        }
7328
7329        let mut static_blocks: Vec<(Bytecode, Vec<(u32, u16)>)> = Vec::new();
7330        for element in &body.elements {
7331            if let ClassElement::StaticBlock(block) = element {
7332                let mut nested = self.nested_codegen();
7333                let (block_bc, block_upvalues) = nested.compile_function_with_parent(
7334                    &[],
7335                    &BlockStatement {
7336                        body: block.body.clone(),
7337                        lines: block.lines.clone(),
7338                    },
7339                    ctx,
7340                    class_parent_vars.clone(),
7341                    None,
7342                    false,
7343                )?;
7344                static_blocks.push((block_bc, block_upvalues));
7345            }
7346        }
7347
7348        let ctor_reg = self.alloc_register();
7349        let class_name_atom = ctx.atom_table_mut().intern(class_name);
7350        let ctor_name_atom = class_name_atom.0;
7351        self.emit_embedded_function(
7352            &ctor_bytecode,
7353            &ctor_upvalues,
7354            ctor_name_atom,
7355            ctor_reg,
7356            false,
7357            false,
7358        );
7359
7360        if let Some(super_expr) = super_class {
7361            let super_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.__super__));
7362            let super_key = self.alloc_register();
7363            self.emit(Opcode::LoadConst);
7364            self.emit_u16(super_key);
7365            self.emit_u32(super_key_idx);
7366            let super_val = self.gen_expression(super_expr, ctx)?;
7367            self.emit(Opcode::SetProp);
7368            self.emit_u16(ctor_reg);
7369            self.emit_u16(super_key);
7370            self.emit_u16(super_val);
7371            self.free_register(super_key);
7372            self.free_register(super_val);
7373        }
7374
7375        if class_slot != ctor_reg {
7376            self.emit(Opcode::Move);
7377            self.emit_u16(class_slot);
7378            self.emit_u16(ctor_reg);
7379        }
7380
7381        let proto_reg = self.alloc_register();
7382        self.emit(Opcode::NewObject);
7383        self.emit_u16(proto_reg);
7384
7385        if let Some(super_expr) = super_class {
7386            let proto_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.prototype));
7387            let proto_key = self.alloc_register();
7388            self.emit(Opcode::LoadConst);
7389            self.emit_u16(proto_key);
7390            self.emit_u32(proto_key_idx);
7391            let super_val = self.gen_expression(super_expr, ctx)?;
7392            let super_proto = self.alloc_register();
7393            self.emit(Opcode::GetProp);
7394            self.emit_u16(super_proto);
7395            self.emit_u16(super_val);
7396            self.emit_u16(proto_key);
7397            self.free_register(super_val);
7398            self.emit(Opcode::SetProto);
7399            self.emit_u16(proto_reg);
7400            self.emit_u16(super_proto);
7401            self.free_register(super_proto);
7402            self.free_register(proto_key);
7403        }
7404
7405        for (method_key, method_bc, method_upvalues, is_gen, is_async, kind, is_private) in
7406            instance_methods
7407        {
7408            let key = self.alloc_register();
7409            let method_name_atom = match &method_key {
7410                PropertyKey::Identifier(n) | PropertyKey::Literal(Literal::String(n, _)) => {
7411                    let method_atom = ctx.atom_table_mut().intern(n.as_str());
7412                    let method_idx = self.add_constant(JSValue::new_string(method_atom));
7413                    self.emit(Opcode::LoadConst);
7414                    self.emit_u16(key);
7415                    self.emit_u32(method_idx);
7416                    method_atom.0
7417                }
7418                PropertyKey::Literal(Literal::Number(n)) => {
7419                    let s = if n.fract() == 0.0 && *n >= i32::MIN as f64 && *n <= i32::MAX as f64 {
7420                        (*n as i32).to_string()
7421                    } else {
7422                        f64_to_js_string(*n)
7423                    };
7424                    let method_atom = ctx.atom_table_mut().intern(&s);
7425                    let method_idx = self.add_constant(JSValue::new_string(method_atom));
7426                    self.emit(Opcode::LoadConst);
7427                    self.emit_u16(key);
7428                    self.emit_u32(method_idx);
7429                    method_atom.0
7430                }
7431                PropertyKey::PrivateIdentifier(n) => {
7432                    let method_atom = ctx.atom_table_mut().intern(n.as_str());
7433                    let method_idx = self.add_constant(JSValue::new_string(method_atom));
7434                    self.emit(Opcode::LoadConst);
7435                    self.emit_u16(key);
7436                    self.emit_u32(method_idx);
7437                    method_atom.0
7438                }
7439                PropertyKey::Computed(expr) => {
7440                    let k = self.gen_expression(expr, ctx)?;
7441                    self.emit(Opcode::Move);
7442                    self.emit_u16(key);
7443                    self.emit_u16(k);
7444                    self.free_register(k);
7445                    0
7446                }
7447                _ => {
7448                    self.free_register(key);
7449                    continue;
7450                }
7451            };
7452            let method_reg = self.alloc_register();
7453            self.emit_embedded_function(
7454                &method_bc,
7455                &method_upvalues,
7456                method_name_atom,
7457                method_reg,
7458                is_gen,
7459                is_async,
7460            );
7461            match kind {
7462                MethodKind::Get if is_private => {
7463                    self.emit(Opcode::DefinePrivateAccessor);
7464                    self.emit_u16(proto_reg);
7465                    self.emit_u16(key);
7466                    self.emit_u16(method_reg);
7467                    self.emit_u16(u16::MAX);
7468                }
7469                MethodKind::Set if is_private => {
7470                    self.emit(Opcode::DefinePrivateAccessor);
7471                    self.emit_u16(proto_reg);
7472                    self.emit_u16(key);
7473                    self.emit_u16(u16::MAX);
7474                    self.emit_u16(method_reg);
7475                }
7476                MethodKind::Get => {
7477                    self.emit(Opcode::DefineAccessor);
7478                    self.emit_u16(proto_reg);
7479                    self.emit_u16(key);
7480                    self.emit_u16(method_reg);
7481                    self.emit_u16(u16::MAX);
7482                }
7483                MethodKind::Set => {
7484                    self.emit(Opcode::DefineAccessor);
7485                    self.emit_u16(proto_reg);
7486                    self.emit_u16(key);
7487                    self.emit_u16(u16::MAX);
7488                    self.emit_u16(method_reg);
7489                }
7490                _ => {
7491                    if is_private {
7492                        self.emit(Opcode::SetPrivate);
7493                        self.emit_u16(proto_reg);
7494                        self.emit_u16(key);
7495                        self.emit_u16(method_reg);
7496                    } else {
7497                        self.emit(Opcode::SetMethodProp);
7498                        self.emit_u16(proto_reg);
7499                        self.emit_u16(key);
7500                        self.emit_u16(method_reg);
7501                    }
7502                }
7503            }
7504            self.free_register(key);
7505            self.free_register(method_reg);
7506        }
7507
7508        for (method_key, method_bc, method_upvalues, is_gen, is_async, kind, is_private) in
7509            static_methods
7510        {
7511            let key = self.alloc_register();
7512            let method_name_atom = match &method_key {
7513                PropertyKey::Identifier(n) | PropertyKey::Literal(Literal::String(n, _)) => {
7514                    let method_atom = ctx.atom_table_mut().intern(n.as_str());
7515                    let method_idx = self.add_constant(JSValue::new_string(method_atom));
7516                    self.emit(Opcode::LoadConst);
7517                    self.emit_u16(key);
7518                    self.emit_u32(method_idx);
7519                    method_atom.0
7520                }
7521                PropertyKey::Literal(Literal::Number(n)) => {
7522                    let s = if n.fract() == 0.0 && *n >= i32::MIN as f64 && *n <= i32::MAX as f64 {
7523                        (*n as i32).to_string()
7524                    } else {
7525                        f64_to_js_string(*n)
7526                    };
7527                    let method_atom = ctx.atom_table_mut().intern(&s);
7528                    let method_idx = self.add_constant(JSValue::new_string(method_atom));
7529                    self.emit(Opcode::LoadConst);
7530                    self.emit_u16(key);
7531                    self.emit_u32(method_idx);
7532                    method_atom.0
7533                }
7534                PropertyKey::PrivateIdentifier(n) => {
7535                    let method_atom = ctx.atom_table_mut().intern(n.as_str());
7536                    let method_idx = self.add_constant(JSValue::new_string(method_atom));
7537                    self.emit(Opcode::LoadConst);
7538                    self.emit_u16(key);
7539                    self.emit_u32(method_idx);
7540                    method_atom.0
7541                }
7542                PropertyKey::Computed(expr) => {
7543                    let k = self.gen_expression(expr, ctx)?;
7544                    self.emit(Opcode::Move);
7545                    self.emit_u16(key);
7546                    self.emit_u16(k);
7547                    self.free_register(k);
7548                    0
7549                }
7550                _ => {
7551                    self.free_register(key);
7552                    continue;
7553                }
7554            };
7555            let method_reg = self.alloc_register();
7556            self.emit_embedded_function(
7557                &method_bc,
7558                &method_upvalues,
7559                method_name_atom,
7560                method_reg,
7561                is_gen,
7562                is_async,
7563            );
7564            match kind {
7565                MethodKind::Get if is_private => {
7566                    self.emit(Opcode::DefinePrivateAccessor);
7567                    self.emit_u16(class_slot);
7568                    self.emit_u16(key);
7569                    self.emit_u16(method_reg);
7570                    self.emit_u16(u16::MAX);
7571                }
7572                MethodKind::Set if is_private => {
7573                    self.emit(Opcode::DefinePrivateAccessor);
7574                    self.emit_u16(class_slot);
7575                    self.emit_u16(key);
7576                    self.emit_u16(u16::MAX);
7577                    self.emit_u16(method_reg);
7578                }
7579                MethodKind::Get => {
7580                    self.emit(Opcode::DefineAccessor);
7581                    self.emit_u16(class_slot);
7582                    self.emit_u16(key);
7583                    self.emit_u16(method_reg);
7584                    self.emit_u16(u16::MAX);
7585                }
7586                MethodKind::Set => {
7587                    self.emit(Opcode::DefineAccessor);
7588                    self.emit_u16(class_slot);
7589                    self.emit_u16(key);
7590                    self.emit_u16(u16::MAX);
7591                    self.emit_u16(method_reg);
7592                }
7593                _ => {
7594                    if is_private {
7595                        self.emit(Opcode::SetPrivate);
7596                        self.emit_u16(class_slot);
7597                        self.emit_u16(key);
7598                        self.emit_u16(method_reg);
7599                    } else {
7600                        self.emit(Opcode::SetMethodProp);
7601                        self.emit_u16(class_slot);
7602                        self.emit_u16(key);
7603                        self.emit_u16(method_reg);
7604                    }
7605                }
7606            }
7607            self.free_register(key);
7608            self.free_register(method_reg);
7609        }
7610
7611        let proto_key_idx = self.add_constant(JSValue::new_string(ctx.common_atoms.prototype));
7612        let proto_key = self.alloc_register();
7613        self.emit(Opcode::LoadConst);
7614        self.emit_u16(proto_key);
7615        self.emit_u32(proto_key_idx);
7616        self.emit(Opcode::SetProp);
7617        self.emit_u16(class_slot);
7618        self.emit_u16(proto_key);
7619        self.emit_u16(proto_reg);
7620        self.free_register(proto_key);
7621        self.free_register(proto_reg);
7622
7623        for element in &body.elements {
7624            if let ClassElement::Property(p) = element {
7625                if !p.is_static {
7626                    continue;
7627                }
7628                let val_reg = if let Some(value) = &p.value {
7629                    self.gen_expression(value, ctx)?
7630                } else {
7631                    let r = self.alloc_register();
7632                    self.emit(Opcode::LoadUndefined);
7633                    self.emit_u16(r);
7634                    r
7635                };
7636                if let PropertyKey::Computed(key_expr) = &p.name {
7637                    let key = self.gen_expression(key_expr, ctx)?;
7638                    self.emit(Opcode::SetField);
7639                    self.emit_u16(class_slot);
7640                    self.emit_u16(key);
7641                    self.emit_u16(val_reg);
7642                    self.free_register(key);
7643                    self.free_register(val_reg);
7644                } else {
7645                    let field_name = match &p.name {
7646                        PropertyKey::Identifier(n) => n.clone(),
7647                        PropertyKey::Literal(Literal::String(s, _)) => s.clone(),
7648                        PropertyKey::PrivateIdentifier(n) => n.clone(),
7649                        _ => {
7650                            self.free_register(val_reg);
7651                            continue;
7652                        }
7653                    };
7654                    let field_idx = self.intern_and_add_const(ctx, &field_name);
7655                    let key = self.alloc_register();
7656                    self.emit(Opcode::LoadConst);
7657                    self.emit_u16(key);
7658                    self.emit_u32(field_idx);
7659                    if p.is_private {
7660                        self.emit(Opcode::SetPrivate);
7661                    } else {
7662                        self.emit(Opcode::SetProp);
7663                    }
7664                    self.emit_u16(class_slot);
7665                    self.emit_u16(key);
7666                    self.emit_u16(val_reg);
7667                    self.free_register(key);
7668                    self.free_register(val_reg);
7669                }
7670            }
7671        }
7672
7673        for (block_bc, block_upvalues) in static_blocks {
7674            let block_reg = self.alloc_register();
7675            self.emit_embedded_function(&block_bc, &block_upvalues, 0, block_reg, false, false);
7676            self.emit(Opcode::Call);
7677            self.emit_u16(block_reg);
7678            self.emit_u16(block_reg);
7679            self.emit_u16(0);
7680            self.free_register(block_reg);
7681        }
7682
7683        Ok(class_name_atom)
7684    }
7685}
7686
7687pub(crate) fn detect_simple_constructor_props(
7688    bytecode: &crate::compiler::opcode::Bytecode,
7689) -> bool {
7690    let code = &bytecode.code;
7691    if code.is_empty() {
7692        return false;
7693    }
7694    let mut pc = 0usize;
7695    let mut found_return = false;
7696    let mut has_simple_prop = false;
7697    let pc_limit = bytecode.param_count;
7698    while pc < code.len() {
7699        let op = crate::compiler::opcode::Opcode::from_u8_unchecked(code[pc]);
7700        let size = crate::compiler::opcode::Opcode::instruction_size(op);
7701        if size == 0 || pc + size > code.len() {
7702            break;
7703        }
7704        match op {
7705            crate::compiler::opcode::Opcode::SetNamedProp => {
7706                let obj = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
7707                let val = u16::from_le_bytes([code[pc + 3], code[pc + 4]]);
7708                if val > pc_limit {
7709                    return false;
7710                }
7711                if obj == 0 && val >= 1 {
7712                    has_simple_prop = true;
7713                } else {
7714                    return false;
7715                }
7716            }
7717            crate::compiler::opcode::Opcode::LoadUndefined => {
7718                let dst = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
7719                if dst != 0 {
7720                    return false;
7721                }
7722            }
7723            crate::compiler::opcode::Opcode::Return => {
7724                let src = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
7725                if src == 0 {
7726                    found_return = true;
7727                }
7728                break;
7729            }
7730            _ => {
7731                if op != crate::compiler::opcode::Opcode::Nop {
7732                    return false;
7733                }
7734            }
7735        }
7736        pc += size;
7737    }
7738    found_return && has_simple_prop
7739}
7740
7741fn f64_to_js_string(n: f64) -> String {
7742    if n.is_nan() {
7743        return "NaN".to_string();
7744    }
7745    if n.is_infinite() {
7746        if n.is_sign_negative() {
7747            return "-Infinity".to_string();
7748        }
7749        return "Infinity".to_string();
7750    }
7751    let abs = n.abs();
7752    if abs == 0.0 {
7753        if n.is_sign_negative() {
7754            return "0".to_string();
7755        }
7756        return "0".to_string();
7757    }
7758    if abs < 1e-6 || abs >= 1e21 {
7759        let s = format!("{:e}", n);
7760        s
7761    } else {
7762        n.to_string()
7763    }
7764}
7765
7766#[cfg(test)]
7767mod tests {
7768    use super::*;
7769    use crate::runtime::vm::VM;
7770    use crate::runtime::{JSContext, JSRuntime};
7771
7772    fn run_expr(code: &str) -> i64 {
7773        let mut rt = JSRuntime::new();
7774        let mut ctx = JSContext::new(&mut rt);
7775
7776        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7777        let expr = match ast.body.first() {
7778            Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7779            _ => panic!("expected expression"),
7780        };
7781
7782        let mut codegen = CodeGenerator::with_opt_level(OptLevel::default());
7783        let reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
7784
7785        let mut bytecode = Bytecode::new();
7786        let (code, constants, locals_count, _) = codegen.take_bytecode();
7787        bytecode.code = code;
7788        bytecode.constants = constants;
7789        bytecode.locals_count = locals_count.max((reg + 1) as u32);
7790
7791        if reg != 0 {
7792            bytecode.code.push(Opcode::Move as u8);
7793            bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7794            bytecode.code.extend_from_slice(&reg.to_le_bytes());
7795        }
7796        bytecode.code.push(Opcode::End as u8);
7797
7798        let mut vm = VM::new();
7799        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7800            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7801            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7802        };
7803        result.get_int()
7804    }
7805
7806    fn run_func(code: &str) -> i64 {
7807        let mut rt = JSRuntime::new();
7808        let mut ctx = JSContext::new(&mut rt);
7809
7810        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7811        let func = match ast.body.first() {
7812            Some(ASTNode::FunctionDeclaration(fd)) => fd.clone(),
7813            _ => panic!("expected function declaration"),
7814        };
7815
7816        let mut codegen = CodeGenerator::with_opt_level(OptLevel::default());
7817        let (bytecode, _upvalues) = codegen
7818            .compile_function(&func.params, &func.body, &mut ctx)
7819            .unwrap();
7820
7821        let mut vm = VM::new();
7822        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7823            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7824            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7825        };
7826        result.get_int()
7827    }
7828
7829    fn compile_func_bytecode(code: &str, opt_level: OptLevel) -> Bytecode {
7830        let mut rt = JSRuntime::new();
7831        let mut ctx = JSContext::new(&mut rt);
7832        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7833        let func = match ast.body.first() {
7834            Some(ASTNode::FunctionDeclaration(fd)) => fd.clone(),
7835            _ => panic!("expected function declaration"),
7836        };
7837        let mut codegen = CodeGenerator::with_opt_level(opt_level);
7838        let (bc, _) = codegen
7839            .compile_function(&func.params, &func.body, &mut ctx)
7840            .unwrap();
7841        bc
7842    }
7843
7844    fn compile_script_bytecode_len(code: &str, opt_level: OptLevel) -> usize {
7845        let mut rt = JSRuntime::new();
7846        let mut ctx = JSContext::new(&mut rt);
7847        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
7848        let block = crate::compiler::ast::BlockStatement {
7849            body: ast.body,
7850            lines: ast.lines,
7851        };
7852        let mut codegen = CodeGenerator::with_opt_level(opt_level);
7853        let (bc, _) = codegen.compile_script(&block, &mut ctx).unwrap();
7854        bc.code.len()
7855    }
7856
7857    #[test]
7858    fn test_literal_int() {
7859        assert_eq!(run_expr("42"), 42);
7860    }
7861
7862    #[test]
7863    fn test_add_sub() {
7864        assert_eq!(run_expr("1 + 2"), 3);
7865        assert_eq!(run_expr("10 - 3"), 7);
7866    }
7867
7868    #[test]
7869    fn test_mul_div() {
7870        assert_eq!(run_expr("3 * 4"), 12);
7871        assert_eq!(run_expr("20 / 4"), 5);
7872    }
7873
7874    #[test]
7875    fn test_complex_arithmetic() {
7876        assert_eq!(run_expr("1 + 2 * 3"), 7);
7877        assert_eq!(run_expr("(1 + 2) * 3"), 9);
7878    }
7879
7880    #[test]
7881    fn test_variable_and_assignment() {
7882        assert_eq!(
7883            run_func("function f() { var x = 10; x = x + 5; return x; }"),
7884            15
7885        );
7886    }
7887
7888    #[test]
7889    fn test_if_statement() {
7890        assert_eq!(
7891            run_func("function f() { var x = 3; if (x < 5) { return 1; } return 0; }"),
7892            1
7893        );
7894        assert_eq!(
7895            run_func("function f() { var x = 10; if (x < 5) { return 1; } return 0; }"),
7896            0
7897        );
7898    }
7899
7900    #[test]
7901    fn test_if_else() {
7902        assert_eq!(
7903            run_func("function f() { var x = 2; if (x > 5) { return 10; } else { return 20; } }"),
7904            20
7905        );
7906    }
7907
7908    #[test]
7909    fn test_while_loop() {
7910        assert_eq!(
7911            run_func(
7912                "function f() { var sum = 0; var i = 0; while (i < 10) { sum = sum + i; i = i + 1; } return sum; }"
7913            ),
7914            45
7915        );
7916    }
7917
7918    #[test]
7919    fn test_for_loop() {
7920        assert_eq!(
7921            run_func(
7922                "function f() { var sum = 0; for (var i = 0; i <= 10; i = i + 1) { sum = sum + i; } return sum; }"
7923            ),
7924            55
7925        );
7926    }
7927
7928    #[test]
7929    fn test_ternary() {
7930        assert_eq!(run_expr("1 < 2 ? 10 : 20"), 10);
7931        assert_eq!(run_expr("1 > 2 ? 10 : 20"), 20);
7932    }
7933
7934    #[test]
7935    fn test_unary_neg() {
7936        assert_eq!(run_expr("-5"), -5);
7937        assert_eq!(run_expr("-(-3)"), 3);
7938    }
7939
7940    #[test]
7941    fn test_logical_and() {
7942        assert_eq!(run_expr("1 && 1 ? 1 : 0"), 1);
7943    }
7944
7945    #[test]
7946    fn test_p2_profile_raw_disables_all_literal_folds() {
7947        let code = "function f() { if (true) { return 1; } return 2; }";
7948        let bc = compile_func_bytecode(code, OptLevel::O0);
7949        assert!(bc.code.contains(&(Opcode::JumpIfNot as u8)));
7950    }
7951
7952    #[test]
7953    fn test_p2_profile_ternary_keeps_if_unfolded() {
7954        let code = "function f() { if (true) { return 1; } return 2; }";
7955        let bc = compile_func_bytecode(code, OptLevel::O1);
7956        assert!(bc.code.contains(&(Opcode::JumpIfNot as u8)));
7957    }
7958
7959    #[test]
7960    fn test_p2_profile_stable_folds_if_true() {
7961        let code = "function f() { if (true) { return 1; } return 2; }";
7962        let bc = compile_func_bytecode(code, OptLevel::O2);
7963        assert!(!bc.code.contains(&(Opcode::JumpIfNot as u8)));
7964    }
7965
7966    #[test]
7967    fn test_p2_profile_aggressive_folds_string_truthy_conditional() {
7968        let mut rt = JSRuntime::new();
7969        let mut ctx = JSContext::new(&mut rt);
7970        let ast = crate::compiler::parser::Parser::new("'x' ? 11 : 22")
7971            .parse()
7972            .unwrap();
7973        let expr = match ast.body.first() {
7974            Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
7975            _ => panic!("expected expression"),
7976        };
7977        let mut codegen = CodeGenerator::with_opt_level(OptLevel::O3);
7978        let reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
7979
7980        let mut bytecode = Bytecode::new();
7981        let (code, constants, locals_count, _) = codegen.take_bytecode();
7982        bytecode.code = code;
7983        bytecode.constants = constants;
7984        bytecode.locals_count = locals_count.max((reg + 1) as u32);
7985        if reg != 0 {
7986            bytecode.code.push(Opcode::Move as u8);
7987            bytecode.code.extend_from_slice(&0u16.to_le_bytes());
7988            bytecode.code.extend_from_slice(&reg.to_le_bytes());
7989        }
7990        bytecode.code.push(Opcode::End as u8);
7991
7992        let mut vm = VM::new();
7993        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
7994            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
7995            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
7996        };
7997        assert_eq!(result.get_int(), 11);
7998    }
7999
8000    #[test]
8001    fn test_prefix_increment() {
8002        assert_eq!(run_func("function f() { var x = 5; return ++x; }"), 6);
8003        assert_eq!(
8004            run_func("function f() { var x = 5; var y = ++x; return x + y; }"),
8005            12
8006        );
8007    }
8008
8009    #[test]
8010    fn test_postfix_increment() {
8011        assert_eq!(run_func("function f() { var x = 5; return x++; }"), 5);
8012        assert_eq!(
8013            run_func("function f() { var x = 5; var y = x++; return x + y; }"),
8014            11
8015        );
8016    }
8017
8018    #[test]
8019    fn test_prefix_decrement() {
8020        assert_eq!(run_func("function f() { var x = 5; return --x; }"), 4);
8021        assert_eq!(
8022            run_func("function f() { var x = 5; var y = --x; return x + y; }"),
8023            8
8024        );
8025    }
8026
8027    #[test]
8028    fn test_postfix_decrement() {
8029        assert_eq!(run_func("function f() { var x = 5; return x--; }"), 5);
8030        assert_eq!(
8031            run_func("function f() { var x = 5; var y = x--; return x + y; }"),
8032            9
8033        );
8034    }
8035
8036    #[test]
8037    fn test_do_while_loop() {
8038        assert_eq!(
8039            run_func(
8040                "function f() { var sum = 0; var i = 0; do { sum = sum + i; i = i + 1; } while (i < 10); return sum; }"
8041            ),
8042            45
8043        );
8044    }
8045
8046    #[test]
8047    fn test_break_in_while() {
8048        assert_eq!(
8049            run_func(
8050                "function f() { var sum = 0; var i = 0; while (i < 100) { sum = sum + i; i = i + 1; if (i > 5) break; } return sum; }"
8051            ),
8052            15
8053        );
8054    }
8055
8056    #[test]
8057    fn test_break_in_for() {
8058        assert_eq!(
8059            run_func(
8060                "function f() { var sum = 0; for (var i = 0; i < 100; i = i + 1) { sum = sum + i; if (i == 5) break; } return sum; }"
8061            ),
8062            15
8063        );
8064    }
8065
8066    #[test]
8067    fn test_break_in_do_while() {
8068        assert_eq!(
8069            run_func(
8070                "function f() { var sum = 0; var i = 0; do { sum = sum + i; i = i + 1; if (i > 5) break; } while (i < 100); return sum; }"
8071            ),
8072            15
8073        );
8074    }
8075
8076    #[test]
8077    fn test_continue_in_while() {
8078        assert_eq!(
8079            run_func(
8080                "function f() { var sum = 0; var i = 0; while (i < 5) { i = i + 1; if (i == 3) continue; sum = sum + i; } return sum; }"
8081            ),
8082            12
8083        );
8084    }
8085
8086    #[test]
8087    fn test_continue_in_for() {
8088        assert_eq!(
8089            run_func(
8090                "function f() { var sum = 0; for (var i = 0; i < 5; i = i + 1) { if (i == 3) continue; sum = sum + i; } return sum; }"
8091            ),
8092            7
8093        );
8094    }
8095
8096    #[test]
8097    fn test_tiny_inline_getter_reduces_bytecode() {
8098        let code = r#"
8099            function car(p) { return p.car; }
8100            function main(x) { return car(x); }
8101        "#;
8102
8103        let inline_len = compile_script_bytecode_len(code, OptLevel::O2);
8104        let noninline_len = compile_script_bytecode_len(code, OptLevel::O0);
8105
8106        assert!(
8107            inline_len < noninline_len,
8108            "tiny getter inline should reduce bytecode: inline={}, noninline={}",
8109            inline_len,
8110            noninline_len
8111        );
8112    }
8113
8114    #[test]
8115    fn test_tiny_inline_nested_getter_reduces_bytecode() {
8116        let code = r#"
8117            function cadr(p) { return p.cdr.car; }
8118            function main(x) { return cadr(x); }
8119        "#;
8120
8121        let inline_len = compile_script_bytecode_len(code, OptLevel::O2);
8122        let noninline_len = compile_script_bytecode_len(code, OptLevel::O0);
8123
8124        assert!(
8125            inline_len < noninline_len,
8126            "tiny nested getter inline should reduce bytecode: inline={}, noninline={}",
8127            inline_len,
8128            noninline_len
8129        );
8130    }
8131
8132    #[test]
8133    fn test_continue_in_do_while() {
8134        assert_eq!(
8135            run_func(
8136                "function f() { var sum = 0; var i = 0; do { i = i + 1; if (i == 3) continue; sum = sum + i; } while (i < 5); return sum; }"
8137            ),
8138            12
8139        );
8140    }
8141
8142    #[test]
8143    fn test_bitwise_codegen() {
8144        assert_eq!(run_expr("12 & 10"), 8);
8145        assert_eq!(run_expr("12 | 10"), 14);
8146        assert_eq!(run_expr("12 ^ 10"), 6);
8147        assert_eq!(run_expr("~8"), -9);
8148        assert_eq!(run_expr("1 << 4"), 16);
8149        assert_eq!(run_expr("16 >> 2"), 4);
8150        assert_eq!(run_expr("-1 >>> 1"), 2147483647);
8151    }
8152
8153    #[test]
8154    fn test_mod_pow_codegen() {
8155        assert_eq!(run_expr("17 % 5"), 2);
8156        assert_eq!(run_expr("2 ** 10"), 1024);
8157    }
8158
8159    #[test]
8160    fn test_switch_basic() {
8161        assert_eq!(
8162            run_func(
8163                "function f() { var x = 2; switch (x) { case 1: return 10; case 2: return 20; case 3: return 30; default: return 99; } }"
8164            ),
8165            20
8166        );
8167    }
8168
8169    #[test]
8170    fn test_switch_default() {
8171        assert_eq!(
8172            run_func(
8173                "function f() { var x = 5; switch (x) { case 1: return 10; case 2: return 20; default: return 99; } }"
8174            ),
8175            99
8176        );
8177    }
8178
8179    #[test]
8180    fn test_switch_fallthrough() {
8181        assert_eq!(
8182            run_func(
8183                "function f() { var x = 1; var r = 0; switch (x) { case 1: r = r + 1; case 2: r = r + 10; default: r = r + 100; } return r; }"
8184            ),
8185            111
8186        );
8187    }
8188
8189    #[test]
8190    fn test_switch_break() {
8191        assert_eq!(
8192            run_func(
8193                "function f() { var x = 1; var r = 0; switch (x) { case 1: r = r + 1; break; case 2: r = r + 10; break; default: r = r + 100; } return r; }"
8194            ),
8195            1
8196        );
8197    }
8198
8199    #[test]
8200    fn test_switch_break_in_nested_loop() {
8201        assert_eq!(
8202            run_func(
8203                "function f() { var r = 0; for (var i = 0; i < 3; i = i + 1) { switch (i) { case 0: r = r + 1; break; case 1: r = r + 10; break; default: r = r + 100; } } return r; }"
8204            ),
8205            111
8206        );
8207    }
8208
8209    #[test]
8210    fn test_closure_basic() {
8211        assert_eq!(
8212            run_func(
8213                "function f() { var count = 0; function inc() { count = count + 1; return count; } return inc() + inc(); }"
8214            ),
8215            3
8216        );
8217    }
8218
8219    #[test]
8220    fn test_closure_counter() {
8221        assert_eq!(
8222            run_func(
8223                "function f() { function makeCounter() { var count = 0; return function() { count = count + 1; return count; }; } var c = makeCounter(); return c() + c(); }"
8224            ),
8225            3
8226        );
8227    }
8228
8229    #[test]
8230    fn test_closure_with_param() {
8231        assert_eq!(
8232            run_func(
8233                "function f() { function makeAdder(x) { return function(y) { return x + y; }; } var add5 = makeAdder(5); return add5(3); }"
8234            ),
8235            8
8236        );
8237    }
8238
8239    #[test]
8240    fn test_nested_closure() {
8241        assert_eq!(
8242            run_func(
8243                "function f() { var a = 1; function g() { var b = 2; return function() { return a + b; }; } return g()(); }"
8244            ),
8245            3
8246        );
8247    }
8248
8249    #[test]
8250    fn test_arrow_function() {
8251        assert_eq!(
8252            run_func(
8253                "function f() { var double = function(x) { return x * 2; }; return double(5); }"
8254            ),
8255            10
8256        );
8257    }
8258
8259    #[test]
8260    fn test_delete_prop() {
8261        assert_eq!(
8262            run_func("function f() { var o = {x: 1, y: 2}; delete o.x; return o.y; }"),
8263            2
8264        );
8265    }
8266
8267    #[test]
8268    fn test_delete_returns_true() {
8269        assert_eq!(
8270            run_func("function f() { var o = {x: 1}; return delete o.x; }"),
8271            1
8272        );
8273    }
8274
8275    #[test]
8276    fn test_has_property_in() {
8277        assert_eq!(
8278            run_func("function f() { var o = {x: 1}; return 'x' in o; }"),
8279            1
8280        );
8281    }
8282
8283    #[test]
8284    fn test_has_property_not_in() {
8285        assert_eq!(
8286            run_func("function f() { var o = {x: 1}; return 'y' in o; }"),
8287            0
8288        );
8289    }
8290
8291    #[test]
8292    fn test_rest_params() {
8293        assert_eq!(
8294            run_func(
8295                "function outer() { function f(a, b, ...rest) { return rest.length; } return f(1, 2, 3, 4); }"
8296            ),
8297            2
8298        );
8299    }
8300
8301    #[test]
8302    fn test_rest_params_values() {
8303        assert_eq!(
8304            run_func(
8305                "function outer() { function f(a, ...rest) { return rest[0] + rest[1]; } return f(1, 10, 20); }"
8306            ),
8307            30
8308        );
8309    }
8310
8311    #[test]
8312    fn test_rest_params_empty() {
8313        assert_eq!(
8314            run_func(
8315                "function outer() { function f(a, b, ...rest) { return rest.length; } return f(1, 2); }"
8316            ),
8317            0
8318        );
8319    }
8320
8321    #[test]
8322    fn test_object_spread() {
8323        assert_eq!(
8324            run_func(
8325                "function f() { var o = {a: 1, b: 2}; var n = {...o, c: 3}; return n.a + n.b + n.c; }"
8326            ),
8327            6
8328        );
8329    }
8330
8331    #[test]
8332    fn test_array_spread() {
8333        assert_eq!(
8334            run_func(
8335                "function f() { var a = [1, 2]; var b = [...a, 3, 4]; return b[0] + b[1] + b[2] + b[3]; }"
8336            ),
8337            10
8338        );
8339    }
8340
8341    #[test]
8342    fn test_array_spread_mixed() {
8343        assert_eq!(
8344            run_func("function f() { var a = [10, 20]; var b = [5, ...a]; return b.length; }"),
8345            3
8346        );
8347    }
8348
8349    #[test]
8350    fn test_for_of_basic() {
8351        assert_eq!(
8352            run_func(
8353                "function f() { var s = 0; for (var x of [1, 2, 3]) { s = s + x; } return s; }"
8354            ),
8355            6
8356        );
8357    }
8358
8359    #[test]
8360    fn test_for_of_destructuring_array() {
8361        assert_eq!(
8362            run_func(
8363                "function f() { var s = 0; for (var [a, b] of [[1,2],[3,4]]) { s = s + a + b; } return s; }"
8364            ),
8365            10
8366        );
8367    }
8368
8369    #[test]
8370    fn test_for_of_destructuring_object() {
8371        assert_eq!(
8372            run_func(
8373                "function f() { var s = 0; for (var {x} of [{x:1},{x:2},{x:3}]) { s = s + x; } return s; }"
8374            ),
8375            6
8376        );
8377    }
8378
8379    #[test]
8380    fn test_binding_destructuring_nested() {
8381        assert_eq!(
8382            run_func("function f() { var [[x], {y}] = [[1], {y: 2}]; return x + y; }"),
8383            3
8384        );
8385    }
8386
8387    #[test]
8388    fn test_binding_destructuring_defaults() {
8389        assert_eq!(
8390            run_func("function f() { var [a = 10, b = 20] = [1]; return a + b; }"),
8391            21
8392        );
8393    }
8394
8395    #[test]
8396    fn test_for_in_destructuring_array() {
8397        assert_eq!(
8398            run_func(
8399                "function f() { var o = {a:1,b:2}; var s = 0; for (var [k] in o) { s = s + 1; } return s; }"
8400            ),
8401            2
8402        );
8403    }
8404
8405    #[test]
8406    fn test_class_basic() {
8407        assert_eq!(
8408            run_func(
8409                "function f() { class A { constructor() { this.x = 42; } getX() { return this.x; } } var a = new A(); return a.getX(); }"
8410            ),
8411            42
8412        );
8413    }
8414
8415    #[test]
8416    fn test_class_with_static_method() {
8417        assert_eq!(
8418            run_func("function f() { class A { static foo() { return 10; } } return A.foo(); }"),
8419            10
8420        );
8421    }
8422
8423    #[test]
8424    fn test_class_with_instance_field() {
8425        assert_eq!(
8426            run_func(
8427                "function f() { class A { x = 5; y = 7; sum() { return this.x + this.y; } } var a = new A(); return a.sum(); }"
8428            ),
8429            12
8430        );
8431    }
8432
8433    #[test]
8434    fn test_class_inheritance() {
8435        assert_eq!(
8436            run_func(
8437                "function f() { class A { constructor() { this.x = 10; } foo() { return this.x; } } class B extends A { constructor() { super(); this.y = 20; } bar() { return this.y; } } var b = new B(); return b.foo() + b.bar(); }"
8438            ),
8439            30
8440        );
8441    }
8442
8443    #[test]
8444    fn test_class_default_constructor_with_super() {
8445        assert_eq!(
8446            run_func(
8447                "function f() { class A { constructor() { this.x = 100; } } class B extends A {} var b = new B(); return b.x; }"
8448            ),
8449            100
8450        );
8451    }
8452
8453    #[test]
8454    fn test_class_static_field() {
8455        assert_eq!(
8456            run_func("function f() { class A { static count = 3; } return A.count; }"),
8457            3
8458        );
8459    }
8460
8461    #[test]
8462    fn test_for_in_basic() {
8463        assert_eq!(
8464            run_func(
8465                "function f() { var o = {a: 1, b: 2, c: 3}; var sum = 0; for (var k in o) { sum = sum + 1; } return sum; }"
8466            ),
8467            3
8468        );
8469    }
8470
8471    #[test]
8472    fn test_for_in_sum_values() {
8473        assert_eq!(
8474            run_func(
8475                "function f() { var o = {a: 10, b: 20}; var sum = 0; for (var k in o) { sum = sum + o[k]; } return sum; }"
8476            ),
8477            30
8478        );
8479    }
8480
8481    #[test]
8482    fn test_computed_prop_access() {
8483        assert_eq!(
8484            run_func("function f() { var o = {a: 10}; return o['a']; }"),
8485            10
8486        );
8487    }
8488
8489    #[test]
8490    fn test_try_catch_throw() {
8491        assert_eq!(
8492            run_func("function f() { try { throw 42; return 0; } catch(e) { return e + 1; } }"),
8493            43
8494        );
8495    }
8496
8497    #[test]
8498    fn test_try_no_throw() {
8499        assert_eq!(
8500            run_func("function f() { try { return 7; } catch(e) { return 99; } }"),
8501            7
8502        );
8503    }
8504
8505    #[test]
8506    fn test_try_catch_finally() {
8507        assert_eq!(
8508            run_func(
8509                "function f() { var x = 0; try { throw 5; } catch(e) { x = e + 10; } finally { x = x + 1; } return x; }"
8510            ),
8511            16
8512        );
8513    }
8514
8515    #[test]
8516    fn test_try_catch_nested() {
8517        assert_eq!(
8518            run_func(
8519                "function f() { try { try { throw 3; } catch(e) { throw e + 2; } } catch(e) { return e * 2; } }"
8520            ),
8521            10
8522        );
8523    }
8524
8525    #[test]
8526    fn test_private_instance_field_get() {
8527        assert_eq!(
8528            run_func(
8529                "function f() { class A { #x = 42; getX() { return this.#x; } } return new A().getX(); }"
8530            ),
8531            42
8532        );
8533    }
8534
8535    #[test]
8536    fn test_private_instance_field_set() {
8537        assert_eq!(
8538            run_func(
8539                "function f() { class A { #x = 10; setX(v) { this.#x = v; } getX() { return this.#x; } } var a = new A(); a.setX(99); return a.getX(); }"
8540            ),
8541            99
8542        );
8543    }
8544
8545    #[test]
8546    fn test_private_instance_field_in() {
8547        assert_eq!(
8548            run_func(
8549                "function f() { class A { #x = 1; hasX() { return #x in this; } } return new A().hasX() ? 1 : 0; }"
8550            ),
8551            1
8552        );
8553    }
8554
8555    #[test]
8556    fn test_private_static_field() {
8557        assert_eq!(
8558            run_func(
8559                "function f() { class A { static #x = 7; static getX() { return A.#x; } } return A.getX(); }"
8560            ),
8561            7
8562        );
8563    }
8564
8565    #[test]
8566    fn test_generator_basic() {
8567        let mut rt = JSRuntime::new();
8568        let mut ctx = JSContext::new(&mut rt);
8569        let code = "(function*() { yield 1; yield 2; })";
8570        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
8571        let expr = match ast.body.first() {
8572            Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
8573            _ => panic!("expected expression"),
8574        };
8575        let mut codegen = CodeGenerator::new();
8576        let func_reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
8577
8578        let call_dst = codegen.alloc_register();
8579        codegen.emit(Opcode::Call);
8580        codegen.emit_u16(call_dst);
8581        codegen.emit_u16(func_reg);
8582        codegen.emit_u16(0);
8583
8584        let mut bytecode = Bytecode::new();
8585        let (code, constants, locals_count, _) = codegen.take_bytecode();
8586        bytecode.code = code;
8587        bytecode.constants = constants;
8588        bytecode.locals_count = locals_count.max((call_dst + 1) as u32);
8589        bytecode.code.push(Opcode::Move as u8);
8590        bytecode.code.extend_from_slice(&0u16.to_le_bytes());
8591        bytecode.code.extend_from_slice(&call_dst.to_le_bytes());
8592        bytecode.code.push(Opcode::End as u8);
8593
8594        let mut vm = VM::new();
8595        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
8596            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
8597            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
8598        };
8599        assert!(result.is_object(), "expected generator object");
8600    }
8601
8602    #[test]
8603    fn test_async_generator_basic() {
8604        let mut rt = JSRuntime::new();
8605        let mut ctx = JSContext::new(&mut rt);
8606        let code = "(async function*() { yield 1; yield 2; })";
8607        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
8608        let expr = match ast.body.first() {
8609            Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
8610            _ => panic!("expected expression"),
8611        };
8612        let mut codegen = CodeGenerator::new();
8613        let func_reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
8614
8615        let call_dst = codegen.alloc_register();
8616        codegen.emit(Opcode::Call);
8617        codegen.emit_u16(call_dst);
8618        codegen.emit_u16(func_reg);
8619        codegen.emit_u16(0);
8620
8621        let mut bytecode = Bytecode::new();
8622        let (code, constants, locals_count, _) = codegen.take_bytecode();
8623        bytecode.code = code;
8624        bytecode.constants = constants;
8625        bytecode.locals_count = locals_count.max((call_dst + 1) as u32);
8626        bytecode.code.push(Opcode::Move as u8);
8627        bytecode.code.extend_from_slice(&0u16.to_le_bytes());
8628        bytecode.code.extend_from_slice(&call_dst.to_le_bytes());
8629        bytecode.code.push(Opcode::End as u8);
8630
8631        let mut vm = VM::new();
8632        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
8633            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
8634            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
8635        };
8636        assert!(result.is_object(), "expected async generator object");
8637        let obj = result.as_object();
8638        assert!(obj.is_generator(), "expected generator flag to be set");
8639    }
8640
8641    #[test]
8642    fn test_promise_resolve_in_register_vm() {
8643        let mut rt = JSRuntime::new();
8644        let mut ctx = JSContext::new(&mut rt);
8645        let code = "Promise.resolve(42)";
8646        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
8647        let expr = match ast.body.first() {
8648            Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
8649            _ => panic!("expected expression"),
8650        };
8651        let mut codegen = CodeGenerator::new();
8652        let dst = codegen.compile_expression(&expr, &mut ctx).unwrap();
8653
8654        let mut bytecode = Bytecode::new();
8655        let (code, constants, locals_count, _) = codegen.take_bytecode();
8656        bytecode.code = code;
8657        bytecode.constants = constants;
8658        bytecode.locals_count = locals_count.max((dst + 1) as u32);
8659        bytecode.code.push(Opcode::Move as u8);
8660        bytecode.code.extend_from_slice(&0u16.to_le_bytes());
8661        bytecode.code.extend_from_slice(&dst.to_le_bytes());
8662        bytecode.code.push(Opcode::End as u8);
8663
8664        let mut vm = VM::new();
8665        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
8666            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
8667            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
8668        };
8669        assert!(result.is_object(), "expected promise object");
8670        assert!(
8671            crate::builtins::promise::is_promise(&result),
8672            "expected a Promise"
8673        );
8674        let promise_obj = result.as_object();
8675        let state = promise_obj
8676            .get(ctx.common_atoms.__promise_state__)
8677            .unwrap_or(JSValue::new_int(0))
8678            .get_int();
8679        assert_eq!(state, 1, "expected resolved promise");
8680        let value = promise_obj
8681            .get(ctx.common_atoms.__promise_result__)
8682            .unwrap_or(JSValue::undefined());
8683        assert_eq!(value.get_int(), 42, "expected promise result to be 42");
8684    }
8685
8686    #[test]
8687    fn test_async_function_await_resolved_promise() {
8688        let mut rt = JSRuntime::new();
8689        let mut ctx = JSContext::new(&mut rt);
8690        let code = "(async function() { return await Promise.resolve(42); })";
8691        let ast = crate::compiler::parser::Parser::new(code).parse().unwrap();
8692        let expr = match ast.body.first() {
8693            Some(ASTNode::ExpressionStatement(es)) => es.expression.clone(),
8694            _ => panic!("expected expression"),
8695        };
8696        let mut codegen = CodeGenerator::new();
8697        let func_reg = codegen.compile_expression(&expr, &mut ctx).unwrap();
8698
8699        let call_dst = codegen.alloc_register();
8700        codegen.emit(Opcode::Call);
8701        codegen.emit_u16(call_dst);
8702        codegen.emit_u16(func_reg);
8703        codegen.emit_u16(0);
8704
8705        let mut bytecode = Bytecode::new();
8706        let (code, constants, locals_count, _) = codegen.take_bytecode();
8707        bytecode.code = code;
8708        bytecode.constants = constants;
8709        bytecode.locals_count = locals_count.max((call_dst + 1) as u32);
8710        bytecode.code.push(Opcode::Move as u8);
8711        bytecode.code.extend_from_slice(&0u16.to_le_bytes());
8712        bytecode.code.extend_from_slice(&call_dst.to_le_bytes());
8713        bytecode.code.push(Opcode::End as u8);
8714
8715        let mut vm = VM::new();
8716        let result = match vm.execute(&mut ctx, &bytecode).unwrap() {
8717            crate::runtime::vm::ExecutionOutcome::Complete(v) => v,
8718            crate::runtime::vm::ExecutionOutcome::Yield(v) => v,
8719        };
8720        assert!(
8721            result.is_object(),
8722            "expected promise object from async function call"
8723        );
8724
8725        assert!(
8726            crate::builtins::promise::is_promise(&result),
8727            "expected a Promise"
8728        );
8729        let promise_obj = result.as_object();
8730        let state = promise_obj
8731            .get(ctx.common_atoms.__promise_state__)
8732            .unwrap_or(JSValue::new_int(0))
8733            .get_int();
8734        assert_eq!(state, 1, "expected resolved promise");
8735        let value = promise_obj
8736            .get(ctx.common_atoms.__promise_result__)
8737            .unwrap_or(JSValue::undefined());
8738        assert_eq!(value.get_int(), 42, "expected promise result to be 42");
8739    }
8740
8741    #[test]
8742    fn test_deep_nesting_no_overflow() {
8743        let code = "1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1";
8744        assert_eq!(run_expr(code), 49);
8745    }
8746
8747    #[test]
8748    fn test_very_deep_nesting_no_overflow() {
8749        let ones: Vec<&str> = std::iter::repeat("1").take(100).collect();
8750        let code = ones.join(" + ");
8751        assert_eq!(run_expr(&code), 100);
8752    }
8753
8754    #[test]
8755    fn test_extreme_deep_nesting_no_overflow() {
8756        let ones: Vec<&str> = std::iter::repeat("1").take(300).collect();
8757        let code = ones.join(" + ");
8758        assert_eq!(run_expr(&code), 300);
8759    }
8760}