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