Skip to main content

pipa/runtime/
vm.rs

1use crate::compiler::opcode::{Bytecode, Opcode};
2use crate::runtime::context::JSContext;
3#[cfg(test)]
4use crate::runtime::runtime::JSRuntime;
5use crate::util::FxHashMap;
6use crate::value::JSValue;
7
8const GC_CHECK_INTERVAL: u64 = 16384;
9const INTERRUPT_POLL_EVERY_MASK: usize = 0xFFF;
10
11const PRIM_STRING_SHAPE_ID: usize = usize::MAX - 10;
12const PRIM_NUMBER_SHAPE_ID: usize = usize::MAX - 11;
13
14#[derive(Debug, Clone)]
15pub struct CallFrame {
16    pub return_pc: usize,
17    pub registers_base: usize,
18    pub registers_count: usize,
19    pub locals_count: u32,
20    pub bytecode_ptr: *const u8,
21    pub bytecode_len: usize,
22    pub constants_ptr: *const JSValue,
23    pub constants_len: usize,
24    pub function_ptr: Option<usize>,
25    pub ic_table_ptr: *mut crate::compiler::InlineCacheTable,
26    pub this_value: JSValue,
27    pub saved_args: Vec<JSValue>,
28    pub upvalue_sync_map: Option<Box<FxHashMap<u16, std::rc::Rc<std::cell::Cell<JSValue>>>>>,
29
30    pub upvalue_sync_bitset: u64,
31    pub dst_reg: u16,
32    pub arg_count: u16,
33    pub super_ctor: JSValue,
34    pub is_constructor: bool,
35    pub is_async: bool,
36    pub uses_arguments: bool,
37    pub current_pc: usize,
38    pub is_strict_frame: bool,
39
40    pub has_upvalues: bool,
41
42    pub var_name_map: *const Vec<(u32, u16)>,
43    pub eval_bindings: Option<Box<std::collections::HashMap<u32, JSValue>>>,
44    pub cached_arguments: Option<usize>,
45}
46
47impl CallFrame {
48    pub fn new() -> Self {
49        Self {
50            return_pc: 0,
51            registers_base: 0,
52            registers_count: 0,
53            locals_count: 0,
54            bytecode_ptr: std::ptr::null(),
55            bytecode_len: 0,
56            constants_ptr: std::ptr::null(),
57            constants_len: 0,
58            function_ptr: None,
59            ic_table_ptr: std::ptr::null_mut(),
60            this_value: JSValue::undefined(),
61            saved_args: Vec::new(),
62            upvalue_sync_map: None,
63            upvalue_sync_bitset: 0,
64            dst_reg: 0,
65            arg_count: 0,
66            super_ctor: JSValue::undefined(),
67            is_constructor: false,
68            is_async: false,
69            uses_arguments: false,
70            current_pc: 0,
71            is_strict_frame: false,
72            has_upvalues: false,
73            var_name_map: std::ptr::null(),
74            eval_bindings: None,
75            cached_arguments: None,
76        }
77    }
78}
79
80#[derive(Clone)]
81pub struct ExceptionHandler {
82    pub frame_index: usize,
83    pub catch_pc: usize,
84    pub finally_pc: Option<usize>,
85}
86
87#[derive(Debug)]
88pub enum ExecutionOutcome {
89    Complete(JSValue),
90    Yield(JSValue),
91}
92
93enum ThrowDispatch {
94    Caught,
95
96    Uncaught(String),
97
98    AsyncComplete(ExecutionOutcome),
99}
100
101pub struct VM {
102    pub registers: Vec<JSValue>,
103    pub frames: Vec<CallFrame>,
104    pub frame_index: usize,
105    pub pc: usize,
106
107    cached_code_ptr: *const u8,
108    cached_code_len: usize,
109    cached_const_ptr: *const JSValue,
110    cached_registers_base: usize,
111    cached_registers_ptr: *mut JSValue,
112    cached_has_upvalue_sync: bool,
113
114    cached_upvalue_sync_bitset: u64,
115
116    cached_ic_table_ptr: *mut crate::compiler::InlineCacheTable,
117
118    cached_upvalue_slot_ptr: *const std::rc::Rc<std::cell::Cell<JSValue>>,
119    cached_upvalues_len: usize,
120
121    exception_handlers: Vec<ExceptionHandler>,
122
123    pub(crate) pending_throw: Option<JSValue>,
124
125    pub(crate) last_caught_exception: Option<JSValue>,
126
127    finally_rethrow: Option<JSValue>,
128    pending_finally_rethrow: Option<JSValue>,
129    pending_finally_return: Option<JSValue>,
130
131    ctx_ptr: *mut crate::runtime::JSContext,
132
133    allocation_count: u64,
134
135    caller_vm: Option<usize>,
136
137    eval_binding_frames: u32,
138
139    cached_has_instance_atom: crate::runtime::atom::Atom,
140
141    instanceof_cache_ctor: usize,
142    instanceof_cache_proto: usize,
143    instanceof_cache_result: bool,
144
145    regex_lit_cache: std::collections::HashMap<usize, crate::regexp::Regex>,
146
147    gc_roots: Vec<JSValue>,
148}
149
150impl VM {
151    fn builtin_needs_callee(name: &str) -> bool {
152        matches!(
153            name,
154            "intl_collator_compare_call" | "promise_internal_resolve" | "promise_internal_reject"
155        )
156    }
157
158    pub fn new() -> Self {
159        let mut vm = Self {
160            registers: Vec::with_capacity(4096),
161            frames: vec![CallFrame::new(); 64],
162            frame_index: 0,
163            pc: 0,
164            cached_code_ptr: std::ptr::null(),
165            cached_code_len: 0,
166            cached_const_ptr: std::ptr::null(),
167            cached_registers_base: 0,
168            cached_registers_ptr: std::ptr::null_mut(),
169            cached_has_upvalue_sync: false,
170            cached_upvalue_sync_bitset: 0,
171            cached_ic_table_ptr: std::ptr::null_mut(),
172            cached_upvalue_slot_ptr: std::ptr::null(),
173            cached_upvalues_len: 0,
174            exception_handlers: Vec::new(),
175            pending_throw: None,
176            last_caught_exception: None,
177            finally_rethrow: None,
178            pending_finally_rethrow: None,
179            pending_finally_return: None,
180            ctx_ptr: std::ptr::null_mut(),
181            allocation_count: 0,
182            caller_vm: None,
183            eval_binding_frames: 0,
184            cached_has_instance_atom: crate::runtime::atom::Atom(0),
185            instanceof_cache_ctor: 0,
186            instanceof_cache_proto: 0,
187            instanceof_cache_result: false,
188            regex_lit_cache: std::collections::HashMap::new(),
189            gc_roots: Vec::with_capacity(512),
190        };
191        vm.frames[0] = CallFrame::new();
192        vm
193    }
194
195    fn cleanup_handlers_for_frame(&mut self) {
196        let fi = self.frame_index;
197        while self
198            .exception_handlers
199            .last()
200            .map_or(false, |h| h.frame_index == fi)
201        {
202            self.exception_handlers.pop();
203        }
204    }
205
206    fn throw_reference_error(&mut self, ctx: &mut JSContext, msg: &str) -> Option<JSValue> {
207        let mut err = crate::object::object::JSObject::new();
208        err.set(
209            ctx.intern("name"),
210            JSValue::new_string(ctx.intern("ReferenceError")),
211        );
212        err.set(ctx.intern("message"), JSValue::new_string(ctx.intern(msg)));
213        if let Some(proto) = ctx.get_reference_error_prototype() {
214            err.prototype = Some(proto);
215        }
216        let ptr = Box::into_raw(Box::new(err)) as usize;
217        ctx.runtime_mut().gc_heap_mut().track(ptr);
218        self.pending_throw = Some(JSValue::new_object(ptr));
219        None
220    }
221
222    fn create_iter_object(&mut self, ctx: &mut JSContext, iterable: JSValue) -> JSValue {
223        let mut iter_obj = crate::object::object::JSObject::new();
224        let arr_atom = ctx.common_atoms.__iter_arr__;
225        let idx_atom = ctx.common_atoms.__iter_idx__;
226        iter_obj.set_cached(arr_atom, iterable, ctx.shape_cache_mut());
227        iter_obj.set_cached(idx_atom, JSValue::new_int(0), ctx.shape_cache_mut());
228        let iter_ptr = Box::into_raw(Box::new(iter_obj)) as usize;
229        ctx.runtime_mut().gc_heap_mut().track(iter_ptr);
230        self.allocation_count += 1;
231        JSValue::new_object(iter_ptr)
232    }
233
234    #[inline(always)]
235    fn refresh_cache(&mut self) {
236        let frame = &self.frames[self.frame_index];
237        self.cached_code_ptr = frame.bytecode_ptr;
238        self.cached_code_len = frame.bytecode_len;
239        self.cached_const_ptr = frame.constants_ptr;
240        self.cached_registers_base = frame.registers_base;
241        self.cached_has_upvalue_sync = frame.upvalue_sync_map.is_some();
242        self.cached_upvalue_sync_bitset = frame.upvalue_sync_bitset;
243        self.cached_registers_ptr =
244            unsafe { self.registers.as_mut_ptr().add(self.cached_registers_base) };
245        self.cached_ic_table_ptr = frame.ic_table_ptr;
246
247        if frame.has_upvalues {
248            if let Some(func_ptr) = frame.function_ptr {
249                let func_val = JSValue::new_function(func_ptr);
250                let func = func_val.as_function();
251                if let Some(uv) = func.upvalues.as_ref() {
252                    let slots = &uv.upvalue_slots;
253                    self.cached_upvalue_slot_ptr = slots.as_ptr();
254                    self.cached_upvalues_len = slots.len();
255                } else {
256                    self.cached_upvalue_slot_ptr = std::ptr::null();
257                    self.cached_upvalues_len = 0;
258                }
259            } else {
260                self.cached_upvalue_slot_ptr = std::ptr::null();
261                self.cached_upvalues_len = 0;
262            }
263        } else {
264            self.cached_upvalue_slot_ptr = std::ptr::null();
265            self.cached_upvalues_len = 0;
266        }
267    }
268
269    fn push_frame(
270        &mut self,
271        bytecode: &Bytecode,
272        return_pc: usize,
273        function_ptr: Option<usize>,
274        this_value: JSValue,
275        dst_reg: u16,
276        arg_count: u16,
277        is_constructor: bool,
278        is_async: bool,
279        args: &[JSValue],
280        save_args: bool,
281    ) {
282        self.push_frame_raw(
283            bytecode.locals_count,
284            bytecode.effective_code_ptr(),
285            bytecode.effective_code_len(),
286            bytecode.effective_const_ptr(),
287            bytecode.effective_const_len(),
288            return_pc,
289            function_ptr,
290            this_value,
291            dst_reg,
292            arg_count,
293            is_constructor,
294            is_async,
295            args,
296            save_args,
297        );
298    }
299
300    fn push_frame_raw(
301        &mut self,
302        locals_count: u32,
303        bytecode_ptr: *const u8,
304        bytecode_len: usize,
305        constants_ptr: *const JSValue,
306        constants_len: usize,
307        return_pc: usize,
308        function_ptr: Option<usize>,
309        this_value: JSValue,
310        dst_reg: u16,
311        arg_count: u16,
312        is_constructor: bool,
313        is_async: bool,
314        args: &[JSValue],
315        save_args: bool,
316    ) {
317        let needed = (locals_count as usize).max(arg_count as usize + 1);
318        let prev = &self.frames[self.frame_index];
319        let base = prev.registers_base + prev.registers_count;
320        let total = base + needed;
321        if total > self.registers.len() {
322            if total > self.registers.capacity() {
323                let new_cap = (self.registers.capacity() * 2 + 64).max(total);
324                self.registers.reserve(new_cap - self.registers.len());
325            }
326
327            unsafe {
328                self.registers.set_len(total);
329            }
330            self.cached_registers_ptr =
331                unsafe { self.registers.as_mut_ptr().add(self.cached_registers_base) };
332        }
333
334        unsafe {
335            let ptr = self.registers.as_mut_ptr().add(base);
336            ptr.write(this_value);
337            for (i, arg) in args.iter().enumerate() {
338                ptr.add(i + 1).write(*arg);
339            }
340            let undef = JSValue::undefined();
341            for i in (args.len() + 1)..needed {
342                ptr.add(i).write(undef);
343            }
344        }
345
346        self.frame_index += 1;
347        if self.frame_index >= self.frames.len() {
348            self.frames.resize(self.frames.len() * 2, CallFrame::new());
349        }
350
351        let frame = &mut self.frames[self.frame_index];
352        frame.return_pc = return_pc;
353        frame.registers_base = base;
354        frame.registers_count = needed;
355        frame.locals_count = locals_count;
356        frame.bytecode_ptr = bytecode_ptr;
357        frame.bytecode_len = bytecode_len;
358        frame.constants_ptr = constants_ptr;
359        frame.constants_len = constants_len;
360        frame.function_ptr = function_ptr;
361        frame.ic_table_ptr = function_ptr
362            .and_then(|fptr| {
363                let js_func = unsafe { JSValue::function_from_ptr(fptr) };
364                js_func
365                    .bytecode
366                    .as_ref()
367                    .map(|rb| rb.effective_ic_table_ptr())
368            })
369            .unwrap_or(std::ptr::null_mut());
370        frame.this_value = this_value;
371        frame.dst_reg = dst_reg;
372        frame.arg_count = arg_count;
373        frame.super_ctor = JSValue::undefined();
374        frame.is_constructor = is_constructor;
375        frame.is_async = is_async;
376        if let Some(fptr) = function_ptr {
377            let func = unsafe { &*(fptr as *const crate::object::function::JSFunction) };
378            frame.is_strict_frame = func.is_strict();
379            frame.var_name_map = func.bytecode.as_ref().map_or(std::ptr::null(), |bc| {
380                std::rc::Rc::as_ptr(&bc.var_name_to_slot)
381            });
382            frame.has_upvalues = func
383                .upvalues
384                .as_ref()
385                .map_or(false, |uv| !uv.upvalue_slots.is_empty());
386        } else {
387            frame.is_strict_frame = false;
388            frame.var_name_map = std::ptr::null();
389            frame.has_upvalues = false;
390        }
391        frame.cached_arguments = None;
392        frame.uses_arguments = save_args;
393        if save_args {
394            frame.saved_args.clear();
395            frame.saved_args.extend_from_slice(args);
396        } else if !frame.saved_args.is_empty() {
397            frame.saved_args.clear();
398        }
399        if frame.upvalue_sync_map.is_some() {
400            frame.upvalue_sync_map = None;
401            frame.upvalue_sync_bitset = 0;
402        }
403        self.pc = 0;
404        self.cached_code_ptr = bytecode_ptr;
405        self.cached_code_len = bytecode_len;
406        self.cached_const_ptr = constants_ptr;
407        self.cached_registers_base = base;
408        self.cached_has_upvalue_sync = false;
409        self.cached_upvalue_sync_bitset = 0;
410        self.cached_registers_ptr = unsafe { self.registers.as_mut_ptr().add(base) };
411        self.cached_ic_table_ptr = frame.ic_table_ptr;
412        if let Some(fptr) = function_ptr {
413            if frame.has_upvalues {
414                let func = unsafe { JSValue::function_from_ptr(fptr) };
415                if let Some(uv) = func.upvalues.as_ref() {
416                    let slots = &uv.upvalue_slots;
417                    self.cached_upvalue_slot_ptr = slots.as_ptr();
418                    self.cached_upvalues_len = slots.len();
419                } else {
420                    self.cached_upvalue_slot_ptr = std::ptr::null();
421                    self.cached_upvalues_len = 0;
422                }
423            } else {
424                self.cached_upvalue_slot_ptr = std::ptr::null();
425                self.cached_upvalues_len = 0;
426            }
427        } else {
428            self.cached_upvalue_slot_ptr = std::ptr::null();
429            self.cached_upvalues_len = 0;
430        }
431    }
432
433    #[inline(always)]
434    fn push_frame_from_arg_regs_raw(
435        &mut self,
436        _ctx: &mut JSContext,
437        locals_count: u32,
438        bytecode_ptr: *const u8,
439        bytecode_len: usize,
440        constants_ptr: *const JSValue,
441        constants_len: usize,
442        return_pc: usize,
443        function_ptr: Option<usize>,
444        ic_table_ptr: *mut crate::compiler::InlineCacheTable,
445        this_value: JSValue,
446        dst_reg: u16,
447        arg_count: u16,
448        is_constructor: bool,
449        is_async: bool,
450        _caller_base: usize,
451        arg_regs: &[u16],
452        save_args: bool,
453    ) {
454        let needed = (locals_count as usize).max(arg_count as usize + 1);
455        let prev = &self.frames[self.frame_index];
456        let base = prev.registers_base + prev.registers_count;
457        let total = base + needed;
458
459        if total > self.registers.len() {
460            if total > self.registers.capacity() {
461                let new_cap = (self.registers.capacity() * 2 + 64).max(total);
462                self.registers.reserve(new_cap - self.registers.len());
463            }
464
465            unsafe {
466                self.registers.set_len(total);
467            }
468            self.cached_registers_ptr =
469                unsafe { self.registers.as_mut_ptr().add(self.cached_registers_base) };
470        }
471
472        unsafe {
473            let ptr = self.registers.as_mut_ptr().add(base);
474            ptr.write(this_value);
475
476            let caller_ptr = self.cached_registers_ptr;
477            for (i, r) in arg_regs.iter().enumerate() {
478                ptr.add(i + 1).write(*caller_ptr.add(*r as usize));
479            }
480            let fill_start = arg_regs.len() + 1;
481            if fill_start < needed {
482                let undef = JSValue::undefined();
483                for i in fill_start..needed {
484                    ptr.add(i).write(undef);
485                }
486            }
487        }
488
489        self.frame_index += 1;
490        let is_new_frame = self.frame_index >= self.frames.len();
491        if is_new_frame {
492            self.frames.resize(self.frames.len() * 2, CallFrame::new());
493        }
494
495        let frame = &mut self.frames[self.frame_index];
496        frame.return_pc = return_pc;
497        frame.registers_base = base;
498        frame.registers_count = needed;
499        frame.locals_count = locals_count;
500        frame.bytecode_ptr = bytecode_ptr;
501        frame.bytecode_len = bytecode_len;
502        frame.constants_ptr = constants_ptr;
503        frame.constants_len = constants_len;
504        frame.function_ptr = function_ptr;
505        frame.ic_table_ptr = ic_table_ptr;
506        frame.this_value = this_value;
507        frame.dst_reg = dst_reg;
508        frame.arg_count = arg_count;
509        frame.super_ctor = JSValue::undefined();
510        frame.is_constructor = is_constructor;
511        frame.is_async = is_async;
512        if let Some(fptr) = function_ptr {
513            let func = unsafe { &*(fptr as *const crate::object::function::JSFunction) };
514            frame.is_strict_frame = func.is_strict();
515            frame.var_name_map = func.bytecode.as_ref().map_or(std::ptr::null(), |bc| {
516                std::rc::Rc::as_ptr(&bc.var_name_to_slot)
517            });
518            frame.has_upvalues = func
519                .upvalues
520                .as_ref()
521                .map_or(false, |uv| !uv.upvalue_slots.is_empty());
522        } else {
523            frame.is_strict_frame = false;
524            frame.var_name_map = std::ptr::null();
525            frame.has_upvalues = false;
526        }
527        frame.cached_arguments = None;
528        frame.uses_arguments = save_args;
529        if save_args {
530            if !is_new_frame && !frame.saved_args.is_empty() {
531                frame.saved_args.clear();
532            }
533            frame.saved_args.reserve(arg_regs.len());
534            let caller_ptr = self.cached_registers_ptr;
535            for r in arg_regs.iter() {
536                frame
537                    .saved_args
538                    .push(unsafe { *caller_ptr.add(*r as usize) });
539            }
540        } else if !is_new_frame && !frame.saved_args.is_empty() {
541            frame.saved_args.clear();
542        }
543        if !is_new_frame && frame.upvalue_sync_map.is_some() {
544            frame.upvalue_sync_map = None;
545            frame.upvalue_sync_bitset = 0;
546        }
547        self.pc = 0;
548        self.cached_code_ptr = bytecode_ptr;
549        self.cached_code_len = bytecode_len;
550        self.cached_const_ptr = constants_ptr;
551        self.cached_registers_base = base;
552        self.cached_has_upvalue_sync = false;
553        self.cached_upvalue_sync_bitset = 0;
554        self.cached_registers_ptr = unsafe { self.registers.as_mut_ptr().add(base) };
555        self.cached_ic_table_ptr = ic_table_ptr;
556        if let Some(fptr) = function_ptr {
557            if frame.has_upvalues {
558                let func = unsafe { JSValue::function_from_ptr(fptr) };
559                if let Some(uv) = func.upvalues.as_ref() {
560                    let slots = &uv.upvalue_slots;
561                    self.cached_upvalue_slot_ptr = slots.as_ptr();
562                    self.cached_upvalues_len = slots.len();
563                } else {
564                    self.cached_upvalue_slot_ptr = std::ptr::null();
565                    self.cached_upvalues_len = 0;
566                }
567            } else {
568                self.cached_upvalue_slot_ptr = std::ptr::null();
569                self.cached_upvalues_len = 0;
570            }
571        } else {
572            self.cached_upvalue_slot_ptr = std::ptr::null();
573            self.cached_upvalues_len = 0;
574        }
575    }
576
577    #[inline(never)]
578    fn scan_eval_bindings(
579        frames: &[CallFrame],
580        frame_index: usize,
581        atom_id: u32,
582    ) -> Option<JSValue> {
583        for fi in (0..=frame_index).rev() {
584            if let Some(ref eb) = frames[fi].eval_bindings {
585                if let Some(val) = eb.get(&atom_id) {
586                    return Some(*val);
587                }
588            }
589        }
590        None
591    }
592
593    #[inline(always)]
594    fn pop_frame(&mut self, return_value: JSValue) {
595        if self.frame_index == 0 {
596            return;
597        }
598
599        if self.eval_binding_frames > 0 && self.frames[self.frame_index].eval_bindings.is_some() {
600            self.frames[self.frame_index].eval_bindings = None;
601            self.eval_binding_frames -= 1;
602        }
603        let frame = &self.frames[self.frame_index];
604        let dst_reg = frame.dst_reg;
605        let return_pc = frame.return_pc;
606
607        self.frame_index -= 1;
608        self.pc = return_pc;
609        self.refresh_cache();
610        unsafe {
611            *self.cached_registers_ptr.add(dst_reg as usize) = return_value;
612        }
613
614        let caller = &self.frames[self.frame_index];
615        if let Some(ref sync_map) = caller.upvalue_sync_map {
616            if let Some(cell) = sync_map.get(&dst_reg) {
617                cell.set(return_value);
618            }
619        }
620    }
621
622    pub fn find_var_in_frame_stack(&self, name_atom: u32) -> Option<(usize, u16)> {
623        for fi in (0..=self.frame_index).rev() {
624            let frame = &self.frames[fi];
625            if !frame.var_name_map.is_null() {
626                let map = unsafe { &*frame.var_name_map };
627                for &(an, slot) in map.iter().rev() {
628                    if an == name_atom {
629                        return Some((fi, slot));
630                    }
631                }
632            }
633        }
634        None
635    }
636
637    #[inline(always)]
638    pub fn get_var_in_caller_vm(&self, name_atom: u32) -> Option<JSValue> {
639        if let Some(ptr) = self.caller_vm {
640            let caller = unsafe { &*(ptr as *const VM) };
641            for fi in (0..=caller.frame_index).rev() {
642                let frame = &caller.frames[fi];
643                if let Some(ref eb) = frame.eval_bindings {
644                    if let Some(val) = eb.get(&name_atom) {
645                        return Some(*val);
646                    }
647                }
648                if !frame.var_name_map.is_null() {
649                    let map = unsafe { &*frame.var_name_map };
650                    for &(an, slot) in map.iter().rev() {
651                        if an == name_atom {
652                            let base = frame.registers_base;
653                            let val = caller.registers[base + slot as usize];
654                            return Some(val);
655                        }
656                    }
657                }
658            }
659        }
660        None
661    }
662
663    pub fn set_var_in_caller_vm(
664        &mut self,
665        ctx: &mut JSContext,
666        name_atom: u32,
667        value: JSValue,
668    ) -> bool {
669        if let Some(ptr) = self.caller_vm {
670            let caller = unsafe { &mut *(ptr as *mut VM) };
671            for fi in (0..=caller.frame_index).rev() {
672                let in_eb = caller.frames[fi]
673                    .eval_bindings
674                    .as_ref()
675                    .map_or(false, |eb| eb.contains_key(&name_atom));
676                if in_eb {
677                    caller.frames[fi]
678                        .eval_bindings
679                        .as_mut()
680                        .unwrap()
681                        .insert(name_atom, value);
682
683                    if fi == 0 {
684                        let global = ctx.global();
685                        if global.is_object() {
686                            global.as_object_mut().set_cached(
687                                crate::runtime::atom::Atom(name_atom),
688                                value,
689                                ctx.shape_cache_mut(),
690                            );
691                        }
692                    }
693                    return true;
694                }
695                if !caller.frames[fi].var_name_map.is_null() {
696                    let map = unsafe { &*caller.frames[fi].var_name_map };
697                    for &(an, slot) in map.iter().rev() {
698                        if an == name_atom {
699                            let base = caller.frames[fi].registers_base;
700                            if fi == caller.frame_index {
701                                caller.registers[base + slot as usize] = value;
702                            } else {
703                                let saved = caller.frame_index;
704                                caller.frame_index = fi;
705                                caller.refresh_cache();
706                                caller.set_reg(slot, value);
707                                caller.frame_index = saved;
708                                caller.refresh_cache();
709                            }
710
711                            if fi == 0 {
712                                let global = ctx.global();
713                                if global.is_object() {
714                                    global.as_object_mut().set_cached(
715                                        crate::runtime::atom::Atom(name_atom),
716                                        value,
717                                        ctx.shape_cache_mut(),
718                                    );
719                                }
720                            }
721                            return true;
722                        }
723                    }
724                }
725            }
726
727            for fi in (0..=caller.frame_index).rev() {
728                if caller.frames[fi].function_ptr.is_some() || fi == 0 {
729                    if caller.frames[fi].eval_bindings.is_none() {
730                        caller.frames[fi].eval_bindings =
731                            Some(Box::new(std::collections::HashMap::new()));
732                        caller.eval_binding_frames += 1;
733                    }
734                    caller.frames[fi]
735                        .eval_bindings
736                        .as_mut()
737                        .unwrap()
738                        .insert(name_atom, value);
739
740                    if fi == 0 {
741                        let global = ctx.global();
742                        if global.is_object() {
743                            global.as_object_mut().set_cached(
744                                crate::runtime::atom::Atom(name_atom),
745                                value,
746                                ctx.shape_cache_mut(),
747                            );
748                        }
749                    }
750                    return true;
751                }
752            }
753        }
754        false
755    }
756
757    pub fn init_var_in_caller_vm(
758        &mut self,
759        ctx: &mut JSContext,
760        name_atom: u32,
761        value: JSValue,
762    ) -> bool {
763        if let Some(ptr) = self.caller_vm {
764            let caller = unsafe { &mut *(ptr as *mut VM) };
765            for fi in (0..=caller.frame_index).rev() {
766                if let Some(ref eb) = caller.frames[fi].eval_bindings {
767                    if eb.contains_key(&name_atom) {
768                        return true;
769                    }
770                }
771                if !caller.frames[fi].var_name_map.is_null() {
772                    let map = unsafe { &*caller.frames[fi].var_name_map };
773                    if map.iter().any(|&(an, _)| an == name_atom) {
774                        return true;
775                    }
776                }
777            }
778
779            for fi in (0..=caller.frame_index).rev() {
780                if caller.frames[fi].function_ptr.is_some() || fi == 0 {
781                    if caller.frames[fi].eval_bindings.is_none() {
782                        caller.frames[fi].eval_bindings =
783                            Some(Box::new(std::collections::HashMap::new()));
784                        caller.eval_binding_frames += 1;
785                    }
786                    caller.frames[fi]
787                        .eval_bindings
788                        .as_mut()
789                        .unwrap()
790                        .insert(name_atom, value);
791                    if fi == 0 {
792                        let global = ctx.global();
793                        if global.is_object() {
794                            global.as_object_mut().set_cached(
795                                crate::runtime::atom::Atom(name_atom),
796                                value,
797                                ctx.shape_cache_mut(),
798                            );
799                        }
800                    }
801                    return true;
802                }
803            }
804        }
805        false
806    }
807
808    pub fn set_var_in_frame_stack(&mut self, ctx: &mut JSContext, name: &str, value: JSValue) {
809        let atom = ctx.intern(name);
810        for fi in (0..=self.frame_index).rev() {
811            if !self.frames[fi].var_name_map.is_null() {
812                let map = unsafe { &*self.frames[fi].var_name_map };
813                for &(an, slot) in map.iter().rev() {
814                    if an == atom.0 {
815                        let base = self.frames[fi].registers_base;
816                        if fi == self.frame_index {
817                            self.registers[base + slot as usize] = value;
818                        } else {
819                            let saved_fi = self.frame_index;
820                            self.frame_index = fi;
821                            self.refresh_cache();
822                            self.set_reg(slot, value);
823                            self.frame_index = saved_fi;
824                            self.refresh_cache();
825                        }
826                        return;
827                    }
828                }
829            }
830        }
831        let global = ctx.global();
832        if global.is_object() {
833            global
834                .as_object_mut()
835                .set_cached(atom, value, ctx.shape_cache_mut());
836        }
837    }
838
839    #[inline(always)]
840    fn execute_call(
841        &mut self,
842        ctx: &mut JSContext,
843        func_val: JSValue,
844        this_val: JSValue,
845        dst: u16,
846        argc: u16,
847        arg_regs: &[u16],
848        obj_reg: u16,
849        is_call_new: bool,
850        is_call_method: bool,
851    ) -> Result<bool, String> {
852        if func_val.is_function() {
853            let ptr = func_val.get_ptr();
854            let js_func = unsafe { JSValue::function_from_ptr(ptr) };
855            if let Some(ref rb) = js_func.bytecode {
856                let return_pc = self.pc;
857                let caller_base = self.cached_registers_base;
858                let uses_arguments = js_func.uses_arguments();
859
860                if js_func.is_generator() {
861                    let mut args_buf = [JSValue::undefined(); 16];
862                    let mut args_vec = Vec::new();
863                    let args: &[JSValue] = if argc as usize <= 16 {
864                        for (i, r) in arg_regs.iter().enumerate() {
865                            args_buf[i] = self.registers[caller_base + *r as usize];
866                        }
867                        &args_buf[..argc as usize]
868                    } else {
869                        args_vec.reserve(argc as usize);
870                        for r in arg_regs {
871                            args_vec.push(self.registers[caller_base + *r as usize]);
872                        }
873                        &args_vec
874                    };
875
876                    let saved_frame_index = self.frame_index;
877
878                    let snapshot_this = if !js_func.is_strict()
879                        && (this_val.is_undefined() || this_val.is_null())
880                    {
881                        ctx.global()
882                    } else {
883                        this_val
884                    };
885                    let min_slots = 1 + args.len();
886                    let snapshot_len = (rb.locals_count as usize).max(min_slots);
887                    let mut snapshot = vec![JSValue::undefined(); snapshot_len];
888                    snapshot[0] = snapshot_this;
889                    for (i, arg) in args.iter().enumerate() {
890                        snapshot[i + 1] = *arg;
891                    }
892
893                    let has_params = argc < rb.param_count;
894
895                    let saved_handlers = self.exception_handlers.clone();
896
897                    let (result_snapshot, result_pc, result_done) = if has_params {
898                        match self.execute_generator_step(ctx, rb, &snapshot, 0) {
899                            Ok((_val, new_snapshot, new_pc, done)) => {
900                                self.exception_handlers = saved_handlers;
901                                self.frame_index = saved_frame_index;
902                                (new_snapshot, new_pc, done)
903                            }
904                            Err(e) => {
905                                self.exception_handlers = saved_handlers;
906                                self.frame_index = saved_frame_index;
907                                if self.pending_throw.is_some() {
908                                    return Ok(false);
909                                }
910                                if e.contains("SyntaxError") || e.contains("Uncaught") {
911                                    return Err(e);
912                                }
913                                (snapshot, 0, false)
914                            }
915                        }
916                    } else {
917                        (snapshot, 0, false)
918                    };
919
920                    let mut gen_obj = crate::object::object::JSObject::new();
921                    gen_obj.set_is_generator(true);
922                    if let Some(proto_ptr) = ctx.get_generator_prototype() {
923                        gen_obj.prototype = Some(proto_ptr);
924                    }
925                    gen_obj.set_generator_state(crate::object::object::GeneratorState {
926                        bytecode: Box::new(rb.clone()),
927                        snapshot: result_snapshot,
928                        pc: result_pc,
929                        done: result_done,
930                    });
931                    let gen_ptr = Box::into_raw(Box::new(gen_obj)) as usize;
932                    ctx.runtime_mut().gc_heap_mut().track(gen_ptr);
933                    self.set_reg(dst, JSValue::new_object(gen_ptr));
934
935                    self.frame_index = saved_frame_index;
936
937                    return Ok(false);
938                }
939
940                if is_call_new && rb.is_simple_constructor {
941                    if let Some(cached_shape) = rb.cached_constructor_final_shape {
942                        if this_val.is_object() {
943                            let obj = unsafe { JSValue::object_from_ptr_mut(this_val.get_ptr()) };
944                            let cached_regs_ptr = self.cached_registers_ptr;
945                            obj.fast_init_from_simple_constructor(
946                                rb.simple_constructor_props
947                                    .iter()
948                                    .map(|&(atom, arg_idx, _)| {
949                                        let value = if (arg_idx as usize) < arg_regs.len() {
950                                            unsafe {
951                                                *cached_regs_ptr
952                                                    .add(arg_regs[arg_idx as usize] as usize)
953                                            }
954                                        } else {
955                                            JSValue::undefined()
956                                        };
957                                        (atom, value)
958                                    }),
959                                cached_shape,
960                            );
961                        }
962                        self.set_reg(dst, this_val);
963                        return Ok(false);
964                    }
965                }
966                let fn_this =
967                    if !js_func.is_strict() && (this_val.is_undefined() || this_val.is_null()) {
968                        ctx.global()
969                    } else {
970                        this_val
971                    };
972                self.push_frame_from_arg_regs_raw(
973                    ctx,
974                    rb.locals_count,
975                    rb.effective_code_ptr(),
976                    rb.effective_code_len(),
977                    rb.effective_const_ptr(),
978                    rb.effective_const_len(),
979                    return_pc,
980                    Some(ptr),
981                    rb.effective_ic_table_ptr(),
982                    fn_this,
983                    dst,
984                    argc,
985                    is_call_new,
986                    js_func.is_async(),
987                    caller_base,
988                    arg_regs,
989                    uses_arguments,
990                );
991                if is_call_new {
992                    let super_key = ctx.common_atoms.__super__;
993                    if let Some(super_val) = js_func.base.get(super_key) {
994                        self.frames[self.frame_index].super_ctor = super_val;
995                    }
996                }
997                Ok(true)
998            } else if js_func.is_builtin() {
999                if is_call_new
1000                    && !ctx.builtin_is_constructor(
1001                        &js_func
1002                            .builtin_atom
1003                            .map(|ba| ctx.get_atom_str(ba).to_string())
1004                            .unwrap_or_default(),
1005                    )
1006                {
1007                    self.set_pending_type_error(ctx, "not a constructor");
1008                    if let Some(exc) = self.pending_throw.take() {
1009                        match self.dispatch_throw_value(ctx, exc) {
1010                            ThrowDispatch::Caught => return Ok(false),
1011                            ThrowDispatch::Uncaught(e) => return Err(e),
1012                            ThrowDispatch::AsyncComplete(ExecutionOutcome::Complete(v)) => {
1013                                self.set_reg(dst, v);
1014                                return Ok(false);
1015                            }
1016                            ThrowDispatch::AsyncComplete(ExecutionOutcome::Yield(v)) => {
1017                                self.set_reg(dst, v);
1018                                return Ok(false);
1019                            }
1020                        }
1021                    }
1022                    return Ok(false);
1023                }
1024                let caller_base = self.cached_registers_base;
1025                let mut args_buf = [JSValue::undefined(); 17];
1026                let builtin_name = js_func
1027                    .builtin_atom
1028                    .map(|ba| ctx.get_atom_str(ba).to_string())
1029                    .unwrap_or_default();
1030                if is_call_new && builtin_name == "symbol_constructor" {
1031                    self.set_pending_type_error(ctx, "Symbol is not a constructor");
1032                    if let Some(exc) = self.pending_throw.take() {
1033                        match self.dispatch_throw_value(ctx, exc) {
1034                            ThrowDispatch::Caught => return Ok(false),
1035                            ThrowDispatch::Uncaught(e) => return Err(e),
1036                            _ => {}
1037                        }
1038                    }
1039                    return Ok(false);
1040                }
1041                let needs_callee = Self::builtin_needs_callee(&builtin_name);
1042                let pass_this = is_call_method && !needs_callee && js_func.builtin_needs_this();
1043                let builtin_arg_count = argc as usize
1044                    + if pass_this { 1 } else { 0 }
1045                    + if needs_callee { 1 } else { 0 };
1046                let mut args_vec = Vec::new();
1047                let args: &[JSValue] = if builtin_arg_count <= 16 {
1048                    let mut idx = 0;
1049                    if needs_callee {
1050                        args_buf[idx] = func_val;
1051                        idx += 1;
1052                    }
1053                    if pass_this {
1054                        args_buf[0] = self.registers[caller_base + obj_reg as usize];
1055                        idx += 1;
1056                    }
1057                    for (i, r) in arg_regs.iter().enumerate() {
1058                        args_buf[idx + i] = self.registers[caller_base + *r as usize];
1059                    }
1060                    &args_buf[..builtin_arg_count]
1061                } else {
1062                    args_vec.reserve(builtin_arg_count);
1063                    if needs_callee {
1064                        args_vec.push(func_val);
1065                    }
1066                    if pass_this {
1067                        args_vec.push(self.registers[caller_base + obj_reg as usize]);
1068                    }
1069                    for r in arg_regs {
1070                        args_vec.push(self.registers[caller_base + *r as usize]);
1071                    }
1072                    &args_vec
1073                };
1074                let result = if let Some(bf) = js_func.builtin_func {
1075                    ctx.call_builtin_direct(bf, args)
1076                } else if let Some(ba) = js_func.builtin_atom {
1077                    let name = ctx.get_atom_str(ba).to_string();
1078                    ctx.call_builtin(&name, args)
1079                } else {
1080                    JSValue::undefined()
1081                };
1082                if let Some(exc) = ctx.pending_exception.take() {
1083                    match self.dispatch_throw_value(ctx, exc) {
1084                        ThrowDispatch::Caught => return Ok(false),
1085                        ThrowDispatch::Uncaught(e) => return Err(e),
1086                        ThrowDispatch::AsyncComplete(o) => match o {
1087                            ExecutionOutcome::Complete(v) => {
1088                                self.set_reg(dst, v);
1089                                return Ok(false);
1090                            }
1091                            ExecutionOutcome::Yield(v) => {
1092                                self.set_reg(dst, v);
1093                                return Ok(false);
1094                            }
1095                        },
1096                    }
1097                }
1098                if is_call_new && !result.is_object_like() {
1099                    if this_val.is_object() {
1100                        this_val
1101                            .as_object_mut()
1102                            .set(ctx.common_atoms.__value__, result);
1103                        if result.is_string() {
1104                            let s = ctx.get_atom_str(result.get_atom()).to_string();
1105                            let char_count = s.chars().count();
1106                            this_val.as_object_mut().define_property(
1107                                ctx.common_atoms.length,
1108                                crate::object::object::PropertyDescriptor {
1109                                    value: Some(JSValue::new_int(char_count as i64)),
1110                                    writable: false,
1111                                    enumerable: false,
1112                                    configurable: false,
1113                                    get: None,
1114                                    set: None,
1115                                },
1116                            );
1117                            if char_count > 0 {
1118                                let elements = this_val.as_object_mut().ensure_elements();
1119                                for c in s.chars() {
1120                                    let atom = ctx.intern(&c.to_string());
1121                                    elements.push(JSValue::new_string(atom));
1122                                }
1123                            }
1124                        }
1125                    }
1126                    self.set_reg(dst, this_val);
1127                } else {
1128                    self.set_reg(dst, result);
1129                }
1130                Ok(false)
1131            } else {
1132                Ok(false)
1133            }
1134        } else if func_val.is_object() && is_call_new {
1135            let obj = func_val.as_object();
1136            if let Some(bound_fn) = obj.get(ctx.common_atoms.__boundFn) {
1137                let caller_base = self.cached_registers_base;
1138                let bound_args_val = obj.get(ctx.common_atoms.__boundArgs).filter(|v| v.is_object());
1139                let bound_len = bound_args_val
1140                    .and_then(|v| v.as_object().get(ctx.common_atoms.length))
1141                    .map(|v| v.get_int() as usize)
1142                    .unwrap_or(0);
1143                let mut actual_args = Vec::with_capacity(bound_len + argc as usize);
1144                if let Some(ref bound_args_val) = bound_args_val {
1145                    let bound_args_obj = bound_args_val.as_object();
1146                    for i in 0..bound_len {
1147                        let key = self.int_atom(i, ctx);
1148                        if let Some(arg_val) = bound_args_obj.get(key) {
1149                            actual_args.push(arg_val);
1150                        }
1151                    }
1152                }
1153                for r in arg_regs {
1154                    actual_args.push(self.registers[caller_base + *r as usize]);
1155                }
1156                let mut new_obj = crate::object::object::JSObject::new();
1157                if bound_fn.is_function() {
1158                    let target_func = bound_fn.as_function();
1159                    let proto_atom = ctx.common_atoms.prototype;
1160                    if let Some(proto_val) = target_func.base.get(proto_atom) {
1161                        if proto_val.is_object_like() {
1162                            let proto_ptr = proto_val.get_ptr();
1163                            new_obj.prototype = Some(proto_ptr as *mut crate::object::object::JSObject);
1164                        }
1165                    }
1166                }
1167                let new_ptr = Box::into_raw(Box::new(new_obj)) as usize;
1168                ctx.runtime_mut().gc_heap_mut().track(new_ptr);
1169                let new_val = JSValue::new_object(new_ptr);
1170                let result = self.call_function_with_this(ctx, bound_fn, new_val, &actual_args);
1171                match result {
1172                    Ok(r) => {
1173                        let final_val = if r.is_object_like() { r } else { new_val };
1174                        self.set_reg(dst, final_val);
1175                    }
1176                    Err(_) => {
1177                        self.set_pending_type_error(ctx, "bound function construct failed");
1178                    }
1179                }
1180                Ok(false)
1181            } else {
1182                let msg = format!(
1183                    "{} is not a function",
1184                    self.format_thrown_value(&func_val, ctx)
1185                );
1186                self.set_pending_type_error(ctx, &msg);
1187                Ok(false)
1188            }
1189        } else if func_val.is_object() && !is_call_new {
1190            let caller_base = self.cached_registers_base;
1191            let mut args_buf = [JSValue::undefined(); 16];
1192            let mut args_vec = Vec::new();
1193            let call_args: &[JSValue] = if argc as usize <= 16 {
1194                for (i, r) in arg_regs.iter().enumerate() {
1195                    args_buf[i] = self.registers[caller_base + *r as usize];
1196                }
1197                &args_buf[..argc as usize]
1198            } else {
1199                args_vec.reserve(argc as usize);
1200                for r in arg_regs.iter() {
1201                    args_vec.push(self.registers[caller_base + *r as usize]);
1202                }
1203                &args_vec
1204            };
1205            match self.call_function_with_this(ctx, func_val, this_val, call_args) {
1206                Ok(result) => {
1207                    self.set_reg(dst, result);
1208                    Ok(false)
1209                }
1210                Err(_) => {
1211                    let msg = format!(
1212                        "{} is not a function",
1213                        self.format_thrown_value(&func_val, ctx)
1214                    );
1215                    self.set_pending_type_error(ctx, &msg);
1216                    if let Some(exc) = self.pending_throw.take() {
1217                        let disp = self.dispatch_throw_value(ctx, exc);
1218                        match disp {
1219                            ThrowDispatch::Caught => Ok(false),
1220                            ThrowDispatch::Uncaught(e) => Err(e),
1221                            ThrowDispatch::AsyncComplete(o) => match o {
1222                                ExecutionOutcome::Complete(v) => {
1223                                    self.set_reg(dst, v);
1224                                    Ok(false)
1225                                }
1226                                _ => Err("call error".to_string()),
1227                            },
1228                        }
1229                    } else {
1230                        Ok(false)
1231                    }
1232                }
1233            }
1234        } else {
1235            let msg = format!(
1236                "{} is not a function",
1237                self.format_thrown_value(&func_val, ctx)
1238            );
1239            self.set_pending_type_error(ctx, &msg);
1240            Ok(false)
1241        }
1242    }
1243
1244    #[inline(always)]
1245    fn read_u8(&mut self) -> u8 {
1246        let pc = self.pc;
1247        let val = unsafe { *self.cached_code_ptr.add(pc) };
1248        self.pc = pc + 1;
1249        val
1250    }
1251
1252    #[inline(always)]
1253    fn read_i64(&mut self) -> i64 {
1254        let val =
1255            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const i64) };
1256        self.pc += 8;
1257        val
1258    }
1259
1260    #[inline(always)]
1261    fn read_i32(&mut self) -> i32 {
1262        let val =
1263            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const i32) };
1264        self.pc += 4;
1265        val
1266    }
1267
1268    #[inline(always)]
1269    fn read_u32(&mut self) -> u32 {
1270        self.read_i32() as u32
1271    }
1272
1273    #[inline(always)]
1274    fn get_reg(&self, idx: u16) -> JSValue {
1275        if self.cached_has_upvalue_sync {
1276            return self.get_reg_upvalue_slow(idx);
1277        }
1278        unsafe { *self.cached_registers_ptr.add(idx as usize) }
1279    }
1280
1281    #[cold]
1282    #[inline(never)]
1283    fn get_reg_upvalue_slow(&self, idx: u16) -> JSValue {
1284        let captured = if idx < 64 {
1285            self.cached_upvalue_sync_bitset & (1u64 << idx) != 0
1286        } else {
1287            true
1288        };
1289        if captured {
1290            let frame = &self.frames[self.frame_index];
1291            if let Some(cell) = frame.upvalue_sync_map.as_ref().and_then(|m| m.get(&idx)) {
1292                return cell.get();
1293            }
1294        }
1295        unsafe { *self.cached_registers_ptr.add(idx as usize) }
1296    }
1297
1298    #[inline(always)]
1299    fn set_reg(&mut self, idx: u16, val: JSValue) {
1300        unsafe {
1301            *self.cached_registers_ptr.add(idx as usize) = val;
1302        }
1303        if self.cached_has_upvalue_sync {
1304            self.set_reg_upvalue_slow(idx, val);
1305        }
1306    }
1307
1308    #[cold]
1309    #[inline(never)]
1310    fn set_reg_upvalue_slow(&mut self, idx: u16, val: JSValue) {
1311        let captured = if idx < 64 {
1312            self.cached_upvalue_sync_bitset & (1u64 << idx) != 0
1313        } else {
1314            true
1315        };
1316        if captured {
1317            let frame = &self.frames[self.frame_index];
1318            if let Some(cell) = frame.upvalue_sync_map.as_ref().and_then(|m| m.get(&idx)) {
1319                cell.set(val);
1320            }
1321        }
1322    }
1323
1324    fn format_stack_trace(&self, ctx: &JSContext) -> String {
1325        let mut frames = Vec::new();
1326        for fi in (0..=self.frame_index).rev() {
1327            let frame = &self.frames[fi];
1328            let func_name = if let Some(fptr) = frame.function_ptr {
1329                let func_val = JSValue::new_function(fptr);
1330                let js_func = func_val.as_function();
1331                ctx.get_atom_str(js_func.name).to_string()
1332            } else {
1333                "<top-level>".to_string()
1334            };
1335            let mut location = crate::compiler::location::SourceLocation::unknown();
1336            if let Some(fptr) = frame.function_ptr {
1337                let func_val = JSValue::new_function(fptr);
1338                let js_func = func_val.as_function();
1339                location.filename = js_func.source_filename.clone();
1340                if let Some(ref table) = js_func.line_number_table {
1341                    if let Some(line) = table.lookup_line(frame.current_pc as u32) {
1342                        location.line = line;
1343                    }
1344                }
1345            }
1346            frames.push(crate::compiler::location::FrameInfo::new(
1347                func_name, location,
1348            ));
1349        }
1350        let mut result = String::new();
1351        for frame in frames.iter().rev() {
1352            if !result.is_empty() {
1353                result.push('\n');
1354            }
1355            result.push_str(&format!(
1356                "  at {} ({}:{}:{})",
1357                frame.function_name,
1358                frame.location.filename,
1359                frame.location.line,
1360                frame.location.column
1361            ));
1362        }
1363        result
1364    }
1365
1366    #[cold]
1367    pub fn set_pending_type_error(&mut self, ctx: &mut JSContext, msg: &str) {
1368        use crate::object::object::JSObject;
1369        let mut err = JSObject::new();
1370        let name_atom = ctx.intern("name");
1371        let msg_atom = ctx.intern("message");
1372        let type_error_atom = ctx.intern("TypeError");
1373        let msg_str_atom = ctx.intern(msg);
1374        err.set(name_atom, JSValue::new_string(type_error_atom));
1375        err.set(msg_atom, JSValue::new_string(msg_str_atom));
1376        if let Some(proto) = ctx.get_type_error_prototype() {
1377            err.prototype = Some(proto);
1378        }
1379        let ptr = Box::into_raw(Box::new(err)) as usize;
1380        ctx.runtime_mut().gc_heap_mut().track(ptr);
1381        self.pending_throw = Some(JSValue::new_object(ptr));
1382    }
1383
1384    fn rethrow_last_caught(&mut self) {
1385        if let Some(exc) = self.last_caught_exception.take() {
1386            self.pending_throw = Some(exc);
1387        }
1388    }
1389
1390    #[cold]
1391    fn set_pending_reference_error(&mut self, ctx: &mut JSContext, msg: &str) {
1392        use crate::object::object::JSObject;
1393        let mut err = JSObject::new();
1394        let name_atom = ctx.intern("name");
1395        let msg_atom = ctx.intern("message");
1396        let ref_error_atom = ctx.intern("ReferenceError");
1397        let msg_str_atom = ctx.intern(msg);
1398        err.set(name_atom, JSValue::new_string(ref_error_atom));
1399        err.set(msg_atom, JSValue::new_string(msg_str_atom));
1400        if let Some(proto) = ctx.get_reference_error_prototype() {
1401            err.prototype = Some(proto);
1402        }
1403        let ptr = Box::into_raw(Box::new(err)) as usize;
1404        ctx.runtime_mut().gc_heap_mut().track(ptr);
1405        self.pending_throw = Some(JSValue::new_object(ptr));
1406    }
1407
1408    #[cold]
1409    fn dispatch_throw_value(&mut self, ctx: &mut JSContext, value: JSValue) -> ThrowDispatch {
1410        let find_async_frame = |frames: &[CallFrame], frame_index: usize| {
1411            for i in (0..=frame_index).rev() {
1412                if frames[i].is_async {
1413                    return Some(i);
1414                }
1415            }
1416            None
1417        };
1418
1419        if let Some(handler) = self.exception_handlers.last().cloned() {
1420            self.exception_handlers.pop();
1421
1422            if self.frame_index != handler.frame_index {
1423                if let Some(async_idx) = find_async_frame(&self.frames, self.frame_index) {
1424                    if async_idx > handler.frame_index {
1425                        while self.frame_index > async_idx {
1426                            if self.frame_index == 0 {
1427                                break;
1428                            }
1429                            self.frame_index -= 1;
1430                        }
1431                        let rejected = ctx.call_builtin("promise_reject", &[value]);
1432                        if self.frame_index == 0 {
1433                            return ThrowDispatch::AsyncComplete(ExecutionOutcome::Complete(
1434                                rejected,
1435                            ));
1436                        }
1437                        let return_pc = self.frames[self.frame_index].return_pc;
1438                        let dst_reg = self.frames[self.frame_index].dst_reg;
1439                        self.frame_index -= 1;
1440                        self.pc = return_pc;
1441                        self.refresh_cache();
1442                        self.set_reg(dst_reg, rejected);
1443                        return ThrowDispatch::Caught;
1444                    }
1445                }
1446                while self.frame_index > handler.frame_index {
1447                    if self.frame_index == 0 {
1448                        break;
1449                    }
1450                    self.frame_index -= 1;
1451                }
1452            }
1453
1454            if handler
1455                .finally_pc
1456                .map_or(false, |fp| fp == handler.catch_pc)
1457            {
1458                self.finally_rethrow = Some(value);
1459            }
1460            self.pc = handler.catch_pc;
1461            self.refresh_cache();
1462            self.set_reg(0, value);
1463            ThrowDispatch::Caught
1464        } else {
1465            if let Some(async_idx) = find_async_frame(&self.frames, self.frame_index) {
1466                let rejected = ctx.call_builtin("promise_reject", &[value]);
1467                if async_idx == 0 {
1468                    return ThrowDispatch::AsyncComplete(ExecutionOutcome::Complete(rejected));
1469                }
1470                while self.frame_index > async_idx {
1471                    self.frame_index -= 1;
1472                }
1473                let return_pc = self.frames[self.frame_index].return_pc;
1474                let dst_reg = self.frames[self.frame_index].dst_reg;
1475                self.frame_index -= 1;
1476                self.pc = return_pc;
1477                self.refresh_cache();
1478                self.set_reg(dst_reg, rejected);
1479                return ThrowDispatch::Caught;
1480            }
1481            self.pending_throw = Some(value);
1482            let msg = self.format_thrown_value(&value, ctx);
1483            let trace = self.format_stack_trace(ctx);
1484            ThrowDispatch::Uncaught(format!("Uncaught: {}\nStack trace:\n{}", msg, trace))
1485        }
1486    }
1487
1488    fn format_thrown_value(&self, value: &JSValue, ctx: &JSContext) -> String {
1489        if value.is_string() {
1490            return ctx.get_atom_str(value.get_atom()).to_string();
1491        }
1492        if value.is_int() {
1493            return value.get_int().to_string();
1494        }
1495        if value.is_float() {
1496            return value.get_float().to_string();
1497        }
1498        if value.is_bool() {
1499            return value.get_bool().to_string();
1500        }
1501        if value.is_null() {
1502            return "null".to_string();
1503        }
1504        if value.is_undefined() {
1505            return "undefined".to_string();
1506        }
1507        if value.is_object() {
1508            let ptr = value.get_ptr();
1509            let obj = unsafe { &*(ptr as *const crate::object::JSObject) };
1510            let name = obj.get(ctx.common_atoms.name).and_then(|v| {
1511                if v.is_string() {
1512                    Some(ctx.get_atom_str(v.get_atom()).to_string())
1513                } else {
1514                    None
1515                }
1516            });
1517            let message = obj.get(ctx.common_atoms.message).and_then(|v| {
1518                if v.is_string() {
1519                    Some(ctx.get_atom_str(v.get_atom()).to_string())
1520                } else {
1521                    None
1522                }
1523            });
1524            match (name, message) {
1525                (Some(n), Some(m)) => {
1526                    if m.is_empty() {
1527                        return n;
1528                    }
1529                    return format!("{}: {}", n, m);
1530                }
1531                (Some(n), _) => return n,
1532                (None, Some(m)) => {
1533                    if m.is_empty() {
1534                        return "Error".to_string();
1535                    }
1536                    return m;
1537                }
1538                _ => {}
1539            }
1540            return "[object Object]".to_string();
1541        }
1542        if value.is_function() {
1543            return "function".to_string();
1544        }
1545        if value.is_symbol() {
1546            return "Symbol".to_string();
1547        }
1548        if value.is_bigint() {
1549            return "BigInt".to_string();
1550        }
1551        "<value>".to_string()
1552    }
1553
1554    #[inline(never)]
1555    fn fill_gc_roots(&mut self, ctx: &JSContext) {
1556        let roots = &mut self.gc_roots;
1557        roots.clear();
1558        let global = ctx.global();
1559        if global.is_object() || global.is_function() {
1560            roots.push(global);
1561        }
1562
1563        for fi in 0..=self.frame_index {
1564            let frame = &self.frames[fi];
1565            let base = frame.registers_base;
1566            let count = frame.registers_count;
1567            for i in 0..count {
1568                let v = self.registers[base + i];
1569                if v.is_object() || v.is_function() {
1570                    roots.push(v);
1571                }
1572            }
1573            if let Some(ptr) = frame.function_ptr {
1574                roots.push(JSValue::new_function(ptr));
1575            }
1576            let tv = frame.this_value;
1577            if tv.is_object() || tv.is_function() {
1578                roots.push(tv);
1579            }
1580            let sv = frame.super_ctor;
1581            if sv.is_object() || sv.is_function() {
1582                roots.push(sv);
1583            }
1584        }
1585
1586        macro_rules! push_proto {
1587            ($getter:ident) => {
1588                if let Some(ptr) = ctx.$getter() {
1589                    roots.push(JSValue::new_object(ptr as usize));
1590                }
1591            };
1592        }
1593        push_proto!(get_string_prototype);
1594        push_proto!(get_number_prototype);
1595        push_proto!(get_array_prototype);
1596        push_proto!(get_regexp_prototype);
1597        push_proto!(get_object_prototype);
1598        push_proto!(get_function_prototype);
1599        push_proto!(get_map_prototype);
1600        push_proto!(get_set_prototype);
1601        push_proto!(get_weakmap_prototype);
1602        push_proto!(get_weakset_prototype);
1603        push_proto!(get_error_prototype);
1604        push_proto!(get_type_error_prototype);
1605        push_proto!(get_weakref_prototype);
1606        push_proto!(get_finalization_registry_prototype);
1607        push_proto!(get_generator_prototype);
1608        push_proto!(get_async_generator_prototype);
1609        push_proto!(get_promise_prototype);
1610
1611        for &v in &ctx.runtime().gc_heap().extra_roots {
1612            if v.is_object() || v.is_function() {
1613                roots.push(v);
1614            }
1615        }
1616    }
1617
1618    pub fn collect_roots(&self, ctx: &JSContext) -> Vec<JSValue> {
1619        let mut roots = Vec::with_capacity(256);
1620        let global = ctx.global();
1621        if global.is_object() || global.is_function() {
1622            roots.push(global);
1623        }
1624
1625        for fi in 0..=self.frame_index {
1626            let frame = &self.frames[fi];
1627            let base = frame.registers_base;
1628            let count = frame.registers_count;
1629            for i in 0..count {
1630                let v = self.registers[base + i];
1631                if v.is_object() || v.is_function() {
1632                    roots.push(v);
1633                }
1634            }
1635            if let Some(ptr) = frame.function_ptr {
1636                roots.push(JSValue::new_function(ptr));
1637            }
1638            let tv = frame.this_value;
1639            if tv.is_object() || tv.is_function() {
1640                roots.push(tv);
1641            }
1642            let sv = frame.super_ctor;
1643            if sv.is_object() || sv.is_function() {
1644                roots.push(sv);
1645            }
1646        }
1647
1648        macro_rules! push_proto {
1649            ($getter:ident) => {
1650                if let Some(ptr) = ctx.$getter() {
1651                    roots.push(JSValue::new_object(ptr as usize));
1652                }
1653            };
1654        }
1655        push_proto!(get_string_prototype);
1656        push_proto!(get_number_prototype);
1657        push_proto!(get_array_prototype);
1658        push_proto!(get_regexp_prototype);
1659        push_proto!(get_object_prototype);
1660        push_proto!(get_function_prototype);
1661        push_proto!(get_map_prototype);
1662        push_proto!(get_set_prototype);
1663        push_proto!(get_weakmap_prototype);
1664        push_proto!(get_weakset_prototype);
1665        push_proto!(get_error_prototype);
1666        push_proto!(get_type_error_prototype);
1667        push_proto!(get_weakref_prototype);
1668        push_proto!(get_finalization_registry_prototype);
1669        push_proto!(get_generator_prototype);
1670        push_proto!(get_async_generator_prototype);
1671        push_proto!(get_promise_prototype);
1672
1673        for &v in &ctx.runtime().gc_heap().extra_roots {
1674            if v.is_object() || v.is_function() {
1675                roots.push(v);
1676            }
1677        }
1678        roots
1679    }
1680
1681    #[inline(always)]
1682    pub fn maybe_gc(&mut self, ctx: &mut JSContext) {
1683        if self.allocation_count >= GC_CHECK_INTERVAL {
1684            self.allocation_count = 0;
1685            let heap = ctx.runtime().gc_heap();
1686            let do_minor = heap.nursery_is_full();
1687            let do_full = heap.should_collect();
1688
1689            if do_minor || do_full {
1690                // Skip minor GC if nursery has no collectible objects
1691                // (e.g., only pinned survivors that never die)
1692                let skip_minor = do_minor && heap.nursery_collectible_count() == 0
1693                    && heap.nursery_pinned_count() > 0;
1694
1695                if do_minor && !skip_minor {
1696                    self.fill_gc_roots(ctx);
1697                    let roots_len = self.gc_roots.len();
1698                    let roots_ptr = self.gc_roots.as_mut_ptr();
1699                    let roots = unsafe { std::slice::from_raw_parts_mut(roots_ptr, roots_len) };
1700                    let _ = ctx.runtime_mut().minor_gc(roots);
1701                }
1702
1703                if do_full || ctx.runtime().gc_heap().should_collect() {
1704                    self.run_gc(ctx);
1705                }
1706            }
1707        }
1708    }
1709
1710    pub fn minor_gc(&mut self, ctx: &mut JSContext) -> usize {
1711        self.fill_gc_roots(ctx);
1712        let roots_ptr = self.gc_roots.as_ptr();
1713        let roots_len = self.gc_roots.len();
1714        let roots = unsafe { std::slice::from_raw_parts(roots_ptr, roots_len) };
1715        ctx.runtime_mut().minor_gc(roots)
1716    }
1717
1718    pub fn run_gc(&mut self, ctx: &mut JSContext) -> usize {
1719        self.fill_gc_roots(ctx);
1720        let roots_ptr = self.gc_roots.as_ptr();
1721        let roots_len = self.gc_roots.len();
1722        let roots = unsafe { std::slice::from_raw_parts(roots_ptr, roots_len) };
1723        let freed = ctx.runtime_mut().run_gc(roots);
1724
1725        if ctx.runtime().gc_heap().deleted_props_count > 0 {
1726            let live_objects: Vec<(usize, u8)> = {
1727                let heap = ctx.runtime().gc_heap();
1728                let mut objs = Vec::new();
1729                heap.for_each_live_object(|ptr, tag| objs.push((ptr, tag)));
1730                objs
1731            };
1732            {
1733                let cache = ctx.shape_cache_mut();
1734                for (ptr, tag) in live_objects {
1735                    unsafe {
1736                        if tag == crate::runtime::gc::TAG_ARRAY {
1737                            let arr = &mut *(ptr as *mut crate::object::array_obj::JSArrayObject);
1738                            arr.header.compact_props(cache);
1739                        } else {
1740                            let obj = &mut *(ptr as *mut crate::object::object::JSObject);
1741                            obj.compact_props(cache);
1742                        }
1743                    }
1744                }
1745            }
1746
1747            ctx.runtime_mut().gc_heap_mut().deleted_props_count = 0;
1748        }
1749        freed
1750    }
1751
1752    pub fn call_function(
1753        &mut self,
1754        ctx: &mut JSContext,
1755        func: JSValue,
1756        args: &[JSValue],
1757    ) -> Result<JSValue, String> {
1758        self.call_function_with_this(ctx, func, JSValue::undefined(), args)
1759    }
1760
1761    pub fn construct(
1762        &mut self,
1763        ctx: &mut JSContext,
1764        ctor: JSValue,
1765        args: &[JSValue],
1766    ) -> Result<JSValue, String> {
1767        if !ctor.is_function() {
1768            return Err("not a constructor".to_string());
1769        }
1770
1771        let js_func = ctor.as_function();
1772
1773        let mut new_obj = crate::object::object::JSObject::new();
1774
1775        if !js_func.cached_prototype_ptr.is_null() {
1776            new_obj.prototype = Some(js_func.cached_prototype_ptr);
1777        } else {
1778            let proto_key = ctx.common_atoms.prototype;
1779            if let Some(proto_val) = js_func.base.get(proto_key) {
1780                if proto_val.is_object_like() {
1781                    let cptr = proto_val.get_ptr() as *mut crate::object::object::JSObject;
1782                    ctor.as_function_mut().cached_prototype_ptr = cptr;
1783                    new_obj.prototype = Some(cptr);
1784                }
1785            } else if !js_func.is_builtin() {
1786                let mut pobj = crate::object::object::JSObject::new();
1787                pobj.set(ctx.common_atoms.constructor, ctor);
1788                if let Some(opp) = ctx.get_object_prototype() {
1789                    pobj.prototype = Some(opp);
1790                }
1791                let pp = Box::into_raw(Box::new(pobj)) as usize;
1792                ctx.runtime_mut().gc_heap_mut().track(pp);
1793                ctor.as_function_mut().base.set(proto_key, JSValue::new_object(pp));
1794                let cptr = pp as *mut crate::object::object::JSObject;
1795                ctor.as_function_mut().cached_prototype_ptr = cptr;
1796                new_obj.prototype = Some(cptr);
1797            }
1798        }
1799
1800        let new_ptr = Box::into_raw(Box::new(new_obj)) as usize;
1801        ctx.runtime_mut().gc_heap_mut().track(new_ptr);
1802        let new_val = JSValue::new_object(new_ptr);
1803
1804        match self.call_function_with_this(ctx, ctor, new_val, args) {
1805            Ok(result) => {
1806                if result.is_object_like() {
1807                    Ok(result)
1808                } else {
1809                    Ok(new_val)
1810                }
1811            }
1812            Err(e) => Err(e),
1813        }
1814    }
1815
1816    pub fn call_function_with_this(
1817        &mut self,
1818        ctx: &mut JSContext,
1819        func: JSValue,
1820        this_value: JSValue,
1821        args: &[JSValue],
1822    ) -> Result<JSValue, String> {
1823        if func.is_object() {
1824            let obj = func.as_object();
1825            if let Some(bound_fn) = obj.get(ctx.common_atoms.__boundFn) {
1826                let bound_this = obj.get(ctx.common_atoms.__boundThis).unwrap_or(this_value);
1827                let bound_args_val = obj
1828                    .get(ctx.common_atoms.__boundArgs)
1829                    .filter(|val| val.is_object());
1830
1831                let bound_len = bound_args_val
1832                    .and_then(|v| v.as_object().get(ctx.common_atoms.length))
1833                    .map(|v| v.get_int() as usize)
1834                    .unwrap_or(0);
1835
1836                let mut actual_args = Vec::with_capacity(bound_len.saturating_add(args.len()));
1837                if let Some(bound_args_val) = bound_args_val {
1838                    let bound_args_obj = bound_args_val.as_object();
1839                    for i in 0..bound_len {
1840                        let key = self.int_atom(i, ctx);
1841                        if let Some(arg_val) = bound_args_obj.get(key) {
1842                            actual_args.push(arg_val);
1843                        }
1844                    }
1845                }
1846                actual_args.extend_from_slice(args);
1847                return self.call_function_with_this(ctx, bound_fn, bound_this, &actual_args);
1848            }
1849        }
1850
1851        if !func.is_function() {
1852            return Err("call_function: not a function".to_string());
1853        }
1854
1855        let ptr = func.get_ptr();
1856        let js_func = func.as_function();
1857
1858        if js_func.is_builtin() {
1859            let builtin_name = js_func
1860                .builtin_atom
1861                .map(|ba| ctx.get_atom_str(ba).to_string())
1862                .unwrap_or_default();
1863            let needs_this = ctx.builtin_needs_this(&builtin_name);
1864            let needs_callee = Self::builtin_needs_callee(&builtin_name);
1865
1866            let mut call_args_vec: Vec<JSValue> = Vec::new();
1867            let call_args: &[JSValue] = if needs_callee {
1868                call_args_vec.reserve(args.len() + 1);
1869                call_args_vec.push(func);
1870                call_args_vec.extend_from_slice(args);
1871                &call_args_vec
1872            } else if needs_this {
1873                call_args_vec.reserve(args.len() + 1);
1874                call_args_vec.push(this_value);
1875                call_args_vec.extend_from_slice(args);
1876                &call_args_vec
1877            } else {
1878                args
1879            };
1880
1881            if let Some(bf) = js_func.builtin_func {
1882                let result = ctx.call_builtin_direct(bf, call_args);
1883                if let Some(exc) = ctx.pending_exception.take() {
1884                    let msg = if exc.is_string() {
1885                        ctx.get_atom_str(exc.get_atom()).to_string()
1886                    } else if exc.is_object() {
1887                        let obj = exc.as_object();
1888                        let m = obj.get(ctx.common_atoms.message);
1889                        let n = obj.get(ctx.intern("name"));
1890                        match (n, m) {
1891                            (Some(nv), Some(mv)) if nv.is_string() && mv.is_string() => {
1892                                format!(
1893                                    "{}: {}",
1894                                    ctx.get_atom_str(nv.get_atom()),
1895                                    ctx.get_atom_str(mv.get_atom())
1896                                )
1897                            }
1898                            _ => "builtin error".to_string(),
1899                        }
1900                    } else {
1901                        "builtin error".to_string()
1902                    };
1903                    self.last_caught_exception = Some(exc);
1904                    return Err(msg);
1905                }
1906                return Ok(result);
1907            } else if let Some(ba) = js_func.builtin_atom {
1908                let result = ctx.call_builtin(&ctx.get_atom_str(ba).to_string(), call_args);
1909                if let Some(exc) = ctx.pending_exception.take() {
1910                    let msg = if exc.is_string() {
1911                        ctx.get_atom_str(exc.get_atom()).to_string()
1912                    } else if exc.is_object() {
1913                        let obj = exc.as_object();
1914                        let m = obj.get(ctx.common_atoms.message);
1915                        let n = obj.get(ctx.intern("name"));
1916                        match (n, m) {
1917                            (Some(nv), Some(mv)) if nv.is_string() && mv.is_string() => {
1918                                format!(
1919                                    "{}: {}",
1920                                    ctx.get_atom_str(nv.get_atom()),
1921                                    ctx.get_atom_str(mv.get_atom())
1922                                )
1923                            }
1924                            _ => "builtin error".to_string(),
1925                        }
1926                    } else {
1927                        "builtin error".to_string()
1928                    };
1929                    self.last_caught_exception = Some(exc);
1930                    return Err(msg);
1931                }
1932                return Ok(result);
1933            }
1934            return Ok(JSValue::undefined());
1935        }
1936
1937        if let Some(ref rb) = js_func.bytecode {
1938            if js_func.is_generator() {
1939                let snapshot_this = if !js_func.is_strict()
1940                    && (this_value.is_undefined() || this_value.is_null())
1941                {
1942                    ctx.global()
1943                } else {
1944                    this_value
1945                };
1946                let min_slots = 1 + args.len();
1947                let snapshot_len = (rb.locals_count as usize).max(min_slots);
1948                let mut snapshot = vec![JSValue::undefined(); snapshot_len];
1949                snapshot[0] = snapshot_this;
1950                for (i, arg) in args.iter().enumerate() {
1951                    snapshot[i + 1] = *arg;
1952                }
1953                let has_params = (args.len() as u32) < rb.param_count as u32;
1954                let (result_snapshot, result_pc, result_done) = if has_params {
1955                    match self.execute_generator_step(ctx, rb, &snapshot, 0) {
1956                        Ok((_val, new_snapshot, new_pc, done)) => (new_snapshot, new_pc, done),
1957                        Err(_) => (snapshot, 0, false),
1958                    }
1959                } else {
1960                    (snapshot, 0, false)
1961                };
1962                let mut gen_obj = crate::object::object::JSObject::new();
1963                gen_obj.set_is_generator(true);
1964                if let Some(proto_ptr) = ctx.get_generator_prototype() {
1965                    gen_obj.prototype = Some(proto_ptr);
1966                }
1967                gen_obj.set_generator_state(crate::object::object::GeneratorState {
1968                    bytecode: Box::new(rb.clone()),
1969                    snapshot: result_snapshot,
1970                    pc: result_pc,
1971                    done: result_done,
1972                });
1973                let gen_ptr = Box::into_raw(Box::new(gen_obj)) as usize;
1974                ctx.runtime_mut().gc_heap_mut().track(gen_ptr);
1975                return Ok(JSValue::new_object(gen_ptr));
1976            }
1977
1978            let saved_frame_index = self.frame_index;
1979            let saved_pc = self.pc;
1980            let saved_r0 = self.get_reg(0);
1981            let saved_handlers: Vec<_> = std::mem::take(&mut self.exception_handlers);
1982
1983            let fn_this =
1984                if !js_func.is_strict() && (this_value.is_undefined() || this_value.is_null()) {
1985                    ctx.global()
1986                } else {
1987                    this_value
1988                };
1989            self.push_frame_raw(
1990                rb.locals_count,
1991                rb.effective_code_ptr(),
1992                rb.effective_code_len(),
1993                rb.effective_const_ptr(),
1994                rb.effective_const_len(),
1995                self.frames[self.frame_index].bytecode_len,
1996                Some(ptr),
1997                fn_this,
1998                0,
1999                args.len() as u16,
2000                false,
2001                js_func.is_async(),
2002                args,
2003                js_func.uses_arguments(),
2004            );
2005
2006            let result = self.execute_inner(ctx, rb, false, 0, false);
2007            let return_value = self.get_reg(0);
2008
2009            let pending_exc = result
2010                .as_ref()
2011                .err()
2012                .and_then(|_| self.pending_throw.take());
2013
2014            while self.frame_index > saved_frame_index {
2015                self.pop_frame(JSValue::undefined());
2016            }
2017            self.pc = saved_pc;
2018            self.exception_handlers = saved_handlers;
2019            self.refresh_cache();
2020            self.set_reg(0, saved_r0);
2021
2022            if let Some(exc) = pending_exc {
2023                self.last_caught_exception = Some(exc.clone());
2024                ctx.pending_exception = Some(exc);
2025                return Err("".to_string());
2026            }
2027
2028            return match result {
2029                Ok(_) => Ok(return_value),
2030                Err(e) => Err(e),
2031            };
2032        }
2033
2034        Ok(JSValue::undefined())
2035    }
2036
2037    pub fn execute(
2038        &mut self,
2039        ctx: &mut JSContext,
2040        bytecode: &Bytecode,
2041    ) -> Result<ExecutionOutcome, String> {
2042        let result = self.execute_inner(ctx, bytecode, false, 0, true);
2043
2044        if !self.ctx_ptr.is_null() {
2045            unsafe {
2046                let count = self.frames[0].registers_count;
2047                for i in 0..count {
2048                    self.registers[i].release_atoms_in(&mut *self.ctx_ptr);
2049                    self.registers[i] = JSValue::undefined();
2050                }
2051            }
2052        }
2053        self.ctx_ptr = std::ptr::null_mut();
2054        result
2055    }
2056
2057    pub fn execute_preserving_registers(
2058        &mut self,
2059        ctx: &mut JSContext,
2060        bytecode: &Bytecode,
2061    ) -> Result<ExecutionOutcome, String> {
2062        let result = self.execute_inner(ctx, bytecode, true, 0, true);
2063        self.ctx_ptr = std::ptr::null_mut();
2064        result
2065    }
2066
2067    pub fn execute_eval(
2068        &mut self,
2069        ctx: &mut JSContext,
2070        bytecode: &Bytecode,
2071        this_value: JSValue,
2072        caller_vm_ptr: Option<usize>,
2073    ) -> Result<ExecutionOutcome, String> {
2074        self.ctx_ptr = ctx;
2075        self.caller_vm = caller_vm_ptr;
2076        ctx.set_register_vm_ptr(Some(self as *mut VM as usize));
2077
2078        let needed = (bytecode.locals_count as usize).max(1);
2079        if needed > self.registers.len() {
2080            self.registers.resize(needed, JSValue::undefined());
2081        }
2082        self.registers[0] = this_value;
2083
2084        let frame = &mut self.frames[0];
2085        frame.registers_base = 0;
2086        frame.registers_count = needed;
2087        frame.locals_count = bytecode.locals_count;
2088        frame.bytecode_ptr = bytecode.effective_code_ptr();
2089        frame.bytecode_len = bytecode.effective_code_len();
2090        frame.constants_ptr = bytecode.effective_const_ptr();
2091        frame.constants_len = bytecode.effective_const_len();
2092        frame.return_pc = 0;
2093        frame.function_ptr = None;
2094        frame.this_value = this_value;
2095        frame.saved_args.clear();
2096        frame.upvalue_sync_map = None;
2097        frame.upvalue_sync_bitset = 0;
2098        frame.uses_arguments = false;
2099        frame.var_name_map = std::rc::Rc::as_ptr(&bytecode.var_name_to_slot);
2100        frame.ic_table_ptr = bytecode.effective_ic_table_ptr();
2101
2102        self.frame_index = 0;
2103        self.exception_handlers.clear();
2104        self.frames[0].is_strict_frame = bytecode.is_strict;
2105        self.refresh_cache();
2106
2107        let result = self.execute_inner(ctx, bytecode, true, 0, false);
2108        self.ctx_ptr = std::ptr::null_mut();
2109        result
2110    }
2111
2112    pub fn direct_eval(&mut self, ctx: &mut JSContext, source: &str) -> Result<JSValue, String> {
2113        let is_strict_caller = self.frames[self.frame_index].is_strict_frame;
2114
2115        let trimmed = source.trim();
2116        if trimmed.starts_with("var ")
2117            || trimmed.starts_with("let ")
2118            || trimmed.starts_with("const ")
2119        {
2120            let decl_kw = if trimmed.starts_with("var ") {
2121                "var "
2122            } else if trimmed.starts_with("let ") {
2123                "let "
2124            } else {
2125                "const "
2126            };
2127            let after_kw = &trimmed[decl_kw.len()..].trim_start();
2128            let var_name = after_kw
2129                .split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$')
2130                .next()
2131                .unwrap_or("");
2132            if var_name == "arguments" {
2133                let mut err = crate::object::object::JSObject::new();
2134                err.set(
2135                    ctx.intern("name"),
2136                    JSValue::new_string(ctx.intern("SyntaxError")),
2137                );
2138                err.set(
2139                    ctx.intern("message"),
2140                    JSValue::new_string(ctx.intern("arguments not allowed in direct eval")),
2141                );
2142                if let Some(proto) = ctx.get_syntax_error_prototype() {
2143                    err.prototype = Some(proto);
2144                }
2145                let ptr = Box::into_raw(Box::new(err)) as usize;
2146                ctx.runtime_mut().gc_heap_mut().track(ptr);
2147                let exc = JSValue::new_object(ptr);
2148                match self.dispatch_throw_value(ctx, exc) {
2149                    ThrowDispatch::Caught => return Ok(JSValue::undefined()),
2150                    ThrowDispatch::Uncaught(e) => return Err(e),
2151                    ThrowDispatch::AsyncComplete(o) => match o {
2152                        ExecutionOutcome::Complete(v) => return Ok(v),
2153                        _ => return Err("async eval error".to_string()),
2154                    },
2155                }
2156            }
2157        }
2158
2159        let caller_vm_ptr = self as *mut VM;
2160        match crate::compiler::eval_code_via_ast_with_opt_level_as_eval_with_caller(
2161            ctx,
2162            source,
2163            ctx.get_compiler_opt_level(),
2164            is_strict_caller,
2165            caller_vm_ptr,
2166        ) {
2167            Ok(v) => Ok(v),
2168            Err(e) => self.convert_eval_error_to_exc(ctx, e),
2169        }
2170    }
2171
2172    fn convert_eval_error_to_exc(
2173        &mut self,
2174        ctx: &mut JSContext,
2175        e: String,
2176    ) -> Result<JSValue, String> {
2177        if e.starts_with("Uncaught:") {
2178            let value_str = e[9..].split('\n').next().unwrap_or("").trim();
2179            let is_error = value_str.contains("Error:")
2180                || value_str.starts_with("[object ")
2181                || value_str.starts_with("function ");
2182            if is_error {
2183                let mut err = crate::object::object::JSObject::new();
2184                let name = if value_str.starts_with("SyntaxError") {
2185                    "SyntaxError"
2186                } else if value_str.starts_with("ReferenceError") {
2187                    "ReferenceError"
2188                } else if value_str.starts_with("TypeError") {
2189                    "TypeError"
2190                } else if value_str.starts_with("RangeError") {
2191                    "RangeError"
2192                } else {
2193                    "Error"
2194                };
2195                err.set(ctx.intern("name"), JSValue::new_string(ctx.intern(name)));
2196                err.set(ctx.intern("message"), JSValue::new_string(ctx.intern(&e)));
2197                match name {
2198                    "TypeError" => {
2199                        if let Some(proto) = ctx.get_type_error_prototype() {
2200                            err.prototype = Some(proto);
2201                        }
2202                    }
2203                    "SyntaxError" => {
2204                        if let Some(proto) = ctx.get_syntax_error_prototype() {
2205                            err.prototype = Some(proto);
2206                        }
2207                    }
2208                    "ReferenceError" => {
2209                        if let Some(proto) = ctx.get_reference_error_prototype() {
2210                            err.prototype = Some(proto);
2211                        }
2212                    }
2213                    _ => {
2214                        if let Some(proto) = ctx.get_error_prototype() {
2215                            err.prototype = Some(proto);
2216                        }
2217                    }
2218                }
2219                let ptr = Box::into_raw(Box::new(err)) as usize;
2220                ctx.runtime_mut().gc_heap_mut().track(ptr);
2221                let exc = JSValue::new_object(ptr);
2222                match self.dispatch_throw_value(ctx, exc) {
2223                    ThrowDispatch::Caught => Ok(JSValue::undefined()),
2224                    ThrowDispatch::Uncaught(e) => Err(e),
2225                    ThrowDispatch::AsyncComplete(o) => match o {
2226                        ExecutionOutcome::Complete(v) => Ok(v),
2227                        _ => Err("async eval error".to_string()),
2228                    },
2229                }
2230            } else {
2231                let raw_value = if let Ok(n) = value_str.parse::<i64>() {
2232                    JSValue::new_int(n)
2233                } else if let Ok(f) = value_str.parse::<f64>() {
2234                    JSValue::new_float(f)
2235                } else if value_str == "true" {
2236                    JSValue::bool(true)
2237                } else if value_str == "false" {
2238                    JSValue::bool(false)
2239                } else if value_str == "null" {
2240                    JSValue::null()
2241                } else if value_str == "undefined" {
2242                    JSValue::undefined()
2243                } else {
2244                    JSValue::undefined()
2245                };
2246                match self.dispatch_throw_value(ctx, raw_value) {
2247                    ThrowDispatch::Caught => Ok(JSValue::undefined()),
2248                    ThrowDispatch::Uncaught(e) => Err(e),
2249                    ThrowDispatch::AsyncComplete(o) => match o {
2250                        ExecutionOutcome::Complete(v) => Ok(v),
2251                        _ => Err("async eval error".to_string()),
2252                    },
2253                }
2254            }
2255        } else if e.starts_with("Parse error") || e.starts_with("SyntaxError") {
2256            let mut err = crate::object::object::JSObject::new();
2257            err.set(
2258                ctx.intern("name"),
2259                JSValue::new_string(ctx.intern("SyntaxError")),
2260            );
2261            err.set(ctx.intern("message"), JSValue::new_string(ctx.intern(&e)));
2262            if let Some(proto) = ctx.get_syntax_error_prototype() {
2263                err.prototype = Some(proto);
2264            }
2265            let ptr = Box::into_raw(Box::new(err)) as usize;
2266            ctx.runtime_mut().gc_heap_mut().track(ptr);
2267            let exc = JSValue::new_object(ptr);
2268            match self.dispatch_throw_value(ctx, exc) {
2269                ThrowDispatch::Caught => Ok(JSValue::undefined()),
2270                ThrowDispatch::Uncaught(e) => Err(e),
2271                ThrowDispatch::AsyncComplete(o) => match o {
2272                    ExecutionOutcome::Complete(v) => Ok(v),
2273                    _ => Err("async eval error".to_string()),
2274                },
2275            }
2276        } else if e.starts_with("ReferenceError") {
2277            let mut err = crate::object::object::JSObject::new();
2278            err.set(
2279                ctx.intern("name"),
2280                JSValue::new_string(ctx.intern("ReferenceError")),
2281            );
2282            err.set(ctx.intern("message"), JSValue::new_string(ctx.intern(&e)));
2283            if let Some(proto) = ctx.get_reference_error_prototype() {
2284                err.prototype = Some(proto);
2285            }
2286            let ptr = Box::into_raw(Box::new(err)) as usize;
2287            ctx.runtime_mut().gc_heap_mut().track(ptr);
2288            let exc = JSValue::new_object(ptr);
2289            match self.dispatch_throw_value(ctx, exc) {
2290                ThrowDispatch::Caught => Ok(JSValue::undefined()),
2291                ThrowDispatch::Uncaught(e) => Err(e),
2292                ThrowDispatch::AsyncComplete(o) => match o {
2293                    ExecutionOutcome::Complete(v) => Ok(v),
2294                    _ => Err("async eval error".to_string()),
2295                },
2296            }
2297        } else if e.starts_with("TypeError") {
2298            let mut err = crate::object::object::JSObject::new();
2299            err.set(
2300                ctx.intern("name"),
2301                JSValue::new_string(ctx.intern("TypeError")),
2302            );
2303            err.set(ctx.intern("message"), JSValue::new_string(ctx.intern(&e)));
2304            if let Some(proto) = ctx.get_type_error_prototype() {
2305                err.prototype = Some(proto);
2306            }
2307            let ptr = Box::into_raw(Box::new(err)) as usize;
2308            ctx.runtime_mut().gc_heap_mut().track(ptr);
2309            let exc = JSValue::new_object(ptr);
2310            match self.dispatch_throw_value(ctx, exc) {
2311                ThrowDispatch::Caught => Ok(JSValue::undefined()),
2312                ThrowDispatch::Uncaught(e) => Err(e),
2313                ThrowDispatch::AsyncComplete(o) => match o {
2314                    ExecutionOutcome::Complete(v) => Ok(v),
2315                    _ => Err("async eval error".to_string()),
2316                },
2317            }
2318        } else {
2319            Err(e)
2320        }
2321    }
2322
2323    fn execute_inner(
2324        &mut self,
2325        ctx: &mut JSContext,
2326        bytecode: &Bytecode,
2327        preserve_registers: bool,
2328        start_pc: usize,
2329        setup_frame: bool,
2330    ) -> Result<ExecutionOutcome, String> {
2331        self.ctx_ptr = ctx;
2332
2333        if setup_frame {
2334            let needed = bytecode.locals_count as usize;
2335            if needed > self.registers.len() {
2336                self.registers.resize(needed, JSValue::undefined());
2337            }
2338            let global_val = ctx.global();
2339            if !preserve_registers {
2340                for i in 0..needed {
2341                    self.registers[i] = if i == 0 {
2342                        global_val
2343                    } else {
2344                        JSValue::undefined()
2345                    };
2346                }
2347            } else {
2348                self.registers[0] = global_val;
2349            }
2350
2351            let frame = &mut self.frames[0];
2352            frame.registers_base = 0;
2353            frame.registers_count = needed;
2354            frame.locals_count = bytecode.locals_count;
2355            frame.bytecode_ptr = bytecode.effective_code_ptr();
2356            frame.bytecode_len = bytecode.effective_code_len();
2357            frame.constants_ptr = bytecode.effective_const_ptr();
2358            frame.constants_len = bytecode.effective_const_len();
2359            frame.return_pc = 0;
2360            frame.function_ptr = None;
2361            frame.this_value = global_val;
2362            frame.saved_args.clear();
2363            frame.upvalue_sync_map = None;
2364            frame.upvalue_sync_bitset = 0;
2365            frame.uses_arguments = false;
2366            frame.var_name_map = std::rc::Rc::as_ptr(&bytecode.var_name_to_slot);
2367            frame.ic_table_ptr = bytecode.effective_ic_table_ptr();
2368
2369            self.frame_index = 0;
2370            self.exception_handlers.clear();
2371            self.frames[0].is_strict_frame = bytecode.is_strict;
2372        }
2373        self.pc = start_pc;
2374        self.refresh_cache();
2375        ctx.reset_interrupt_counter();
2376
2377        loop {
2378            if self.pending_throw.is_some() {
2379                let exc = unsafe { self.pending_throw.take().unwrap_unchecked() };
2380                match self.dispatch_throw_value(ctx, exc) {
2381                    ThrowDispatch::Caught => {}
2382                    ThrowDispatch::Uncaught(e) => return Err(e),
2383                    ThrowDispatch::AsyncComplete(o) => return Ok(o),
2384                }
2385            }
2386            if (self.pc & INTERRUPT_POLL_EVERY_MASK) == 0 {
2387                ctx.check_interrupt()?;
2388            }
2389            if self.pc >= self.cached_code_len {
2390                return Ok(ExecutionOutcome::Complete(JSValue::undefined()));
2391            }
2392
2393            let instr_pc = self.pc;
2394            let op_val = unsafe { *self.cached_code_ptr.add(instr_pc) };
2395            self.pc = instr_pc + 1;
2396            if op_val == 90 {
2397                let dst = unsafe {
2398                    std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const u16)
2399                };
2400                self.pc += 2;
2401                let src = unsafe {
2402                    std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const u16)
2403                };
2404                self.pc += 2;
2405                let val = self.get_reg(src);
2406                self.set_reg(dst, val);
2407                continue;
2408            }
2409            let op = Opcode::from_u8_unchecked(op_val);
2410
2411            match op {
2412                Opcode::Nop => {}
2413                Opcode::End => {
2414                    if self.frame_index == 0 {
2415                        return Ok(ExecutionOutcome::Complete(self.get_reg(0)));
2416                    }
2417                    self.cleanup_handlers_for_frame();
2418                    self.pop_frame(self.get_reg(0));
2419                }
2420                Opcode::Return => {
2421                    let src = self.read_u16_pc();
2422                    let mut ret = self.get_reg(src);
2423                    let frame = &self.frames[self.frame_index];
2424                    if frame.is_constructor && !ret.is_object() {
2425                        ret = frame.this_value;
2426                    }
2427
2428                    if frame.is_constructor {
2429                        if let Some(fptr) = frame.function_ptr {
2430                            let func =
2431                                unsafe { &*(fptr as *const crate::object::function::JSFunction) };
2432                            if let Some(ref bc) = func.bytecode {
2433                                if bc.is_simple_constructor
2434                                    && bc.cached_constructor_final_shape.is_none()
2435                                {
2436                                    if ret.is_object() {
2437                                        let obj =
2438                                            unsafe { JSValue::object_from_ptr(ret.get_ptr()) };
2439                                        if let Some(shape) = obj.shape_ptr() {
2440                                            let func_mut = unsafe {
2441                                                &mut *(fptr
2442                                                    as *mut crate::object::function::JSFunction)
2443                                            };
2444                                            if let Some(ref mut bc_mut) = func_mut.bytecode {
2445                                                bc_mut.cached_constructor_final_shape = Some(shape);
2446                                            }
2447                                        }
2448                                    }
2449                                }
2450                            }
2451                        }
2452                    }
2453
2454                    // Check if there's a pending finally handler — defer the return
2455                    if let Some(handler) = self.exception_handlers.last() {
2456                        if let Some(fp) = handler.finally_pc {
2457                            if handler.frame_index == self.frame_index {
2458                                self.pending_finally_return = Some(ret);
2459                                self.exception_handlers.pop();
2460                                self.pc = fp;
2461                                self.refresh_cache();
2462                                continue;
2463                            }
2464                        }
2465                    }
2466
2467                    if self.frame_index == 0 {
2468                        if self.frames[0].is_async {
2469                            let result = ctx.call_builtin("promise_resolve", &[ret]);
2470                            return Ok(ExecutionOutcome::Complete(result));
2471                        }
2472                        return Ok(ExecutionOutcome::Complete(ret));
2473                    }
2474                    let is_async = self.frames[self.frame_index].is_async;
2475                    self.cleanup_handlers_for_frame();
2476                    if is_async {
2477                        ret = ctx.call_builtin("promise_resolve", &[ret]);
2478                    }
2479                    self.pop_frame(ret);
2480                }
2481                Opcode::Yield => {
2482                    let src = self.read_u16_pc();
2483                    let val = self.get_reg(src);
2484                    return Ok(ExecutionOutcome::Yield(val));
2485                }
2486                Opcode::Await => {
2487                    let src = self.read_u16_pc();
2488                    let val = self.get_reg(src);
2489                    if crate::builtins::promise::is_promise(&val) {
2490                        let ptr = val.get_ptr();
2491                        let promise_obj =
2492                            unsafe { &*(ptr as *mut crate::object::object::JSObject) };
2493                        let state_atom = ctx.common_atoms.__promise_state__;
2494                        let result_atom = ctx.common_atoms.__promise_result__;
2495                        let state = promise_obj
2496                            .get(state_atom)
2497                            .unwrap_or(JSValue::new_int(0))
2498                            .get_int();
2499                        let result = promise_obj.get(result_atom).unwrap_or(JSValue::undefined());
2500                        match state {
2501                            1 => {
2502                                self.set_reg(src, result);
2503                            }
2504                            2 => {
2505                                if self.frame_index == 0 {
2506                                    let rejected = ctx.call_builtin("promise_reject", &[result]);
2507                                    return Ok(ExecutionOutcome::Complete(rejected));
2508                                }
2509                                let mut found_async = false;
2510                                for i in (0..=self.frame_index).rev() {
2511                                    if self.frames[i].is_async {
2512                                        found_async = true;
2513                                        while self.frame_index > i {
2514                                            self.pop_frame(JSValue::undefined());
2515                                        }
2516                                        let rejected =
2517                                            ctx.call_builtin("promise_reject", &[result]);
2518                                        self.set_reg(0, rejected);
2519                                        break;
2520                                    }
2521                                }
2522                                if !found_async {
2523                                    return Err(format!("Uncaught (in promise): {:?}", result));
2524                                }
2525                            }
2526                            _ => {
2527                                self.set_reg(src, JSValue::undefined());
2528                            }
2529                        }
2530                    } else {
2531                        self.set_reg(src, val);
2532                    }
2533                }
2534
2535                Opcode::LoadConst => {
2536                    let dst = self.read_u16_pc();
2537                    let idx = self.read_u32() as usize;
2538                    let val = if idx < self.frames[self.frame_index].constants_len {
2539                        unsafe { *self.cached_const_ptr.add(idx) }
2540                    } else {
2541                        JSValue::undefined()
2542                    };
2543                    self.set_reg(dst, val);
2544                }
2545                Opcode::LoadInt => {
2546                    let dst = self.read_u16_pc();
2547                    let val = self.read_i32() as i64;
2548                    self.set_reg(dst, JSValue::new_int(val));
2549                }
2550                Opcode::LoadInt8 => {
2551                    let dst = self.read_u16_pc();
2552                    let val = self.read_u8() as i8 as i64;
2553                    self.set_reg(dst, JSValue::new_int(val));
2554                }
2555                Opcode::LoadTrue => {
2556                    let dst = self.read_u16_pc();
2557                    self.set_reg(dst, JSValue::bool(true));
2558                }
2559                Opcode::LoadFalse => {
2560                    let dst = self.read_u16_pc();
2561                    self.set_reg(dst, JSValue::bool(false));
2562                }
2563                Opcode::LoadNull => {
2564                    let dst = self.read_u16_pc();
2565                    self.set_reg(dst, JSValue::null());
2566                }
2567                Opcode::LoadUndefined => {
2568                    let dst = self.read_u16_pc();
2569                    self.set_reg(dst, JSValue::undefined());
2570                }
2571
2572                Opcode::Move => {
2573                    let dst = self.read_u16_pc();
2574                    let src = self.read_u16_pc();
2575                    let val = self.get_reg(src);
2576                    self.set_reg(dst, val);
2577                }
2578
2579                Opcode::Add => {
2580                    let dst = self.read_u16_pc();
2581                    let a_reg = self.read_u16_pc();
2582                    let a = self.get_reg(a_reg);
2583                    let b_reg = self.read_u16_pc();
2584                    let b = self.get_reg(b_reg);
2585                    if JSValue::both_int(&a, &b) {
2586                        self.set_reg(dst, JSValue::new_int(a.get_int() + b.get_int()));
2587                    } else if JSValue::both_raw_float(&a, &b) {
2588                        self.set_reg(dst, JSValue::new_float_raw(a.get_float() + b.get_float()));
2589                    } else {
2590                        let result = self.add_slow(&a, &b, ctx);
2591                        if let Some(exc) = self.pending_throw.take() {
2592                            match self.dispatch_throw_value(ctx, exc) {
2593                                ThrowDispatch::Caught => {
2594                                    self.set_reg(dst, self.get_reg(0));
2595                                    return Ok(ExecutionOutcome::Complete(self.get_reg(dst)));
2596                                }
2597                                ThrowDispatch::Uncaught(e) => return Err(e),
2598                                ThrowDispatch::AsyncComplete(o) => match o {
2599                                    ExecutionOutcome::Complete(v) => {
2600                                        self.set_reg(dst, v);
2601                                        return Ok(ExecutionOutcome::Complete(v));
2602                                    }
2603                                    _ => {
2604                                        self.set_reg(dst, JSValue::undefined());
2605                                        return Ok(
2606                                            ExecutionOutcome::Complete(JSValue::undefined()),
2607                                        );
2608                                    }
2609                                },
2610                            }
2611                        }
2612                        self.set_reg(dst, result);
2613                    }
2614                }
2615                Opcode::AddNum => {
2616                    let dst = self.read_u16_pc();
2617                    let a_reg = self.read_u16_pc();
2618                    let a = self.get_reg(a_reg);
2619                    let b_reg = self.read_u16_pc();
2620                    let b = self.get_reg(b_reg);
2621                    let result = if JSValue::both_int(&a, &b) {
2622                        JSValue::new_int(a.get_int() + b.get_int())
2623                    } else {
2624                        let fa = if a.is_float() {
2625                            a.get_float()
2626                        } else if a.is_int() {
2627                            a.get_int() as f64
2628                        } else {
2629                            Self::js_to_number(&a, ctx)
2630                        };
2631                        let fb = if b.is_float() {
2632                            b.get_float()
2633                        } else if b.is_int() {
2634                            b.get_int() as f64
2635                        } else {
2636                            Self::js_to_number(&b, ctx)
2637                        };
2638                        JSValue::new_float(fa + fb)
2639                    };
2640                    self.set_reg(dst, result);
2641                }
2642                Opcode::AddImm8 => {
2643                    let dst = self.read_u16_pc();
2644                    let src = self.read_u16_pc();
2645                    let imm = self.read_u8() as i8 as i64;
2646                    let val = self.get_reg(src);
2647                    let result = if val.is_int() {
2648                        JSValue::new_int(val.get_int() + imm)
2649                    } else if val.is_float() {
2650                        JSValue::new_float_raw(val.get_float() + imm as f64)
2651                    } else {
2652                        let b = JSValue::new_int(imm);
2653                        self.add_slow(&val, &b, ctx)
2654                    };
2655                    self.set_reg(dst, result);
2656                }
2657                Opcode::SubNum => {
2658                    let dst = self.read_u16_pc();
2659                    let a_reg = self.read_u16_pc();
2660                    let a = self.get_reg(a_reg);
2661                    let b_reg = self.read_u16_pc();
2662                    let b = self.get_reg(b_reg);
2663                    let result = if JSValue::both_int(&a, &b) {
2664                        JSValue::new_int(a.get_int() - b.get_int())
2665                    } else {
2666                        let fa = if a.is_float() {
2667                            a.get_float()
2668                        } else if a.is_int() {
2669                            a.get_int() as f64
2670                        } else {
2671                            Self::js_to_number(&a, ctx)
2672                        };
2673                        let fb = if b.is_float() {
2674                            b.get_float()
2675                        } else if b.is_int() {
2676                            b.get_int() as f64
2677                        } else {
2678                            Self::js_to_number(&b, ctx)
2679                        };
2680                        JSValue::new_float(fa - fb)
2681                    };
2682                    self.set_reg(dst, result);
2683                }
2684                Opcode::MulNum => {
2685                    let dst = self.read_u16_pc();
2686                    let a_reg = self.read_u16_pc();
2687                    let a = self.get_reg(a_reg);
2688                    let b_reg = self.read_u16_pc();
2689                    let b = self.get_reg(b_reg);
2690                    let result = if JSValue::both_int(&a, &b) {
2691                        let ai = a.get_int();
2692                        let bi = b.get_int();
2693                        match ai.checked_mul(bi) {
2694                            Some(prod) if prod >= -(1i64 << 46) && prod < (1i64 << 46) => {
2695                                JSValue::new_int(prod)
2696                            }
2697                            _ => JSValue::new_float(ai as f64 * bi as f64),
2698                        }
2699                    } else if a.is_float() && b.is_float() {
2700                        JSValue::new_float_raw(a.get_float() * b.get_float())
2701                    } else {
2702                        let fa = if a.is_float() {
2703                            a.get_float()
2704                        } else if a.is_int() {
2705                            a.get_int() as f64
2706                        } else {
2707                            Self::js_to_number(&a, ctx)
2708                        };
2709                        let fb = if b.is_float() {
2710                            b.get_float()
2711                        } else if b.is_int() {
2712                            b.get_int() as f64
2713                        } else {
2714                            Self::js_to_number(&b, ctx)
2715                        };
2716                        JSValue::new_float(fa * fb)
2717                    };
2718                    self.set_reg(dst, result);
2719                }
2720                Opcode::DivNum => {
2721                    let dst = self.read_u16_pc();
2722                    let a_reg = self.read_u16_pc();
2723                    let a = self.get_reg(a_reg);
2724                    let b_reg = self.read_u16_pc();
2725                    let b = self.get_reg(b_reg);
2726                    let result = if JSValue::both_int(&a, &b) {
2727                        let ai = a.get_int();
2728                        let bi = b.get_int();
2729                        if bi != 0 && ai % bi == 0 {
2730                            JSValue::new_int(ai / bi)
2731                        } else if bi != 0 {
2732                            JSValue::new_float(ai as f64 / bi as f64)
2733                        } else {
2734                            JSValue::new_float(f64::NAN)
2735                        }
2736                    } else if a.is_float() && b.is_float() {
2737                        JSValue::new_float_raw(a.get_float() / b.get_float())
2738                    } else {
2739                        let fa = if a.is_float() {
2740                            a.get_float()
2741                        } else if a.is_int() {
2742                            a.get_int() as f64
2743                        } else {
2744                            Self::js_to_number(&a, ctx)
2745                        };
2746                        let fb = if b.is_float() {
2747                            b.get_float()
2748                        } else if b.is_int() {
2749                            b.get_int() as f64
2750                        } else {
2751                            Self::js_to_number(&b, ctx)
2752                        };
2753                        if fb != 0.0 {
2754                            JSValue::new_float(fa / fb)
2755                        } else {
2756                            JSValue::new_float(f64::NAN)
2757                        }
2758                    };
2759                    self.set_reg(dst, result);
2760                }
2761                Opcode::Sub => {
2762                    let dst = self.read_u16_pc();
2763                    let a_reg = self.read_u16_pc();
2764                    let a = self.get_reg(a_reg);
2765                    let b_reg = self.read_u16_pc();
2766                    let b = self.get_reg(b_reg);
2767                    let result = if JSValue::both_int(&a, &b) {
2768                        let v = a.get_int() - b.get_int();
2769                        if v == 0 && a.get_int() < b.get_int() {
2770                            JSValue::new_float_raw(-0.0f64)
2771                        } else {
2772                            JSValue::new_int(v)
2773                        }
2774                    } else if JSValue::both_raw_float(&a, &b) {
2775                        JSValue::new_float_raw(a.get_float() - b.get_float())
2776                    } else if a.is_bigint() && b.is_bigint() {
2777                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2778                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2779                        Self::create_bigint(a_int - b_int)
2780                    } else if a.is_int() && b.is_float() {
2781                        JSValue::new_float_raw(a.get_int() as f64 - b.get_float())
2782                    } else if a.is_float() && b.is_int() {
2783                        JSValue::new_float_raw(a.get_float() - b.get_int() as f64)
2784                    } else {
2785                        let fa = Self::js_to_number(&a, ctx);
2786                        let fb = Self::js_to_number(&b, ctx);
2787                        JSValue::new_float_raw(fa - fb)
2788                    };
2789                    self.set_reg(dst, result);
2790                }
2791                Opcode::SubImm8 => {
2792                    let dst = self.read_u16_pc();
2793                    let a_reg = self.read_u16_pc();
2794                    let imm = self.read_u8() as i8 as i64;
2795                    let a = self.get_reg(a_reg);
2796                    let result = if a.is_int() {
2797                        JSValue::new_int(a.get_int() - imm)
2798                    } else if a.is_float() {
2799                        JSValue::new_float(a.get_float() - imm as f64)
2800                    } else {
2801                        JSValue::new_float(Self::js_to_number(&a, ctx) - imm as f64)
2802                    };
2803                    self.set_reg(dst, result);
2804                }
2805                Opcode::Mul => {
2806                    let dst = self.read_u16_pc();
2807                    let a_reg = self.read_u16_pc();
2808                    let a = self.get_reg(a_reg);
2809                    let b_reg = self.read_u16_pc();
2810                    let b = self.get_reg(b_reg);
2811                    let result = if JSValue::both_int(&a, &b) {
2812                        let ai = a.get_int();
2813                        let bi = b.get_int();
2814                        match ai.checked_mul(bi) {
2815                            Some(prod) if prod >= -(1i64 << 46) && prod < (1i64 << 46) => {
2816                                JSValue::new_int(prod)
2817                            }
2818                            _ => JSValue::new_float(ai as f64 * bi as f64),
2819                        }
2820                    } else if JSValue::both_raw_float(&a, &b) {
2821                        JSValue::new_float_raw(a.get_float() * b.get_float())
2822                    } else if a.is_bigint() && b.is_bigint() {
2823                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2824                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2825                        Self::create_bigint(a_int * b_int)
2826                    } else if a.is_int() && b.is_float() {
2827                        JSValue::new_float(a.get_int() as f64 * b.get_float())
2828                    } else if a.is_float() && b.is_int() {
2829                        JSValue::new_float(a.get_float() * b.get_int() as f64)
2830                    } else {
2831                        let fa = Self::js_to_number(&a, ctx);
2832                        let fb = Self::js_to_number(&b, ctx);
2833                        JSValue::new_float(fa * fb)
2834                    };
2835                    self.set_reg(dst, result);
2836                }
2837                Opcode::Div => {
2838                    let dst = self.read_u16_pc();
2839                    let a_reg = self.read_u16_pc();
2840                    let a = self.get_reg(a_reg);
2841                    let b_reg = self.read_u16_pc();
2842                    let b = self.get_reg(b_reg);
2843                    let result = if JSValue::both_int(&a, &b) {
2844                        let ai = a.get_int();
2845                        let bi = b.get_int();
2846                        if bi != 0 && ai % bi == 0 {
2847                            JSValue::new_int(ai / bi)
2848                        } else if bi != 0 {
2849                            JSValue::new_float(ai as f64 / bi as f64)
2850                        } else {
2851                            JSValue::new_float(ai as f64 / 0.0f64)
2852                        }
2853                    } else if JSValue::both_raw_float(&a, &b) {
2854                        JSValue::new_float_raw(a.get_float() / b.get_float())
2855                    } else if a.is_bigint() && b.is_bigint() {
2856                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2857                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2858                        if b_int != 0 {
2859                            Self::create_bigint(a_int / b_int)
2860                        } else {
2861                            JSValue::new_float(f64::NAN)
2862                        }
2863                    } else if a.is_int() && b.is_float() {
2864                        JSValue::new_float(a.get_int() as f64 / b.get_float())
2865                    } else if a.is_float() && b.is_int() {
2866                        let bi = b.get_int();
2867                        if bi != 0 {
2868                            JSValue::new_float(a.get_float() / bi as f64)
2869                        } else {
2870                            JSValue::new_float(a.get_float() / 0.0f64)
2871                        }
2872                    } else {
2873                        let fa = Self::js_to_number(&a, ctx);
2874                        let fb = Self::js_to_number(&b, ctx);
2875                        JSValue::new_float(fa / fb)
2876                    };
2877                    self.set_reg(dst, result);
2878                }
2879                Opcode::Mod => {
2880                    let dst = self.read_u16_pc();
2881                    let a_reg = self.read_u16_pc();
2882                    let a = self.get_reg(a_reg);
2883                    let b_reg = self.read_u16_pc();
2884                    let b = self.get_reg(b_reg);
2885                    let result = if JSValue::both_int(&a, &b) {
2886                        let bi = b.get_int();
2887                        if bi != 0 {
2888                            JSValue::new_int(a.get_int() % bi)
2889                        } else {
2890                            JSValue::new_float(f64::NAN)
2891                        }
2892                    } else if a.is_bigint() && b.is_bigint() {
2893                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2894                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2895                        if b_int != 0 {
2896                            Self::create_bigint(a_int % b_int)
2897                        } else {
2898                            JSValue::new_float(f64::NAN)
2899                        }
2900                    } else {
2901                        let bf = Self::js_to_number(&b, ctx);
2902                        if bf != 0.0 {
2903                            JSValue::new_float(Self::js_to_number(&a, ctx) % bf)
2904                        } else {
2905                            JSValue::new_float(f64::NAN)
2906                        }
2907                    };
2908                    self.set_reg(dst, result);
2909                }
2910                Opcode::Pow => {
2911                    let dst = self.read_u16_pc();
2912                    let a_reg = self.read_u16_pc();
2913                    let a = self.get_reg(a_reg);
2914                    let b_reg = self.read_u16_pc();
2915                    let b = self.get_reg(b_reg);
2916                    let result = if a.is_bigint() && b.is_bigint() {
2917                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2918                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2919                        if b_int < 0 {
2920                            self.set_pending_type_error(
2921                                ctx,
2922                                "BigInt exponent must be non-negative",
2923                            );
2924                            JSValue::undefined()
2925                        } else {
2926                            Self::create_bigint(a_int.pow(b_int as u32))
2927                        }
2928                    } else {
2929                        let bv = a.to_number();
2930                        let ev = b.to_number();
2931                        let r = if ev == 2.0 {
2932                            bv * bv
2933                        } else if ev == 0.5 {
2934                            bv.sqrt()
2935                        } else if ev >= 2.0 && ev <= 8.0 && ev == ev.floor() {
2936                            let mut r = bv;
2937                            let mut n = ev as i32 - 1;
2938                            while n > 0 {
2939                                r *= bv;
2940                                n -= 1;
2941                            }
2942                            r
2943                        } else if ev == -1.0 {
2944                            1.0 / bv
2945                        } else if ev == 1.0 {
2946                            bv
2947                        } else {
2948                            bv.powf(ev)
2949                        };
2950                        JSValue::new_float(r)
2951                    };
2952                    self.set_reg(dst, result);
2953                }
2954                Opcode::BitAnd => {
2955                    let dst = self.read_u16_pc();
2956                    let a_reg = self.read_u16_pc();
2957                    let a = self.get_reg(a_reg);
2958                    let b_reg = self.read_u16_pc();
2959                    let b = self.get_reg(b_reg);
2960                    let result = if a.is_bigint() && b.is_bigint() {
2961                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2962                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2963                        Self::create_bigint(a_int & b_int)
2964                    } else if JSValue::both_int(&a, &b) {
2965                        JSValue::new_int((a.get_int() as i32 & b.get_int() as i32) as i64)
2966                    } else {
2967                        let na = if a.is_int() {
2968                            a.get_int() as f64
2969                        } else {
2970                            Self::js_to_number(&a, ctx)
2971                        };
2972                        let nb = if b.is_int() {
2973                            b.get_int() as f64
2974                        } else {
2975                            Self::js_to_number(&b, ctx)
2976                        };
2977                        let ia = Self::to_int32(na) as i64;
2978                        let ib = Self::to_int32(nb) as i64;
2979                        JSValue::new_int(ia & ib)
2980                    };
2981                    self.set_reg(dst, result);
2982                }
2983                Opcode::BitOr => {
2984                    let dst = self.read_u16_pc();
2985                    let a_reg = self.read_u16_pc();
2986                    let a = self.get_reg(a_reg);
2987                    let b_reg = self.read_u16_pc();
2988                    let b = self.get_reg(b_reg);
2989                    let result = if a.is_bigint() && b.is_bigint() {
2990                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
2991                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
2992                        Self::create_bigint(a_int | b_int)
2993                    } else if JSValue::both_int(&a, &b) {
2994                        JSValue::new_int((a.get_int() as i32 | b.get_int() as i32) as i64)
2995                    } else {
2996                        let na = if a.is_int() {
2997                            a.get_int() as f64
2998                        } else {
2999                            Self::js_to_number(&a, ctx)
3000                        };
3001                        let nb = if b.is_int() {
3002                            b.get_int() as f64
3003                        } else {
3004                            Self::js_to_number(&b, ctx)
3005                        };
3006                        let ia = Self::to_int32(na) as i64;
3007                        let ib = Self::to_int32(nb) as i64;
3008                        JSValue::new_int(ia | ib)
3009                    };
3010                    self.set_reg(dst, result);
3011                }
3012                Opcode::BitXor => {
3013                    let dst = self.read_u16_pc();
3014                    let a_reg = self.read_u16_pc();
3015                    let a = self.get_reg(a_reg);
3016                    let b_reg = self.read_u16_pc();
3017                    let b = self.get_reg(b_reg);
3018                    let result = if a.is_bigint() && b.is_bigint() {
3019                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3020                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3021                        Self::create_bigint(a_int ^ b_int)
3022                    } else if JSValue::both_int(&a, &b) {
3023                        JSValue::new_int((a.get_int() as i32 ^ b.get_int() as i32) as i64)
3024                    } else {
3025                        let na = if a.is_int() {
3026                            a.get_int() as f64
3027                        } else {
3028                            Self::js_to_number(&a, ctx)
3029                        };
3030                        let nb = if b.is_int() {
3031                            b.get_int() as f64
3032                        } else {
3033                            Self::js_to_number(&b, ctx)
3034                        };
3035                        let ia = Self::to_int32(na) as i64;
3036                        let ib = Self::to_int32(nb) as i64;
3037                        JSValue::new_int(ia ^ ib)
3038                    };
3039                    self.set_reg(dst, result);
3040                }
3041                Opcode::BitNot => {
3042                    let dst = self.read_u16_pc();
3043                    let a_reg = self.read_u16_pc();
3044                    let mut a = self.get_reg(a_reg);
3045                    if a.is_object() || a.is_function() {
3046                        a = self.ordinary_to_primitive(&a, "number", ctx);
3047                        if let Some(exc) = self.pending_throw.take() {
3048                            match self.dispatch_throw_value(ctx, exc) {
3049                                ThrowDispatch::Caught => continue,
3050                                ThrowDispatch::Uncaught(e) => return Err(e),
3051                                ThrowDispatch::AsyncComplete(_) => continue,
3052                            }
3053                        }
3054                    }
3055                    let result = if a.is_bigint() {
3056                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3057                        Self::create_bigint(!a_int)
3058                    } else {
3059                        let na = if a.is_int() {
3060                            a.get_int() as f64
3061                        } else {
3062                            Self::js_to_number(&a, ctx)
3063                        };
3064                        let i32_val = Self::to_int32(na);
3065                        JSValue::new_int(!(i32_val as i64))
3066                    };
3067                    self.set_reg(dst, result);
3068                }
3069                Opcode::Shl => {
3070                    let dst = self.read_u16_pc();
3071                    let a_reg = self.read_u16_pc();
3072                    let a = self.get_reg(a_reg);
3073                    let b_reg = self.read_u16_pc();
3074                    let b = self.get_reg(b_reg);
3075                    let result = if JSValue::both_int(&a, &b) {
3076                        let shift = b.get_int() & 0x1f;
3077                        JSValue::new_int(a.get_int() << shift)
3078                    } else if a.is_bigint() && b.is_bigint() {
3079                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3080                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3081                        Self::create_bigint(a_int << b_int)
3082                    } else {
3083                        let nb = Self::js_to_number(&b, ctx);
3084                        let shift = if nb.is_nan() || nb.is_infinite() {
3085                            0
3086                        } else {
3087                            (nb as i64) & 0x1f
3088                        };
3089                        let na = Self::js_to_number(&a, ctx);
3090                        let ia = if na.is_nan() || na.is_infinite() {
3091                            0
3092                        } else {
3093                            na as i64
3094                        };
3095                        JSValue::new_int(ia << shift)
3096                    };
3097                    self.set_reg(dst, result);
3098                }
3099                Opcode::Shr => {
3100                    let dst = self.read_u16_pc();
3101                    let a_reg = self.read_u16_pc();
3102                    let a = self.get_reg(a_reg);
3103                    let b_reg = self.read_u16_pc();
3104                    let b = self.get_reg(b_reg);
3105                    let result = if JSValue::both_int(&a, &b) {
3106                        let shift = b.get_int() & 0x1f;
3107                        JSValue::new_int(a.get_int() >> shift)
3108                    } else if a.is_bigint() && b.is_bigint() {
3109                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3110                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3111                        Self::create_bigint(a_int >> b_int)
3112                    } else {
3113                        let nb = Self::js_to_number(&b, ctx);
3114                        let shift = if nb.is_nan() || nb.is_infinite() {
3115                            0
3116                        } else {
3117                            (nb as i64) & 0x1f
3118                        };
3119                        let na = Self::js_to_number(&a, ctx);
3120                        let ia = if na.is_nan() || na.is_infinite() {
3121                            0
3122                        } else {
3123                            na as i64
3124                        };
3125                        JSValue::new_int(ia >> shift)
3126                    };
3127                    self.set_reg(dst, result);
3128                }
3129                Opcode::UShr => {
3130                    let dst = self.read_u16_pc();
3131                    let a_reg = self.read_u16_pc();
3132                    let a = self.get_reg(a_reg);
3133                    let b_reg = self.read_u16_pc();
3134                    let b = self.get_reg(b_reg);
3135
3136                    if JSValue::both_int(&a, &b) {
3137                        let a_u32 = (a.get_int() as u64 & 0xffffffff) as u32;
3138                        let shift = (b.get_int() & 0x1f) as u32;
3139                        self.set_reg(dst, JSValue::new_int((a_u32 >> shift) as i64));
3140                        continue;
3141                    }
3142                    let nb = Self::js_to_number(&b, ctx);
3143                    let shift = if nb.is_nan() || nb.is_infinite() {
3144                        0
3145                    } else {
3146                        (nb as i64) & 0x1f
3147                    };
3148                    let a_u32 = if a.is_int() {
3149                        (a.get_int() as u64 & 0xffffffff) as u32
3150                    } else {
3151                        let n = Self::js_to_number(&a, ctx);
3152                        if n.is_nan() || n.is_infinite() {
3153                            0u32
3154                        } else {
3155                            let n = n.trunc();
3156                            if n >= 0.0 {
3157                                (n as u64 % (1u64 << 32)) as u32
3158                            } else {
3159                                let m = (-n) as u64 % (1u64 << 32);
3160                                if m == 0 {
3161                                    0u32
3162                                } else {
3163                                    ((1u64 << 32) - m) as u32
3164                                }
3165                            }
3166                        }
3167                    };
3168                    let result = JSValue::new_int((a_u32 >> shift as u32) as i64);
3169                    self.set_reg(dst, result);
3170                }
3171                Opcode::Neg => {
3172                    let dst = self.read_u16_pc();
3173                    let a_reg = self.read_u16_pc();
3174                    let a = self.get_reg(a_reg);
3175                    let result = if a.is_int() {
3176                        let v = a.get_int();
3177                        if v == 0 {
3178                            JSValue::new_float_raw(-0.0f64)
3179                        } else {
3180                            JSValue::new_int(-v)
3181                        }
3182                    } else if a.is_bigint() {
3183                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3184                        Self::create_bigint(-a_int)
3185                    } else if a.is_float() {
3186                        JSValue::new_float_raw(-a.get_float())
3187                    } else {
3188                        JSValue::new_float_raw(f64::NAN)
3189                    };
3190                    self.set_reg(dst, result);
3191                }
3192                Opcode::Pos => {
3193                    let dst = self.read_u16_pc();
3194                    let src = self.read_u16_pc();
3195                    let val = self.get_reg(src);
3196
3197                    if val.is_int() || val.is_float() {
3198                        self.set_reg(dst, val);
3199                    } else {
3200                        let n = Self::js_to_number(&val, ctx);
3201                        self.set_reg(dst, JSValue::new_float(n));
3202                    }
3203                }
3204
3205                Opcode::Lt => {
3206                    let dst = self.read_u16_pc();
3207                    let a_reg = self.read_u16_pc();
3208                    let a = self.get_reg(a_reg);
3209                    let b_reg = self.read_u16_pc();
3210                    let b = self.get_reg(b_reg);
3211                    let result = if JSValue::both_int(&a, &b) {
3212                        JSValue::bool(a.get_int() < b.get_int())
3213                    } else if JSValue::both_raw_float(&a, &b) {
3214                        JSValue::bool(a.get_float() < b.get_float())
3215                    } else if a.is_bigint() && b.is_bigint() {
3216                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3217                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3218                        JSValue::bool(a_int < b_int)
3219                    } else if a.is_string() && b.is_string() {
3220                        let sa = ctx.get_atom_str(a.get_atom());
3221                        let sb = ctx.get_atom_str(b.get_atom());
3222                        JSValue::bool(sa < sb)
3223                    } else {
3224                        JSValue::bool(a.to_number() < b.to_number())
3225                    };
3226                    self.set_reg(dst, result);
3227                }
3228                Opcode::Lte => {
3229                    let dst = self.read_u16_pc();
3230                    let a_reg = self.read_u16_pc();
3231                    let a = self.get_reg(a_reg);
3232                    let b_reg = self.read_u16_pc();
3233                    let b = self.get_reg(b_reg);
3234                    let result = if JSValue::both_int(&a, &b) {
3235                        JSValue::bool(a.get_int() <= b.get_int())
3236                    } else if JSValue::both_raw_float(&a, &b) {
3237                        JSValue::bool(a.get_float() <= b.get_float())
3238                    } else if a.is_bigint() && b.is_bigint() {
3239                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3240                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3241                        JSValue::bool(a_int <= b_int)
3242                    } else if a.is_string() && b.is_string() {
3243                        JSValue::bool(a.get_int() <= b.get_int())
3244                    } else {
3245                        JSValue::bool(a.to_number() <= b.to_number())
3246                    };
3247                    self.set_reg(dst, result);
3248                }
3249                Opcode::LteImm8 => {
3250                    let dst = self.read_u16_pc();
3251                    let a_reg = self.read_u16_pc();
3252                    let imm = self.read_u8() as i8 as i64;
3253                    let a = self.get_reg(a_reg);
3254                    let result = if a.is_int() {
3255                        JSValue::bool(a.get_int() <= imm)
3256                    } else if a.is_float() {
3257                        JSValue::bool(a.get_float() <= imm as f64)
3258                    } else {
3259                        JSValue::bool(Self::js_to_number(&a, ctx) <= imm as f64)
3260                    };
3261                    self.set_reg(dst, result);
3262                }
3263                Opcode::Gt => {
3264                    let dst = self.read_u16_pc();
3265                    let a_reg = self.read_u16_pc();
3266                    let a = self.get_reg(a_reg);
3267                    let b_reg = self.read_u16_pc();
3268                    let b = self.get_reg(b_reg);
3269                    let result = if JSValue::both_int(&a, &b) {
3270                        JSValue::bool(a.get_int() > b.get_int())
3271                    } else if JSValue::both_raw_float(&a, &b) {
3272                        JSValue::bool(a.get_float() > b.get_float())
3273                    } else if a.is_bigint() && b.is_bigint() {
3274                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3275                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3276                        JSValue::bool(a_int > b_int)
3277                    } else if a.is_string() && b.is_string() {
3278                        let sa = ctx.get_atom_str(a.get_atom());
3279                        let sb = ctx.get_atom_str(b.get_atom());
3280                        JSValue::bool(sa > sb)
3281                    } else {
3282                        JSValue::bool(a.to_number() > b.to_number())
3283                    };
3284                    self.set_reg(dst, result);
3285                }
3286                Opcode::Gte => {
3287                    let dst = self.read_u16_pc();
3288                    let a_reg = self.read_u16_pc();
3289                    let a = self.get_reg(a_reg);
3290                    let b_reg = self.read_u16_pc();
3291                    let b = self.get_reg(b_reg);
3292                    let result = if JSValue::both_int(&a, &b) {
3293                        JSValue::bool(a.get_int() >= b.get_int())
3294                    } else if JSValue::both_raw_float(&a, &b) {
3295                        JSValue::bool(a.get_float() >= b.get_float())
3296                    } else if a.is_bigint() && b.is_bigint() {
3297                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
3298                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
3299                        JSValue::bool(a_int >= b_int)
3300                    } else if a.is_string() && b.is_string() {
3301                        let sa = ctx.get_atom_str(a.get_atom());
3302                        let sb = ctx.get_atom_str(b.get_atom());
3303                        JSValue::bool(sa >= sb)
3304                    } else {
3305                        JSValue::bool(a.to_number() >= b.to_number())
3306                    };
3307                    self.set_reg(dst, result);
3308                }
3309                Opcode::Eq => {
3310                    let dst = self.read_u16_pc();
3311                    let a_reg = self.read_u16_pc();
3312                    let a = self.get_reg(a_reg);
3313                    let b_reg = self.read_u16_pc();
3314                    let b = self.get_reg(b_reg);
3315                    self.set_reg(dst, JSValue::bool(loose_equal(ctx, a, b)));
3316                }
3317                Opcode::Neq => {
3318                    let dst = self.read_u16_pc();
3319                    let a_reg = self.read_u16_pc();
3320                    let a = self.get_reg(a_reg);
3321                    let b_reg = self.read_u16_pc();
3322                    let b = self.get_reg(b_reg);
3323                    self.set_reg(dst, JSValue::bool(!loose_equal(ctx, a, b)));
3324                }
3325                Opcode::StrictEq => {
3326                    let dst = self.read_u16_pc();
3327                    let a_reg = self.read_u16_pc();
3328                    let a = self.get_reg(a_reg);
3329                    let b_reg = self.read_u16_pc();
3330                    let b = self.get_reg(b_reg);
3331                    self.set_reg(dst, JSValue::bool(a.strict_eq(&b)));
3332                }
3333                Opcode::StrictNeq => {
3334                    let dst = self.read_u16_pc();
3335                    let a_reg = self.read_u16_pc();
3336                    let a = self.get_reg(a_reg);
3337                    let b_reg = self.read_u16_pc();
3338                    let b = self.get_reg(b_reg);
3339                    self.set_reg(dst, JSValue::bool(!a.strict_eq(&b)));
3340                }
3341                Opcode::Not => {
3342                    let dst = self.read_u16_pc();
3343                    let a_reg = self.read_u16_pc();
3344                    let a = self.get_reg(a_reg);
3345                    self.set_reg(dst, JSValue::bool(!a.is_truthy()));
3346                }
3347                Opcode::TypeOf => {
3348                    let dst = self.read_u16_pc();
3349                    let a_reg = self.read_u16_pc();
3350                    let a = self.get_reg(a_reg);
3351                    let ca = &ctx.common_atoms;
3352                    let atom = if a.is_undefined() {
3353                        ca.typeof_undefined
3354                    } else if a.is_null() {
3355                        ca.typeof_object
3356                    } else if a.is_bool() {
3357                        ca.typeof_boolean
3358                    } else if a.is_int() || a.is_float() {
3359                        ca.typeof_number
3360                    } else if a.is_string() {
3361                        ca.typeof_string
3362                    } else if a.is_symbol() {
3363                        ca.typeof_symbol
3364                    } else if a.is_bigint() {
3365                        ca.typeof_bigint
3366                    } else if a.is_function() {
3367                        ca.typeof_function
3368                    } else if a.is_object() {
3369                        let obj = a.as_object();
3370                        if obj.get(ctx.common_atoms.__boundFn).is_some() {
3371                            ca.typeof_function
3372                        } else {
3373                            ca.typeof_object
3374                        }
3375                    } else {
3376                        ca.typeof_object
3377                    };
3378                    self.set_reg(dst, JSValue::new_string(atom));
3379                }
3380
3381                Opcode::MathSin
3382                | Opcode::MathCos
3383                | Opcode::MathSqrt
3384                | Opcode::MathAbs
3385                | Opcode::MathFloor
3386                | Opcode::MathCeil
3387                | Opcode::MathRound => {
3388                    let dst = self.read_u16_pc();
3389                    let src = self.read_u16_pc();
3390                    let a = self.get_reg(src);
3391
3392                    if a.is_int()
3393                        && matches!(
3394                            op,
3395                            Opcode::MathFloor
3396                                | Opcode::MathCeil
3397                                | Opcode::MathRound
3398                                | Opcode::MathAbs
3399                        )
3400                    {
3401                        let result = if matches!(op, Opcode::MathAbs) {
3402                            let i = a.get_int();
3403                            if i == i64::MIN {
3404                                JSValue::new_float(-(i64::MIN as f64))
3405                            } else if i < 0 {
3406                                JSValue::new_int(-i)
3407                            } else {
3408                                a
3409                            }
3410                        } else {
3411                            a
3412                        };
3413                        self.set_reg(dst, result);
3414                        continue;
3415                    }
3416                    let f = if a.is_int() {
3417                        a.get_int() as f64
3418                    } else if a.is_float() {
3419                        a.get_float()
3420                    } else {
3421                        Self::js_to_number(&a, ctx)
3422                    };
3423                    let result = match op {
3424                        Opcode::MathSin => f.sin(),
3425                        Opcode::MathCos => f.cos(),
3426                        Opcode::MathSqrt => f.sqrt(),
3427                        Opcode::MathAbs => f.abs(),
3428                        Opcode::MathFloor => {
3429                            let floored = f.floor();
3430                            if floored == 0.0 && floored.is_sign_negative() {
3431                                self.set_reg(dst, JSValue::new_float(-0.0));
3432                                continue;
3433                            }
3434                            const MAX_SAFE: f64 = (1i64 << 47) as f64;
3435                            if floored >= -MAX_SAFE && floored <= MAX_SAFE {
3436                                self.set_reg(dst, JSValue::new_int(floored as i64));
3437                                continue;
3438                            }
3439                            floored
3440                        }
3441                        Opcode::MathCeil => {
3442                            let ceiled = f.ceil();
3443                            if ceiled == 0.0 && ceiled.is_sign_negative() {
3444                                self.set_reg(dst, JSValue::new_float(-0.0));
3445                                continue;
3446                            }
3447                            const MAX_SAFE: f64 = (1i64 << 47) as f64;
3448                            if ceiled >= -MAX_SAFE && ceiled <= MAX_SAFE {
3449                                self.set_reg(dst, JSValue::new_int(ceiled as i64));
3450                                continue;
3451                            }
3452                            ceiled
3453                        }
3454                        Opcode::MathRound => {
3455                            if f.is_nan() || f.is_infinite() {
3456                                self.set_reg(dst, JSValue::new_float(f));
3457                                continue;
3458                            }
3459                            if f == 0.0 {
3460                                self.set_reg(dst, JSValue::new_float(if f.is_sign_negative() { -0.0 } else { f }));
3461                                continue;
3462                            }
3463                            let r = (f + 0.5).floor();
3464                            let rounded = if f < r - 0.5 { r - 1.0 } else { r };
3465                            if rounded == 0.0 && f < 0.0 {
3466                                self.set_reg(dst, JSValue::new_float(-0.0));
3467                                continue;
3468                            }
3469                            const MAX_SAFE: f64 = (1i64 << 47) as f64;
3470                            if rounded >= -MAX_SAFE && rounded <= MAX_SAFE {
3471                                self.set_reg(dst, JSValue::new_int(rounded as i64));
3472                                continue;
3473                            }
3474                            rounded
3475                        }
3476                        _ => unreachable!("unhandled math op"),
3477                    };
3478                    self.set_reg(dst, JSValue::new_float(result));
3479                }
3480                Opcode::MathPow => {
3481                    let dst = self.read_u16_pc();
3482                    let base_reg = self.read_u16_pc();
3483                    let exp_reg = self.read_u16_pc();
3484                    let base = self.get_reg(base_reg);
3485                    let exp = self.get_reg(exp_reg);
3486                    let b = if base.is_int() {
3487                        base.get_int() as f64
3488                    } else if base.is_float() {
3489                        base.get_float()
3490                    } else {
3491                        Self::js_to_number(&base, ctx)
3492                    };
3493                    let e = if exp.is_int() {
3494                        exp.get_int() as f64
3495                    } else if exp.is_float() {
3496                        exp.get_float()
3497                    } else {
3498                        Self::js_to_number(&exp, ctx)
3499                    };
3500                    let result = if e.is_nan() {
3501                        f64::NAN
3502                    } else if b.abs() == 1.0 && e.is_infinite() {
3503                        f64::NAN
3504                    } else if e == 2.0 {
3505                        b * b
3506                    } else if e == 0.5 {
3507                        b.sqrt()
3508                    } else if e >= 2.0 && e <= 8.0 && e == e.floor() {
3509                        let mut r = b;
3510                        let mut n = e as i32 - 1;
3511                        while n > 0 {
3512                            r *= b;
3513                            n -= 1;
3514                        }
3515                        r
3516                    } else if e == -1.0 {
3517                        1.0 / b
3518                    } else if e == 1.0 {
3519                        b
3520                    } else {
3521                        b.powf(e)
3522                    };
3523                    self.set_reg(dst, JSValue::new_float(result));
3524                }
3525                Opcode::MathMin | Opcode::MathMax => {
3526                    let dst = self.read_u16_pc();
3527                    let a_reg = self.read_u16_pc();
3528                    let b_reg = self.read_u16_pc();
3529                    let a = self.get_reg(a_reg);
3530                    let b = self.get_reg(b_reg);
3531                    let fa = if a.is_int() {
3532                        a.get_int() as f64
3533                    } else if a.is_float() {
3534                        a.get_float()
3535                    } else if a.is_object() || a.is_function() {
3536                        match crate::builtins::global::js_to_number_value(ctx, &a) {
3537                            Ok(v) => v,
3538                            Err(()) => f64::NAN,
3539                        }
3540                    } else {
3541                        Self::js_to_number(&a, ctx)
3542                    };
3543                    let fb = if b.is_int() {
3544                        b.get_int() as f64
3545                    } else if b.is_float() {
3546                        b.get_float()
3547                    } else if b.is_object() || b.is_function() {
3548                        match crate::builtins::global::js_to_number_value(ctx, &b) {
3549                            Ok(v) => v,
3550                            Err(()) => f64::NAN,
3551                        }
3552                    } else {
3553                        Self::js_to_number(&b, ctx)
3554                    };
3555                    let result = if fa.is_nan() || fb.is_nan() {
3556                        f64::NAN
3557                    } else if op == Opcode::MathMin {
3558                        if fa == 0.0 && fb == 0.0 {
3559                            if fa.is_sign_negative() { fa } else { fb }
3560                        } else {
3561                            fa.min(fb)
3562                        }
3563                    } else {
3564                        if fa == 0.0 && fb == 0.0 {
3565                            if fb.is_sign_positive() { fb } else { fa }
3566                        } else {
3567                            fa.max(fb)
3568                        }
3569                    };
3570                    self.set_reg(dst, JSValue::new_float(result));
3571                }
3572
3573                Opcode::Jump => {
3574                    let offset = self.read_i32();
3575                    self.pc = (self.pc as i64 + offset as i64) as usize;
3576                }
3577                Opcode::JumpIf => {
3578                    let src = self.read_u16_pc();
3579                    let offset = self.read_i32();
3580                    if self.get_reg(src).is_truthy() {
3581                        self.pc = (self.pc as i64 + offset as i64) as usize;
3582                    }
3583                }
3584                Opcode::JumpIfNot => {
3585                    let src = self.read_u16_pc();
3586                    let offset = self.read_i32();
3587                    if !self.get_reg(src).is_truthy() {
3588                        self.pc = (self.pc as i64 + offset as i64) as usize;
3589                    }
3590                }
3591                Opcode::JumpIfNullish => {
3592                    let src = self.read_u16_pc();
3593                    let offset = self.read_i32();
3594                    let v = self.get_reg(src);
3595                    if v.is_null() || v.is_undefined() {
3596                        self.pc = (self.pc as i64 + offset as i64) as usize;
3597                    }
3598                }
3599                Opcode::Jump8 => {
3600                    let offset = self.read_u8() as i8;
3601                    self.pc = (self.pc as i64 + offset as i64) as usize;
3602                }
3603                Opcode::JumpIf8 => {
3604                    let src = self.read_u16_pc();
3605                    let offset = self.read_u8() as i8;
3606                    if self.get_reg(src).is_truthy() {
3607                        self.pc = (self.pc as i64 + offset as i64) as usize;
3608                    }
3609                }
3610                Opcode::JumpIfNot8 => {
3611                    let src = self.read_u16_pc();
3612                    let offset = self.read_u8() as i8;
3613                    if !self.get_reg(src).is_truthy() {
3614                        self.pc = (self.pc as i64 + offset as i64) as usize;
3615                    }
3616                }
3617                Opcode::EndFinally => {
3618                    // Check if there's a pending rethrow after the finally body
3619                    if let Some(value) = self.pending_finally_rethrow.take() {
3620                        if let Some(handler) = self.exception_handlers.last().cloned() {
3621                            self.exception_handlers.pop();
3622                            if self.frame_index != handler.frame_index {
3623                                while self.frame_index > handler.frame_index {
3624                                    if self.frame_index == 0 {
3625                                        break;
3626                                    }
3627                                    self.frame_index -= 1;
3628                                }
3629                            }
3630                            self.pc = handler.catch_pc;
3631                            self.refresh_cache();
3632                            self.set_reg(0, value);
3633                            continue;
3634                        }
3635                        self.pending_throw = Some(value);
3636                        let msg = self.format_thrown_value(&value, ctx);
3637                        let trace = self.format_stack_trace(ctx);
3638                        return Err(format!("Uncaught: {}\nStack trace:\n{}", msg, trace));
3639                    }
3640
3641                    // Check if there's a pending return value
3642                    if let Some(value) = self.pending_finally_return.take() {
3643                        if self.frame_index == 0 {
3644                            return Ok(ExecutionOutcome::Complete(value));
3645                        }
3646                        self.pop_frame(value);
3647                    }
3648                }
3649                Opcode::ResetPerIterVar => {
3650                    let slot = self.read_u16_pc();
3651                    if self.frame_index < self.frames.len() {
3652                        let frame = &mut self.frames[self.frame_index];
3653                        if let Some(ref mut map) = frame.upvalue_sync_map {
3654                            map.remove(&slot);
3655                            if slot < 64 {
3656                                frame.upvalue_sync_bitset &= !(1u64 << slot);
3657                            }
3658                            if map.is_empty() {
3659                                frame.upvalue_sync_map = None;
3660                                frame.upvalue_sync_bitset = 0;
3661                            }
3662                        }
3663                        self.refresh_cache();
3664                    }
3665                }
3666                Opcode::JumpBreak => {
3667                    let offset = self.read_i32();
3668                    self.pc = (self.pc as i32 + offset) as usize;
3669                    self.refresh_cache();
3670                }
3671                Opcode::Throw => {
3672                    let src = self.read_u16_pc();
3673                    let value = self.get_reg(src);
3674
3675                    let find_async_frame = |frames: &[CallFrame], frame_index: usize| {
3676                        for i in (0..=frame_index).rev() {
3677                            if frames[i].is_async {
3678                                return Some(i);
3679                            }
3680                        }
3681                        None
3682                    };
3683
3684                    if let Some(handler) = self.exception_handlers.last().cloned() {
3685                        self.exception_handlers.pop();
3686
3687                        if self.frame_index != handler.frame_index {
3688                            if let Some(async_idx) =
3689                                find_async_frame(&self.frames, self.frame_index)
3690                            {
3691                                if async_idx > handler.frame_index {
3692                                    while self.frame_index > async_idx {
3693                                        if self.frame_index == 0 {
3694                                            break;
3695                                        }
3696                                        self.frame_index -= 1;
3697                                    }
3698
3699                                    let rejected = ctx.call_builtin("promise_reject", &[value]);
3700                                    if self.frame_index == 0 {
3701                                        return Ok(ExecutionOutcome::Complete(rejected));
3702                                    }
3703                                    let return_pc = self.frames[self.frame_index].return_pc;
3704                                    let dst_reg = self.frames[self.frame_index].dst_reg;
3705                                    self.frame_index -= 1;
3706                                    self.pc = return_pc;
3707                                    self.refresh_cache();
3708                                    self.set_reg(dst_reg, rejected);
3709                                    continue;
3710                                }
3711                            }
3712                            while self.frame_index > handler.frame_index {
3713                                if self.frame_index == 0 {
3714                                    break;
3715                                }
3716                                let return_pc = self.frames[self.frame_index].return_pc;
3717                                let _ = return_pc;
3718                                self.frame_index -= 1;
3719                            }
3720                        }
3721
3722                        // If catch_pc equals finally_pc, the exception should run the
3723                        // finally body first (then rethrow)
3724                        if handler
3725                            .finally_pc
3726                            .map_or(false, |fp| fp == handler.catch_pc)
3727                        {
3728                            self.finally_rethrow = Some(value);
3729                            self.pc = handler.catch_pc;
3730                        } else {
3731                            self.pc = handler.catch_pc;
3732                        }
3733                        self.refresh_cache();
3734                        self.set_reg(0, value);
3735                    } else {
3736                        if let Some(async_idx) = find_async_frame(&self.frames, self.frame_index) {
3737                            let rejected = ctx.call_builtin("promise_reject", &[value]);
3738                            if async_idx == 0 {
3739                                return Ok(ExecutionOutcome::Complete(rejected));
3740                            }
3741                            while self.frame_index > async_idx {
3742                                self.frame_index -= 1;
3743                            }
3744                            let return_pc = self.frames[self.frame_index].return_pc;
3745                            let dst_reg = self.frames[self.frame_index].dst_reg;
3746                            self.frame_index -= 1;
3747                            self.pc = return_pc;
3748                            self.refresh_cache();
3749                            self.set_reg(dst_reg, rejected);
3750                            continue;
3751                        }
3752                        self.pending_throw = Some(value);
3753                        let msg = self.format_thrown_value(&value, ctx);
3754                        let trace = self.format_stack_trace(ctx);
3755                        return Err(format!("Uncaught: {}\nStack trace:\n{}", msg, trace));
3756                    }
3757                }
3758                Opcode::Try => {
3759                    let catch_pc = self.read_i32() as usize;
3760                    let finally_pc = self.read_i32() as usize;
3761                    self.exception_handlers.push(ExceptionHandler {
3762                        frame_index: self.frame_index,
3763                        catch_pc,
3764                        finally_pc: if finally_pc > 0 {
3765                            Some(finally_pc)
3766                        } else {
3767                            None
3768                        },
3769                    });
3770                }
3771                Opcode::Catch => {
3772                    self.exception_handlers.pop();
3773                }
3774                Opcode::Finally => {
3775                    // Move finally_rethrow to pending so it can be rethrown
3776                    // AFTER the finally body executes
3777                    if let Some(exc) = self.finally_rethrow.take() {
3778                        self.pending_finally_rethrow = Some(exc);
3779                    }
3780                }
3781
3782                Opcode::GetLocal => {
3783                    let dst = self.read_u16_pc();
3784                    let idx = self.read_u32() as u16;
3785                    self.set_reg(dst, self.get_reg(idx));
3786                }
3787                Opcode::SetLocal => {
3788                    let idx = self.read_u32() as u16;
3789                    let src = self.read_u16_pc();
3790                    let val = self.get_reg(src);
3791                    self.set_reg(idx, val);
3792                }
3793                Opcode::IncLocal => {
3794                    let slot = self.read_u16_pc();
3795                    let val = self.get_reg(slot);
3796                    let result = if val.is_int() {
3797                        JSValue::new_int(val.get_int() + 1)
3798                    } else if val.is_bigint() {
3799                        let big = Self::get_bigint_int(&val).unwrap_or(0);
3800                        Self::create_bigint(big + 1)
3801                    } else if val.is_float() {
3802                        JSValue::new_float(val.get_float() + 1.0)
3803                    } else {
3804                        JSValue::new_float(Self::js_to_number(&val, ctx) + 1.0)
3805                    };
3806                    self.set_reg(slot, result);
3807                }
3808                Opcode::DecLocal => {
3809                    let slot = self.read_u16_pc();
3810                    let val = self.get_reg(slot);
3811                    let result = if val.is_int() {
3812                        JSValue::new_int(val.get_int() - 1)
3813                    } else if val.is_bigint() {
3814                        let big = Self::get_bigint_int(&val).unwrap_or(0);
3815                        Self::create_bigint(big - 1)
3816                    } else if val.is_float() {
3817                        JSValue::new_float(val.get_float() - 1.0)
3818                    } else {
3819                        JSValue::new_float(Self::js_to_number(&val, ctx) - 1.0)
3820                    };
3821                    self.set_reg(slot, result);
3822                }
3823                Opcode::GetGlobal => {
3824                    let ic_pc = self.pc - 1;
3825                    let dst = self.read_u16_pc();
3826                    let idx = self.read_u32() as usize;
3827                    let name = if idx < self.frames[self.frame_index].constants_len {
3828                        unsafe { *self.cached_const_ptr.add(idx) }
3829                    } else {
3830                        JSValue::undefined()
3831                    };
3832                    let atom = name.get_atom();
3833                    let mut resolved = false;
3834                    if self.eval_binding_frames > 0 {
3835                        if let Some(val) =
3836                            Self::scan_eval_bindings(&self.frames, self.frame_index, atom.0)
3837                        {
3838                            self.set_reg(dst, val);
3839                            resolved = true;
3840                        }
3841                    }
3842                    if !resolved {
3843                        if let Some(val) = self.get_var_in_caller_vm(atom.0) {
3844                            self.set_reg(dst, val);
3845                        } else {
3846                            let global = ctx.global();
3847                            let mut result = JSValue::undefined();
3848                            if global.is_object() {
3849                                let global_obj = global.as_object();
3850
3851                                let global_shape_id = global_obj.shape_id_cache;
3852                                let ic_table_ptr = self.cached_ic_table_ptr;
3853                                let mut ic_hit = false;
3854                                if !ic_table_ptr.is_null() {
3855                                    if let Some(offset) = unsafe {
3856                                        (*ic_table_ptr).get_global_cache(
3857                                            ic_pc,
3858                                            global_shape_id,
3859                                            atom.0,
3860                                        )
3861                                    } {
3862                                        result = global_obj
3863                                            .get_by_offset(offset as usize)
3864                                            .unwrap_or(JSValue::undefined());
3865                                        ic_hit = true;
3866                                    }
3867                                }
3868                                if !ic_hit {
3869                                    if let Some(accessor) = global_obj.get_own_accessor_entry(atom)
3870                                    {
3871                                        if let Some(getter) = accessor.get {
3872                                            if getter.is_function() {
3873                                                match self.call_function_with_this(
3874                                                    ctx,
3875                                                    getter,
3876                                                    global,
3877                                                    &[],
3878                                                ) {
3879                                                    Ok(v) => result = v,
3880                                                    Err(msg) => {
3881                                                        self.set_pending_type_error(ctx, &msg);
3882                                                        if let Some(exc) = self.pending_throw.take()
3883                                                        {
3884                                                            match self
3885                                                                .dispatch_throw_value(ctx, exc)
3886                                                            {
3887                                                                ThrowDispatch::Caught => {}
3888                                                                ThrowDispatch::Uncaught(e) => {
3889                                                                    return Err(e);
3890                                                                }
3891                                                                ThrowDispatch::AsyncComplete(o) => {
3892                                                                    return Ok(o);
3893                                                                }
3894                                                            }
3895                                                        }
3896                                                    }
3897                                                }
3898                                            }
3899                                        }
3900                                    } else if let Some(value) = global_obj.get(atom) {
3901                                        result = value;
3902
3903                                        if !ic_table_ptr.is_null() {
3904                                            if let Some(offset) = global_obj.find_offset(atom) {
3905                                                unsafe {
3906                                                    (*ic_table_ptr).insert_global_cache(
3907                                                        ic_pc,
3908                                                        global_shape_id,
3909                                                        offset as u32,
3910                                                        atom.0,
3911                                                    );
3912                                                }
3913                                            }
3914                                        }
3915                                    } else if let Some(func_ptr) =
3916                                        self.frames[self.frame_index].function_ptr
3917                                    {
3918                                        let func_val = JSValue::new_function(func_ptr);
3919                                        let js_func = func_val.as_function();
3920                                        if js_func.name == atom {
3921                                            result = JSValue::new_function(func_ptr);
3922                                        }
3923                                    }
3924                                }
3925                            }
3926                            self.set_reg(dst, result);
3927                        }
3928                    }
3929                }
3930                Opcode::SetGlobal => {
3931                    let idx = self.read_u32() as usize;
3932                    let src = self.read_u16_pc();
3933                    let val = self.get_reg(src);
3934                    let name = if idx < self.frames[self.frame_index].constants_len {
3935                        unsafe { *self.cached_const_ptr.add(idx) }
3936                    } else {
3937                        JSValue::undefined()
3938                    };
3939                    let atom = name.get_atom();
3940                    let is_strict = self.frames[self.frame_index].is_strict_frame;
3941
3942                    if self.caller_vm.is_some() {
3943                        if !self.set_var_in_caller_vm(ctx, atom.0, val) {
3944                            if is_strict {
3945                                let name_str = ctx.get_atom_str(atom).to_string();
3946                                let err_msg = format!("{} is not defined", name_str);
3947                                let mut err = crate::object::object::JSObject::new();
3948                                err.set(
3949                                    ctx.intern("name"),
3950                                    JSValue::new_string(ctx.intern("ReferenceError")),
3951                                );
3952                                err.set(
3953                                    ctx.intern("message"),
3954                                    JSValue::new_string(ctx.intern(&err_msg)),
3955                                );
3956                                if let Some(proto) = ctx.get_reference_error_prototype() {
3957                                    err.prototype = Some(proto);
3958                                }
3959                                let ptr = Box::into_raw(Box::new(err)) as usize;
3960                                ctx.runtime_mut().gc_heap_mut().track(ptr);
3961                                self.pending_throw = Some(JSValue::new_object(ptr));
3962                                if let Some(exc) = self.pending_throw.take() {
3963                                    match self.dispatch_throw_value(ctx, exc) {
3964                                        ThrowDispatch::Caught => continue,
3965                                        ThrowDispatch::Uncaught(e) => return Err(e),
3966                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
3967                                    }
3968                                }
3969                            }
3970                            let global = ctx.global();
3971                            if global.is_object() {
3972                                let global_obj = global.as_object_mut();
3973                                global_obj.set_cached(atom, val, ctx.shape_cache_mut());
3974                            }
3975                        }
3976                    } else {
3977                        if is_strict {
3978                            let mut found = false;
3979
3980                            if self.eval_binding_frames == 0 && self.caller_vm.is_none() {
3981                                let global = ctx.global();
3982                                if global.is_object() {
3983                                    found = global.as_object().get_own(atom).is_some();
3984                                }
3985
3986                                if !found && !self.frames[0].var_name_map.is_null() {
3987                                    let vnm = unsafe { &*self.frames[0].var_name_map };
3988                                    found = vnm.iter().any(|&(an, _)| an == atom.0);
3989                                }
3990                            } else if self.eval_binding_frames > 0 {
3991                                for fi in (0..=self.frame_index).rev() {
3992                                    if let Some(ref eb) = self.frames[fi].eval_bindings {
3993                                        if eb.contains_key(&atom.0) {
3994                                            found = true;
3995                                            break;
3996                                        }
3997                                    }
3998                                    if !self.frames[fi].var_name_map.is_null() {
3999                                        let vnm = unsafe { &*self.frames[fi].var_name_map };
4000                                        if vnm.iter().any(|&(an, _)| an == atom.0) {
4001                                            found = true;
4002                                            break;
4003                                        }
4004                                    }
4005                                }
4006                                if !found {
4007                                    let global = ctx.global();
4008                                    if global.is_object() {
4009                                        found = global.as_object().get_own(atom).is_some();
4010                                    }
4011                                }
4012                            } else {
4013                                if self.get_var_in_caller_vm(atom.0).is_some() {
4014                                    found = true;
4015                                }
4016                                if !found {
4017                                    let global = ctx.global();
4018                                    if global.is_object() {
4019                                        found = global.as_object().get_own(atom).is_some();
4020                                    }
4021                                }
4022                            }
4023                            if !found {
4024                                let name_str = ctx.get_atom_str(atom).to_string();
4025                                let err_msg = format!("{} is not defined", name_str);
4026                                let mut err = crate::object::object::JSObject::new();
4027                                err.set(
4028                                    ctx.intern("name"),
4029                                    JSValue::new_string(ctx.intern("ReferenceError")),
4030                                );
4031                                err.set(
4032                                    ctx.intern("message"),
4033                                    JSValue::new_string(ctx.intern(&err_msg)),
4034                                );
4035                                if let Some(proto) = ctx.get_reference_error_prototype() {
4036                                    err.prototype = Some(proto);
4037                                }
4038                                let ptr = Box::into_raw(Box::new(err)) as usize;
4039                                ctx.runtime_mut().gc_heap_mut().track(ptr);
4040                                self.pending_throw = Some(JSValue::new_object(ptr));
4041                                if let Some(exc) = self.pending_throw.take() {
4042                                    match self.dispatch_throw_value(ctx, exc) {
4043                                        ThrowDispatch::Caught => continue,
4044                                        ThrowDispatch::Uncaught(e) => return Err(e),
4045                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
4046                                    }
4047                                }
4048                            }
4049                        }
4050                        let global = ctx.global();
4051                        if global.is_object() {
4052                            let global_obj_ref = global.as_object();
4053                            if is_strict {
4054                                if let Some(desc) = global_obj_ref.get_own_descriptor(atom) {
4055                                    if !desc.writable && desc.get.is_none() && desc.set.is_none() {
4056                                        self.set_pending_type_error(
4057                                            ctx,
4058                                            "Cannot assign to read only property",
4059                                        );
4060                                        if let Some(exc) = self.pending_throw.take() {
4061                                            match self.dispatch_throw_value(ctx, exc) {
4062                                                ThrowDispatch::Caught => continue,
4063                                                ThrowDispatch::Uncaught(e) => return Err(e),
4064                                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4065                                            }
4066                                        }
4067                                        continue;
4068                                    }
4069                                }
4070                            }
4071                            let global_obj = global.as_object_mut();
4072                            global_obj.set_cached(atom, val, ctx.shape_cache_mut());
4073                        }
4074                    }
4075                }
4076                Opcode::SetGlobalVar => {
4077                    let idx = self.read_u32() as usize;
4078                    let src = self.read_u16_pc();
4079                    let val = self.get_reg(src);
4080                    let name = if idx < self.frames[self.frame_index].constants_len {
4081                        unsafe { *self.cached_const_ptr.add(idx) }
4082                    } else {
4083                        JSValue::undefined()
4084                    };
4085                    let atom = name.get_atom();
4086                    let is_strict = self.frames[self.frame_index].is_strict_frame;
4087                    if self.caller_vm.is_some() {
4088                        if !self.set_var_in_caller_vm(ctx, atom.0, val) {
4089                            if is_strict {
4090                                let name_str = ctx.get_atom_str(atom).to_string();
4091                                let err_msg = format!("{} is not defined", name_str);
4092                                let mut err = crate::object::object::JSObject::new();
4093                                err.set(
4094                                    ctx.intern("name"),
4095                                    JSValue::new_string(ctx.intern("ReferenceError")),
4096                                );
4097                                err.set(
4098                                    ctx.intern("message"),
4099                                    JSValue::new_string(ctx.intern(&err_msg)),
4100                                );
4101                                if let Some(proto) = ctx.get_reference_error_prototype() {
4102                                    err.prototype = Some(proto);
4103                                }
4104                                let ptr = Box::into_raw(Box::new(err)) as usize;
4105                                ctx.runtime_mut().gc_heap_mut().track(ptr);
4106                                self.pending_throw = Some(JSValue::new_object(ptr));
4107                                if let Some(exc) = self.pending_throw.take() {
4108                                    match self.dispatch_throw_value(ctx, exc) {
4109                                        ThrowDispatch::Caught => continue,
4110                                        ThrowDispatch::Uncaught(e) => return Err(e),
4111                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
4112                                    }
4113                                }
4114                            }
4115                            let global = ctx.global();
4116                            if global.is_object() {
4117                                let global_obj = global.as_object_mut();
4118                                global_obj.set_cached_non_configurable(
4119                                    atom,
4120                                    val,
4121                                    ctx.shape_cache_mut(),
4122                                );
4123                            }
4124                        }
4125                    } else {
4126                        if is_strict {
4127                            let mut found = false;
4128
4129                            if self.eval_binding_frames == 0 && self.caller_vm.is_none() {
4130                                let global = ctx.global();
4131                                if global.is_object() {
4132                                    found = global.as_object().get_own(atom).is_some();
4133                                }
4134                                if !found && !self.frames[0].var_name_map.is_null() {
4135                                    let vnm = unsafe { &*self.frames[0].var_name_map };
4136                                    found = vnm.iter().any(|&(an, _)| an == atom.0);
4137                                }
4138                            } else if self.eval_binding_frames > 0 {
4139                                for fi in (0..=self.frame_index).rev() {
4140                                    if let Some(ref eb) = self.frames[fi].eval_bindings {
4141                                        if eb.contains_key(&atom.0) {
4142                                            found = true;
4143                                            break;
4144                                        }
4145                                    }
4146                                    if !self.frames[fi].var_name_map.is_null() {
4147                                        let vnm = unsafe { &*self.frames[fi].var_name_map };
4148                                        if vnm.iter().any(|&(an, _)| an == atom.0) {
4149                                            found = true;
4150                                            break;
4151                                        }
4152                                    }
4153                                }
4154                                if !found {
4155                                    let global = ctx.global();
4156                                    if global.is_object() {
4157                                        found = global.as_object().get_own(atom).is_some();
4158                                    }
4159                                }
4160                            } else {
4161                                if self.get_var_in_caller_vm(atom.0).is_some() {
4162                                    found = true;
4163                                }
4164                                if !found {
4165                                    let global = ctx.global();
4166                                    if global.is_object() {
4167                                        found = global.as_object().get_own(atom).is_some();
4168                                    }
4169                                }
4170                            }
4171                            if !found {
4172                                let name_str = ctx.get_atom_str(atom).to_string();
4173                                let err_msg = format!("{} is not defined", name_str);
4174                                let mut err = crate::object::object::JSObject::new();
4175                                err.set(
4176                                    ctx.intern("name"),
4177                                    JSValue::new_string(ctx.intern("ReferenceError")),
4178                                );
4179                                err.set(
4180                                    ctx.intern("message"),
4181                                    JSValue::new_string(ctx.intern(&err_msg)),
4182                                );
4183                                if let Some(proto) = ctx.get_reference_error_prototype() {
4184                                    err.prototype = Some(proto);
4185                                }
4186                                let ptr = Box::into_raw(Box::new(err)) as usize;
4187                                ctx.runtime_mut().gc_heap_mut().track(ptr);
4188                                self.pending_throw = Some(JSValue::new_object(ptr));
4189                                if let Some(exc) = self.pending_throw.take() {
4190                                    match self.dispatch_throw_value(ctx, exc) {
4191                                        ThrowDispatch::Caught => continue,
4192                                        ThrowDispatch::Uncaught(e) => return Err(e),
4193                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
4194                                    }
4195                                }
4196                            }
4197                        }
4198                        let global = ctx.global();
4199                        if global.is_object() {
4200                            let global_obj = global.as_object_mut();
4201                            global_obj.set_cached_non_configurable(
4202                                atom,
4203                                val,
4204                                ctx.shape_cache_mut(),
4205                            );
4206                        }
4207                    }
4208                }
4209                Opcode::InitGlobalVar => {
4210                    let idx = self.read_u32() as usize;
4211                    let src = self.read_u16_pc();
4212                    let val = self.get_reg(src);
4213                    let name = if idx < self.frames[self.frame_index].constants_len {
4214                        unsafe { *self.cached_const_ptr.add(idx) }
4215                    } else {
4216                        JSValue::undefined()
4217                    };
4218                    let atom = name.get_atom();
4219                    if self.caller_vm.is_some() {
4220                        self.init_var_in_caller_vm(ctx, atom.0, val);
4221                    } else {
4222                        let global = ctx.global();
4223                        if global.is_object() {
4224                            let global_obj = global.as_object_mut();
4225                            if !global_obj.has_own(atom) {
4226                                global_obj.define_cached(atom, val, ctx.shape_cache_mut());
4227                            }
4228                        }
4229                    }
4230                }
4231                Opcode::DefineGlobal => {
4232                    let idx = self.read_u32() as usize;
4233                    let src = self.read_u16_pc();
4234                    let val = self.get_reg(src);
4235                    let name = if idx < self.frames[self.frame_index].constants_len {
4236                        unsafe { *self.cached_const_ptr.add(idx) }
4237                    } else {
4238                        JSValue::undefined()
4239                    };
4240                    let atom = name.get_atom();
4241                    let global = ctx.global();
4242                    if global.is_object() {
4243                        let global_obj = global.as_object_mut();
4244                        global_obj.define_cached(atom, val, ctx.shape_cache_mut());
4245                    }
4246                }
4247                Opcode::DeleteGlobal => {
4248                    let dst = self.read_u16_pc();
4249                    let idx = self.read_u32() as usize;
4250                    let name = if idx < self.frames[self.frame_index].constants_len {
4251                        unsafe { *self.cached_const_ptr.add(idx) }
4252                    } else {
4253                        JSValue::undefined()
4254                    };
4255                    let atom = name.get_atom();
4256                    let global = ctx.global();
4257                    let result = if global.is_object() {
4258                        let global_obj = global.as_object_mut();
4259                        let deleted = global_obj.delete(atom);
4260                        if deleted {
4261                            ctx.runtime_mut().gc_heap_mut().deleted_props_count += 1;
4262                        }
4263                        deleted
4264                    } else {
4265                        true
4266                    };
4267                    let is_strict = self.frames[self.frame_index].is_strict_frame;
4268                    if is_strict && !result {
4269                        self.set_pending_type_error(
4270                            ctx,
4271                            "Delete of unqualified identifier in strict mode",
4272                        );
4273                        if let Some(exc) = self.pending_throw.take() {
4274                            match self.dispatch_throw_value(ctx, exc) {
4275                                ThrowDispatch::Caught => {}
4276                                ThrowDispatch::Uncaught(e) => return Err(e),
4277                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4278                            }
4279                        }
4280                    }
4281                    self.set_reg(dst, JSValue::bool(result));
4282                }
4283                Opcode::ThrowReferenceError => {
4284                    let msg_idx = self.read_u32() as usize;
4285                    let msg_const = if msg_idx < self.frames[self.frame_index].constants_len {
4286                        unsafe { *self.cached_const_ptr.add(msg_idx) }
4287                    } else {
4288                        JSValue::undefined()
4289                    };
4290                    let err_msg = if msg_const.is_string() {
4291                        ctx.get_atom_str(msg_const.get_atom()).to_string()
4292                    } else {
4293                        String::new()
4294                    };
4295                    let mut err = crate::object::object::JSObject::new();
4296                    err.set(
4297                        ctx.intern("name"),
4298                        JSValue::new_string(ctx.intern("ReferenceError")),
4299                    );
4300                    err.set(
4301                        ctx.intern("message"),
4302                        JSValue::new_string(ctx.intern(&err_msg)),
4303                    );
4304                    if let Some(proto) = ctx.get_reference_error_prototype() {
4305                        err.prototype = Some(proto);
4306                    }
4307                    let ptr = Box::into_raw(Box::new(err)) as usize;
4308                    ctx.runtime_mut().gc_heap_mut().track(ptr);
4309                    self.pending_throw = Some(JSValue::new_object(ptr));
4310                    if let Some(exc) = self.pending_throw.take() {
4311                        match self.dispatch_throw_value(ctx, exc) {
4312                            ThrowDispatch::Caught => continue,
4313                            ThrowDispatch::Uncaught(e) => return Err(e),
4314                            ThrowDispatch::AsyncComplete(o) => return Ok(o),
4315                        }
4316                    }
4317                    continue;
4318                }
4319                Opcode::GetUpvalue => {
4320                    let dst = self.read_u16_pc();
4321                    let slot = self.read_u16_pc() as usize;
4322                    let result = if !self.cached_upvalue_slot_ptr.is_null()
4323                        && slot < self.cached_upvalues_len
4324                    {
4325                        let rc = unsafe { &*self.cached_upvalue_slot_ptr.add(slot) };
4326                        unsafe { *(*std::rc::Rc::as_ptr(rc)).as_ptr() }
4327                    } else {
4328                        JSValue::undefined()
4329                    };
4330                    self.set_reg(dst, result);
4331                }
4332                Opcode::SetUpvalue => {
4333                    let slot = self.read_u16_pc() as usize;
4334                    let src = self.read_u16_pc();
4335                    let val = self.get_reg(src);
4336                    if !self.cached_upvalue_slot_ptr.is_null() && slot < self.cached_upvalues_len {
4337                        let rc = unsafe { &*self.cached_upvalue_slot_ptr.add(slot) };
4338                        unsafe { (*std::rc::Rc::as_ptr(rc)).as_ptr().write(val) };
4339                    }
4340                }
4341
4342                Opcode::NewObject => {
4343                    let dst = self.read_u16_pc();
4344                    let ptr = if let Some(proto_ptr) = ctx.get_object_prototype() {
4345                        if let Some(nursery_ptr) = ctx.runtime_mut().gc_heap_mut().alloc_object() {
4346                            unsafe {
4347                                (*nursery_ptr).prototype = Some(proto_ptr);
4348                                (*nursery_ptr).ensure_shape(ctx.shape_cache_mut());
4349                            }
4350                            nursery_ptr as usize
4351                        } else {
4352                            let mut obj = crate::object::object::JSObject::new();
4353                            obj.prototype = Some(proto_ptr);
4354                            obj.ensure_shape(ctx.shape_cache_mut());
4355                            let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
4356                            ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
4357                            heap_ptr
4358                        }
4359                    } else {
4360                        let mut obj = crate::object::object::JSObject::new();
4361                        obj.ensure_shape(ctx.shape_cache_mut());
4362                        let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
4363                        ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
4364                        heap_ptr
4365                    };
4366                    self.allocation_count += 1;
4367                    self.set_reg(dst, JSValue::new_object(ptr));
4368                    self.maybe_gc(ctx);
4369                }
4370                Opcode::NewArray => {
4371                    let dst = self.read_u16_pc();
4372                    let count = self.read_u16_pc();
4373                    let ptr = if let Some(proto_ptr) = ctx.get_array_prototype() {
4374                        if let Some(nursery_ptr) = ctx.runtime_mut().gc_heap_mut().alloc_array() {
4375                            unsafe {
4376                                (*nursery_ptr).header.set_prototype_raw(proto_ptr);
4377                                (*nursery_ptr).elements.reserve(count as usize);
4378                                (*nursery_ptr).header.ensure_shape(ctx.shape_cache_mut());
4379                            }
4380                            nursery_ptr as usize
4381                        } else {
4382                            let mut arr = crate::object::array_obj::JSArrayObject::with_capacity(
4383                                count as usize,
4384                            );
4385                            arr.header.set_prototype_raw(proto_ptr);
4386                            arr.header.ensure_shape(ctx.shape_cache_mut());
4387                            let heap_ptr = Box::into_raw(Box::new(arr)) as usize;
4388                            ctx.runtime_mut().gc_heap_mut().track_array(heap_ptr);
4389                            heap_ptr
4390                        }
4391                    } else {
4392                        let mut arr =
4393                            crate::object::array_obj::JSArrayObject::with_capacity(count as usize);
4394                        arr.header.ensure_shape(ctx.shape_cache_mut());
4395                        let heap_ptr = Box::into_raw(Box::new(arr)) as usize;
4396                        ctx.runtime_mut().gc_heap_mut().track_array(heap_ptr);
4397                        heap_ptr
4398                    };
4399                    self.allocation_count += 1;
4400                    self.set_reg(dst, JSValue::new_object(ptr));
4401                    self.maybe_gc(ctx);
4402                }
4403                Opcode::GetField => {
4404                    let dst = self.read_u16_pc();
4405                    let obj_reg = self.read_u16_pc();
4406                    let key_reg = self.read_u16_pc();
4407                    let obj_val = self.get_reg(obj_reg);
4408                    let key_val = self.get_reg(key_reg);
4409                    let result = if obj_val.is_object_like() && key_val.is_int() {
4410                        let ptr = obj_val.get_ptr();
4411                        let js_obj = unsafe { JSValue::object_from_ptr(ptr) };
4412                        if js_obj.is_dense_array() {
4413                            let idx = key_val.get_int() as usize;
4414                            let arr = unsafe {
4415                                &*(ptr as *const crate::object::array_obj::JSArrayObject)
4416                            };
4417                            if idx < arr.elements.len() {
4418                                arr.elements[idx]
4419                            } else {
4420                                JSValue::undefined()
4421                            }
4422                        } else if js_obj.is_mapped_arguments() {
4423                            let fi = js_obj.mapped_args_frame_index();
4424                            let idx = key_val.get_int();
4425                            if idx >= 0 && fi < self.frames.len() {
4426                                let idx_u = idx as usize;
4427                                let saved = &self.frames[fi].saved_args;
4428                                if idx_u < saved.len() {
4429                                    self.set_reg(dst, saved[idx_u]);
4430                                    continue;
4431                                }
4432                            }
4433                            let idx = key_val.get_int();
4434                            if idx >= 0 {
4435                                if let Some(val) = js_obj.get_indexed(idx as usize) {
4436                                    self.set_reg(dst, val);
4437                                    continue;
4438                                }
4439                            }
4440                            let atom = self.int_atom(idx as usize, ctx);
4441                            js_obj.get(atom).unwrap_or(JSValue::undefined())
4442                        } else if js_obj.obj_type() == crate::object::object::ObjectType::TypedArray {
4443                            let idx = key_val.get_int() as usize;
4444                            if let Some(val) = crate::builtins::typedarray::ta_get_element(ctx, js_obj, idx) {
4445                                self.set_reg(dst, val);
4446                                continue;
4447                            }
4448                            JSValue::undefined()
4449                        } else {
4450                            let idx = key_val.get_int();
4451                            if idx >= 0 {
4452                                if let Some(val) = js_obj.get_indexed(idx as usize) {
4453                                    self.set_reg(dst, val);
4454                                    continue;
4455                                }
4456                            }
4457                            let atom = self.int_atom(idx as usize, ctx);
4458                            js_obj.get(atom).unwrap_or(JSValue::undefined())
4459                        }
4460                    } else if obj_val.is_string() && key_val.is_int() {
4461                        let s = ctx.get_atom_str(obj_val.get_atom());
4462                        let idx = key_val.get_int();
4463                        if idx < 0 {
4464                            JSValue::undefined()
4465                        } else if let Some(ch) = s.chars().nth(idx as usize) {
4466                            let chs = ch.to_string();
4467                            JSValue::new_string(ctx.intern(&chs))
4468                        } else {
4469                            JSValue::undefined()
4470                        }
4471                    } else if obj_val.is_string() && key_val.is_string() {
4472                        let str_atom = obj_val.get_atom();
4473                        let key = ctx.get_atom_str(key_val.get_atom());
4474                        if key == "length" {
4475                            JSValue::new_int(ctx.string_char_count(str_atom) as i64)
4476                        } else if let Ok(idx) = key.parse::<usize>() {
4477                            let s = ctx.get_atom_str(str_atom);
4478                            if let Some(ch) = s.chars().nth(idx) {
4479                                let chs = ch.to_string();
4480                                JSValue::new_string(ctx.intern(&chs))
4481                            } else {
4482                                JSValue::undefined()
4483                            }
4484                        } else if let Some(proto_ptr) = ctx.get_string_prototype() {
4485                            let proto_obj = unsafe { &*proto_ptr };
4486                            proto_obj
4487                                .get(key_val.get_atom())
4488                                .unwrap_or(JSValue::undefined())
4489                        } else {
4490                            JSValue::undefined()
4491                        }
4492                    } else if obj_val.is_object_like() && key_val.is_string() {
4493                        let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4494                        let atom = key_val.get_atom();
4495
4496                        if let Some(getter) = js_obj.get_own_accessor_value(atom) {
4497                            if getter.is_function() {
4498                                match self.call_function_with_this(ctx, getter, obj_val, &[]) {
4499                                    Ok(v) => v,
4500                                    Err(e) => {
4501                                        let exc = self.last_caught_exception.take().unwrap_or(JSValue::undefined());
4502                                        match self.dispatch_throw_value(ctx, exc) {
4503                                            ThrowDispatch::Caught => JSValue::undefined(),
4504                                            ThrowDispatch::Uncaught(_) => {
4505                                                return Err(e);
4506                                            }
4507                                            ThrowDispatch::AsyncComplete(o) => {
4508                                                return Ok(o);
4509                                            }
4510                                        }
4511                                    }
4512                                }
4513                            } else {
4514                                JSValue::undefined()
4515                            }
4516                        } else {
4517                            js_obj.get(atom).unwrap_or(JSValue::undefined())
4518                        }
4519                    } else if obj_val.is_object_like() && key_val.is_symbol() {
4520                        let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4521                        let sym_key =
4522                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4523                        js_obj.get(sym_key).unwrap_or(JSValue::undefined())
4524                    } else if obj_val.is_object_like() && key_val.is_float() {
4525                        let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4526                        let s = VM::js_to_string(&key_val, ctx);
4527                        let atom = s.get_atom();
4528                        js_obj.get(atom).unwrap_or(JSValue::undefined())
4529                    } else if obj_val.is_symbol() && key_val.is_string() {
4530                        if let Some(proto_ptr) = ctx.get_symbol_prototype() {
4531                            let proto_obj = unsafe { &*proto_ptr };
4532                            let atom = key_val.get_atom();
4533                            if let Some(getter) = proto_obj.get_own_accessor_value(atom) {
4534                                if getter.is_function() {
4535                                    match self.call_function_with_this(ctx, getter, obj_val, &[]) {
4536                                        Ok(v) => v,
4537                                        Err(_) => JSValue::undefined(),
4538                                    }
4539                                } else {
4540                                    JSValue::undefined()
4541                                }
4542                            } else {
4543                                proto_obj.get(atom).unwrap_or(JSValue::undefined())
4544                            }
4545                        } else {
4546                            JSValue::undefined()
4547                        }
4548                    } else if obj_val.is_symbol() && key_val.is_symbol() {
4549                        if let Some(proto_ptr) = ctx.get_symbol_prototype() {
4550                            let proto_obj = unsafe { &*proto_ptr };
4551                            let sym_key =
4552                                crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4553                            proto_obj.get(sym_key).unwrap_or(JSValue::undefined())
4554                        } else {
4555                            JSValue::undefined()
4556                        }
4557                    } else {
4558                        JSValue::undefined()
4559                    };
4560                    self.set_reg(dst, result);
4561                }
4562                Opcode::SetField => {
4563                    let obj_reg = self.read_u16_pc();
4564                    let key_reg = self.read_u16_pc();
4565                    let val_reg = self.read_u16_pc();
4566                    let obj_val = self.get_reg(obj_reg);
4567                    let key_val = self.get_reg(key_reg);
4568                    let value = self.get_reg(val_reg);
4569                    if obj_val.is_object_like() && key_val.is_int() {
4570                        let ptr = obj_val.get_ptr();
4571                        let js_obj = unsafe { JSValue::object_from_ptr_mut(ptr) };
4572                        if js_obj.is_dense_array() {
4573                            let idx = key_val.get_int() as usize;
4574                            let arr = unsafe {
4575                                &mut *(ptr as *mut crate::object::array_obj::JSArrayObject)
4576                            };
4577                            if idx < arr.elements.len() {
4578                                arr.elements[idx] = value;
4579                            } else {
4580                                // Check prototype chain for setters when EXTENDING the array
4581                                let mut proto = js_obj.prototype;
4582                                let mut setter_found = None;
4583                                while let Some(p) = proto {
4584                                    let pobj = unsafe { &*p };
4585                                    let key_atom = self.int_atom(idx, ctx);
4586                                    if let Some(entry) = pobj.get_own_accessor_entry(key_atom) {
4587                                        setter_found = entry.set;
4588                                        break;
4589                                    }
4590                                    proto = pobj.prototype;
4591                                }
4592                                if let Some(setter) = setter_found {
4593                                    let _ = self.call_function_with_this(
4594                                        ctx,
4595                                        setter,
4596                                        obj_val,
4597                                        &[value],
4598                                    );
4599                                } else if idx > 100_000 {
4600                                    let len_atom = ctx.common_atoms.length;
4601                                    let old_len = arr
4602                                        .header
4603                                        .get(len_atom)
4604                                        .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
4605                                        .unwrap_or(0);
4606                                    if idx + 1 > old_len {
4607                                        arr.header.set_length_ic(
4608                                            len_atom,
4609                                            JSValue::new_int((idx + 1) as i64),
4610                                            ctx.shape_cache_mut(),
4611                                        );
4612                                    }
4613                                    let key_atom = self.int_atom(idx, ctx);
4614                                    arr.header
4615                                        .set_cached(key_atom, value, ctx.shape_cache_mut());
4616                                    arr.header.clear_dense_array_flag();
4617                                } else {
4618                                    while arr.elements.len() < idx {
4619                                        arr.elements.push(JSValue::undefined());
4620                                    }
4621                                    arr.elements.push(value);
4622                                    let len_atom = ctx.common_atoms.length;
4623
4624                                    let new_elements_len = arr.elements.len();
4625                                    let old_len = arr
4626                                        .header
4627                                        .get(len_atom)
4628                                        .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
4629                                        .unwrap_or(0);
4630                                    let new_len = new_elements_len.max(old_len);
4631                                    if new_len != old_len {
4632                                        arr.header.set_length_ic(
4633                                            len_atom,
4634                                            JSValue::new_int(new_len as i64),
4635                                            ctx.shape_cache_mut(),
4636                                        );
4637                                    }
4638                                }
4639                            }
4640                        } else if js_obj.obj_type() == crate::object::object::ObjectType::TypedArray {
4641                            let idx = key_val.get_int() as usize;
4642                            crate::builtins::typedarray::ta_set_element(ctx, js_obj, idx, value);
4643                        } else {
4644                            let idx = key_val.get_int();
4645                            if idx >= 0 && js_obj.maybe_set_indexed(idx as usize, value) {
4646                            } else {
4647                                let atom = self.int_atom(idx as usize, ctx);
4648                                js_obj.set_cached(atom, value, ctx.shape_cache_mut());
4649                            }
4650                        }
4651                    } else if obj_val.is_object_like() && key_val.is_string() {
4652                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4653                        let atom = key_val.get_atom();
4654
4655                        let setter = js_obj.get_own_accessor_entry(atom).and_then(|e| e.set);
4656                        if let Some(s) = setter {
4657                            if s.is_function() {
4658                                match self.call_function_with_this(ctx, s, obj_val, &[value]) {
4659                                    Ok(_) => {}
4660                                    Err(_) => {}
4661                                }
4662                            }
4663                        } else {
4664                            let pre_offset = js_obj.find_offset(atom);
4665                            if pre_offset.is_some() && !js_obj.is_prop_writable_at(pre_offset) {
4666                                if self.frames[self.frame_index].is_strict_frame {
4667                                    self.set_pending_type_error(
4668                                        ctx,
4669                                        "Cannot assign to read only property",
4670                                    );
4671                                    if let Some(exc) = self.pending_throw.take() {
4672                                        match self.dispatch_throw_value(ctx, exc) {
4673                                            ThrowDispatch::Caught => {}
4674                                            ThrowDispatch::Uncaught(e) => return Err(e),
4675                                            _ => {}
4676                                        }
4677                                    }
4678                                }
4679                            } else {
4680                                js_obj.set_cached(atom, value, ctx.shape_cache_mut());
4681                            }
4682                        }
4683                    } else if obj_val.is_object_like() && key_val.is_float() {
4684                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4685                        let s = VM::js_to_string(&key_val, ctx);
4686                        let atom = s.get_atom();
4687
4688                        let setter = js_obj.get_own_accessor_entry(atom).and_then(|e| e.set);
4689                        if let Some(s) = setter {
4690                            if s.is_function() {
4691                                match self.call_function_with_this(ctx, s, obj_val, &[value]) {
4692                                    Ok(_) => {}
4693                                    Err(_) => {}
4694                                }
4695                            }
4696                        } else {
4697                            let pre_offset = js_obj.find_offset(atom);
4698                            if pre_offset.is_some() && !js_obj.is_prop_writable_at(pre_offset) {
4699                                if self.frames[self.frame_index].is_strict_frame {
4700                                    self.set_pending_type_error(
4701                                        ctx,
4702                                        "Cannot assign to read only property",
4703                                    );
4704                                    if let Some(exc) = self.pending_throw.take() {
4705                                        match self.dispatch_throw_value(ctx, exc) {
4706                                            ThrowDispatch::Caught => {}
4707                                            ThrowDispatch::Uncaught(e) => return Err(e),
4708                                            _ => {}
4709                                        }
4710                                    }
4711                                }
4712                            } else {
4713                                js_obj.set_cached(atom, value, ctx.shape_cache_mut());
4714                            }
4715                        }
4716                    } else if obj_val.is_object_like() && key_val.is_symbol() {
4717                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4718                        let sym_key =
4719                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4720
4721                        let setter = js_obj.get_own_accessor_entry(sym_key).and_then(|e| e.set);
4722                        if let Some(s) = setter {
4723                            if s.is_function() {
4724                                match self.call_function_with_this(ctx, s, obj_val, &[value]) {
4725                                    Ok(_) => {}
4726                                    Err(_) => {}
4727                                }
4728                            }
4729                        } else {
4730                            let pre_offset = js_obj.find_offset(sym_key);
4731                            if pre_offset.is_some() && !js_obj.is_prop_writable_at(pre_offset) {
4732                                if self.frames[self.frame_index].is_strict_frame {
4733                                    self.set_pending_type_error(
4734                                        ctx,
4735                                        "Cannot assign to read only property",
4736                                    );
4737                                    if let Some(exc) = self.pending_throw.take() {
4738                                        match self.dispatch_throw_value(ctx, exc) {
4739                                            ThrowDispatch::Caught => {}
4740                                            ThrowDispatch::Uncaught(e) => return Err(e),
4741                                            _ => {}
4742                                        }
4743                                    }
4744                                }
4745                            } else {
4746                                js_obj.set_cached(sym_key, value, ctx.shape_cache_mut());
4747                            }
4748                        }
4749                    } else if !obj_val.is_object_like() && self.frames[self.frame_index].is_strict_frame {
4750                        self.set_pending_type_error(ctx, "Cannot assign to a primitive value");
4751                        if let Some(exc) = self.pending_throw.take() {
4752                            match self.dispatch_throw_value(ctx, exc) {
4753                                ThrowDispatch::Caught => {}
4754                                ThrowDispatch::Uncaught(e) => return Err(e),
4755                                _ => {}
4756                            }
4757                        }
4758                    }
4759                    self.set_reg(obj_reg, obj_val);
4760                }
4761                Opcode::GetProp => {
4762                    let dst = self.read_u16_pc();
4763                    let obj_reg = self.read_u16_pc();
4764                    let key_reg = self.read_u16_pc();
4765                    let obj_val = self.get_reg(obj_reg);
4766                    let key_val = self.get_reg(key_reg);
4767
4768                    if !obj_val.is_object_like()
4769                        && !obj_val.is_function()
4770                        && (obj_val.is_int()
4771                            || obj_val.is_float()
4772                            || obj_val.is_string()
4773                            || obj_val.is_bool()
4774                            || obj_val.is_symbol())
4775                        && key_val.is_string()
4776                    {
4777                        let atom = key_val.get_atom();
4778                        let start_proto = if obj_val.is_string() {
4779                            ctx.get_string_prototype()
4780                        } else if obj_val.is_int() || obj_val.is_float() {
4781                            ctx.get_number_prototype()
4782                        } else if obj_val.is_symbol() {
4783                            ctx.get_symbol_prototype()
4784                        } else {
4785                            ctx.get_object_prototype()
4786                        };
4787                        let mut current = start_proto;
4788                        let mut found = false;
4789                        while let Some(ptr) = current {
4790                            let pobj = unsafe { &*ptr };
4791                            if let Some(getter) = pobj.get_own_accessor_value(atom) {
4792                                if getter.is_function() {
4793                                    let r = self
4794                                        .call_function_with_this(ctx, getter, obj_val, &[])
4795                                        .unwrap_or(JSValue::undefined());
4796                                    self.set_reg(dst, r);
4797                                } else {
4798                                    self.set_reg(dst, JSValue::undefined());
4799                                }
4800                                found = true;
4801                                break;
4802                            }
4803                            if let Some(val) = pobj.get_own(atom) {
4804                                self.set_reg(dst, val);
4805                                found = true;
4806                                break;
4807                            }
4808                            current = pobj.prototype;
4809                        }
4810                        if !found {
4811                            self.set_reg(dst, JSValue::undefined());
4812                        }
4813                        continue;
4814                    }
4815                    let result = if key_val.is_string() {
4816                        if let Some(result) = self.get_named_prop_result(
4817                            ctx,
4818                            dst,
4819                            obj_val,
4820                            key_val.get_atom(),
4821                            instr_pc,
4822                        ) {
4823                            result
4824                        } else {
4825                            continue;
4826                        }
4827                    } else if key_val.is_symbol() {
4828                        if obj_val.is_object_like() {
4829                            let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4830                            let sym_key =
4831                                crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4832                            js_obj.get(sym_key).unwrap_or(JSValue::undefined())
4833                        } else {
4834                            JSValue::undefined()
4835                        }
4836                    } else if key_val.is_int() && obj_val.is_object_like() {
4837                        if obj_val.is_object() {
4838                            let idx = key_val.get_int();
4839                            let js_obj_check =
4840                                unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4841                            if js_obj_check.is_mapped_arguments() {
4842                                let fi = js_obj_check.mapped_args_frame_index();
4843                                let param_count = js_obj_check.mapped_args_param_count();
4844                                if idx >= 0 && fi < self.frames.len() {
4845                                    let idx_u = idx as usize;
4846                                    if (idx as u32) < param_count {
4847                                        let base = self.frames[fi].registers_base;
4848                                        let reg_idx = base + 1 + idx_u;
4849                                        if reg_idx < self.registers.len() {
4850                                            self.set_reg(dst, self.registers[reg_idx]);
4851                                            continue;
4852                                        }
4853                                    } else {
4854                                        let saved = &self.frames[fi].saved_args;
4855                                        if idx_u < saved.len() {
4856                                            self.set_reg(dst, saved[idx_u]);
4857                                            continue;
4858                                        }
4859                                    }
4860                                }
4861                            }
4862
4863                            if idx >= 0 {
4864                                if let Some(val) = js_obj_check.get_indexed(idx as usize) {
4865                                    self.set_reg(dst, val);
4866                                    continue;
4867                                }
4868                            }
4869                        }
4870                        let atom = self.int_atom(key_val.get_int() as usize, ctx);
4871                        if let Some(result) =
4872                            self.get_named_prop_result(ctx, dst, obj_val, atom, instr_pc)
4873                        {
4874                            result
4875                        } else {
4876                            continue;
4877                        }
4878                    } else if key_val.is_float() && obj_val.is_object_like() {
4879                        let s = VM::js_to_string(&key_val, ctx);
4880                        let atom = s.get_atom();
4881                        if let Some(result) =
4882                            self.get_named_prop_result(ctx, dst, obj_val, atom, instr_pc)
4883                        {
4884                            result
4885                        } else {
4886                            continue;
4887                        }
4888                    } else {
4889                        JSValue::undefined()
4890                    };
4891                    self.set_reg(dst, result);
4892                }
4893                Opcode::GetNamedProp => {
4894                    let dst = self.read_u16_pc();
4895                    let obj_reg = self.read_u16_pc();
4896                    let atom = crate::runtime::atom::Atom(self.read_u32_pc());
4897                    if !self.get_named_prop_fast(ctx, dst, obj_reg, atom, instr_pc) {
4898                        if let Some(exc) = self.pending_throw.take() {
4899                            match self.dispatch_throw_value(ctx, exc) {
4900                                ThrowDispatch::Caught => continue,
4901                                ThrowDispatch::Uncaught(e) => return Err(e),
4902                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4903                            }
4904                        }
4905                        continue;
4906                    }
4907                }
4908                Opcode::SetProp => {
4909                    let obj_reg = self.read_u16_pc();
4910                    let key_reg = self.read_u16_pc();
4911                    let val_reg = self.read_u16_pc();
4912                    let obj_val = self.get_reg(obj_reg);
4913                    let key_val = self.get_reg(key_reg);
4914                    let value = self.get_reg(val_reg);
4915
4916                    if key_val.is_string() {
4917                        self.set_named_prop(ctx, obj_val, value, key_val.get_atom(), usize::MAX);
4918                    } else if key_val.is_symbol() && obj_val.is_object_like() {
4919                        let sym_key =
4920                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4921                        self.set_named_prop(ctx, obj_val, value, sym_key, usize::MAX);
4922                    } else if key_val.is_int() && obj_val.is_object_like() {
4923                        if obj_val.is_object() {
4924                            let js_obj_check =
4925                                unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4926                            if js_obj_check.is_mapped_arguments() {
4927                                let fi = js_obj_check.mapped_args_frame_index();
4928                                let param_count = js_obj_check.mapped_args_param_count();
4929                                let idx = key_val.get_int();
4930                                if idx >= 0 && (idx as u32) < param_count && fi < self.frames.len()
4931                                {
4932                                    let base = self.frames[fi].registers_base;
4933                                    let reg_idx = base + 1 + idx as usize;
4934                                    if reg_idx < self.registers.len() {
4935                                        self.registers[reg_idx] = value;
4936                                    }
4937                                }
4938                            }
4939                        }
4940                        let atom = self.int_atom(key_val.get_int() as usize, ctx);
4941                        self.set_named_prop(ctx, obj_val, value, atom, usize::MAX);
4942                    } else if key_val.is_float() && obj_val.is_object_like() {
4943                        let s = VM::js_to_string(&key_val, ctx);
4944                        let atom = s.get_atom();
4945                        self.set_named_prop(ctx, obj_val, value, atom, usize::MAX);
4946                    }
4947                    self.set_reg(obj_reg, obj_val);
4948                    if let Some(exc) = self.pending_throw.take() {
4949                        match self.dispatch_throw_value(ctx, exc) {
4950                            ThrowDispatch::Caught => continue,
4951                            ThrowDispatch::Uncaught(e) => return Err(e),
4952                            ThrowDispatch::AsyncComplete(_) => continue,
4953                        }
4954                    }
4955                }
4956                Opcode::SetNamedProp => {
4957                    let obj_reg = self.read_u16_pc();
4958                    let val_reg = self.read_u16_pc();
4959                    let atom = crate::runtime::atom::Atom(self.read_u32_pc());
4960                    if self.set_named_prop_fast(ctx, obj_reg, val_reg, atom, instr_pc) {
4961                        continue;
4962                    }
4963                    if let Some(exc) = self.pending_throw.take() {
4964                        match self.dispatch_throw_value(ctx, exc) {
4965                            ThrowDispatch::Caught => continue,
4966                            ThrowDispatch::Uncaught(e) => return Err(e),
4967                            ThrowDispatch::AsyncComplete(_) => continue,
4968                        }
4969                    }
4970                }
4971
4972                Opcode::LtJumpIfNot => {
4973                    let a_reg = self.read_u16_pc();
4974                    let a = self.get_reg(a_reg);
4975                    let b_reg = self.read_u16_pc();
4976                    let b = self.get_reg(b_reg);
4977                    let offset = self.read_i32_pc();
4978                    let cmp = if JSValue::both_int(&a, &b) {
4979                        a.get_int() < b.get_int()
4980                    } else if a.is_float() && b.is_float() {
4981                        a.get_float() < b.get_float()
4982                    } else if a.is_bigint() && b.is_bigint() {
4983                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4984                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4985                        a_int < b_int
4986                    } else {
4987                        a.to_number() < b.to_number()
4988                    };
4989                    if !cmp {
4990                        self.pc = (self.pc as i64 + offset as i64) as usize;
4991                    }
4992                }
4993                Opcode::LtJumpIf => {
4994                    let a_reg = self.read_u16_pc();
4995                    let a = self.get_reg(a_reg);
4996                    let b_reg = self.read_u16_pc();
4997                    let b = self.get_reg(b_reg);
4998                    let offset = self.read_i32_pc();
4999                    let cmp = if JSValue::both_int(&a, &b) {
5000                        a.get_int() < b.get_int()
5001                    } else if a.is_float() && b.is_float() {
5002                        a.get_float() < b.get_float()
5003                    } else if a.is_bigint() && b.is_bigint() {
5004                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5005                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5006                        a_int < b_int
5007                    } else {
5008                        a.to_number() < b.to_number()
5009                    };
5010                    if cmp {
5011                        self.pc = (self.pc as i64 + offset as i64) as usize;
5012                    }
5013                }
5014                Opcode::LteJumpIfNot => {
5015                    let a_reg = self.read_u16_pc();
5016                    let a = self.get_reg(a_reg);
5017                    let b_reg = self.read_u16_pc();
5018                    let b = self.get_reg(b_reg);
5019                    let offset = self.read_i32_pc();
5020                    let cmp = if JSValue::both_int(&a, &b) {
5021                        a.get_int() <= b.get_int()
5022                    } else if a.is_float() && b.is_float() {
5023                        a.get_float() <= b.get_float()
5024                    } else if a.is_bigint() && b.is_bigint() {
5025                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5026                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5027                        a_int <= b_int
5028                    } else {
5029                        a.to_number() <= b.to_number()
5030                    };
5031                    if !cmp {
5032                        self.pc = (self.pc as i64 + offset as i64) as usize;
5033                    }
5034                }
5035                Opcode::LteJumpIf => {
5036                    let a_reg = self.read_u16_pc();
5037                    let a = self.get_reg(a_reg);
5038                    let b_reg = self.read_u16_pc();
5039                    let b = self.get_reg(b_reg);
5040                    let offset = self.read_i32_pc();
5041                    let cmp = if JSValue::both_int(&a, &b) {
5042                        a.get_int() <= b.get_int()
5043                    } else if a.is_float() && b.is_float() {
5044                        a.get_float() <= b.get_float()
5045                    } else if a.is_bigint() && b.is_bigint() {
5046                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5047                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5048                        a_int <= b_int
5049                    } else {
5050                        a.to_number() <= b.to_number()
5051                    };
5052                    if cmp {
5053                        self.pc = (self.pc as i64 + offset as i64) as usize;
5054                    }
5055                }
5056                Opcode::GtJumpIfNot => {
5057                    let a_reg = self.read_u16_pc();
5058                    let a = self.get_reg(a_reg);
5059                    let b_reg = self.read_u16_pc();
5060                    let b = self.get_reg(b_reg);
5061                    let offset = self.read_i32_pc();
5062                    let cmp = if JSValue::both_int(&a, &b) {
5063                        a.get_int() > b.get_int()
5064                    } else if a.is_float() && b.is_float() {
5065                        a.get_float() > b.get_float()
5066                    } else if a.is_bigint() && b.is_bigint() {
5067                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5068                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5069                        a_int > b_int
5070                    } else {
5071                        a.to_number() > b.to_number()
5072                    };
5073                    if !cmp {
5074                        self.pc = (self.pc as i64 + offset as i64) as usize;
5075                    }
5076                }
5077                Opcode::GtJumpIf => {
5078                    let a_reg = self.read_u16_pc();
5079                    let a = self.get_reg(a_reg);
5080                    let b_reg = self.read_u16_pc();
5081                    let b = self.get_reg(b_reg);
5082                    let offset = self.read_i32_pc();
5083                    let cmp = if JSValue::both_int(&a, &b) {
5084                        a.get_int() > b.get_int()
5085                    } else if a.is_float() && b.is_float() {
5086                        a.get_float() > b.get_float()
5087                    } else if a.is_bigint() && b.is_bigint() {
5088                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5089                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5090                        a_int > b_int
5091                    } else {
5092                        a.to_number() > b.to_number()
5093                    };
5094                    if cmp {
5095                        self.pc = (self.pc as i64 + offset as i64) as usize;
5096                    }
5097                }
5098                Opcode::GteJumpIfNot => {
5099                    let a_reg = self.read_u16_pc();
5100                    let a = self.get_reg(a_reg);
5101                    let b_reg = self.read_u16_pc();
5102                    let b = self.get_reg(b_reg);
5103                    let offset = self.read_i32_pc();
5104                    let cmp = if JSValue::both_int(&a, &b) {
5105                        a.get_int() >= b.get_int()
5106                    } else if a.is_float() && b.is_float() {
5107                        a.get_float() >= b.get_float()
5108                    } else if a.is_bigint() && b.is_bigint() {
5109                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5110                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5111                        a_int >= b_int
5112                    } else {
5113                        a.to_number() >= b.to_number()
5114                    };
5115                    if !cmp {
5116                        self.pc = (self.pc as i64 + offset as i64) as usize;
5117                    }
5118                }
5119                Opcode::GteJumpIf => {
5120                    let a_reg = self.read_u16_pc();
5121                    let a = self.get_reg(a_reg);
5122                    let b_reg = self.read_u16_pc();
5123                    let b = self.get_reg(b_reg);
5124                    let offset = self.read_i32_pc();
5125                    let cmp = if JSValue::both_int(&a, &b) {
5126                        a.get_int() >= b.get_int()
5127                    } else if a.is_float() && b.is_float() {
5128                        a.get_float() >= b.get_float()
5129                    } else if a.is_bigint() && b.is_bigint() {
5130                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
5131                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
5132                        a_int >= b_int
5133                    } else {
5134                        a.to_number() >= b.to_number()
5135                    };
5136                    if cmp {
5137                        self.pc = (self.pc as i64 + offset as i64) as usize;
5138                    }
5139                }
5140                Opcode::EqJumpIfNot => {
5141                    let a_reg = self.read_u16_pc();
5142                    let a = self.get_reg(a_reg);
5143                    let b_reg = self.read_u16_pc();
5144                    let b = self.get_reg(b_reg);
5145                    let offset = self.read_i32_pc();
5146                    if !loose_equal(ctx, a, b) {
5147                        self.pc = (self.pc as i32 + offset) as usize;
5148                    }
5149                }
5150                Opcode::EqJumpIf => {
5151                    let a_reg = self.read_u16_pc();
5152                    let a = self.get_reg(a_reg);
5153                    let b_reg = self.read_u16_pc();
5154                    let b = self.get_reg(b_reg);
5155                    let offset = self.read_i32_pc();
5156                    if loose_equal(ctx, a, b) {
5157                        self.pc = (self.pc as i32 + offset) as usize;
5158                    }
5159                }
5160                Opcode::NeqJumpIfNot => {
5161                    let a_reg = self.read_u16_pc();
5162                    let a = self.get_reg(a_reg);
5163                    let b_reg = self.read_u16_pc();
5164                    let b = self.get_reg(b_reg);
5165                    let offset = self.read_i32_pc();
5166                    if loose_equal(ctx, a, b) {
5167                        self.pc = (self.pc as i32 + offset) as usize;
5168                    }
5169                }
5170                Opcode::NeqJumpIf => {
5171                    let a_reg = self.read_u16_pc();
5172                    let a = self.get_reg(a_reg);
5173                    let b_reg = self.read_u16_pc();
5174                    let b = self.get_reg(b_reg);
5175                    let offset = self.read_i32_pc();
5176                    if !loose_equal(ctx, a, b) {
5177                        self.pc = (self.pc as i32 + offset) as usize;
5178                    }
5179                }
5180                Opcode::StrictEqJumpIfNot => {
5181                    let a_reg = self.read_u16_pc();
5182                    let a = self.get_reg(a_reg);
5183                    let b_reg = self.read_u16_pc();
5184                    let b = self.get_reg(b_reg);
5185                    let offset = self.read_i32_pc();
5186                    if !a.strict_eq(&b) {
5187                        self.pc = (self.pc as i32 + offset) as usize;
5188                    }
5189                }
5190                Opcode::StrictEqJumpIf => {
5191                    let a_reg = self.read_u16_pc();
5192                    let a = self.get_reg(a_reg);
5193                    let b_reg = self.read_u16_pc();
5194                    let b = self.get_reg(b_reg);
5195                    let offset = self.read_i32_pc();
5196                    if a.strict_eq(&b) {
5197                        self.pc = (self.pc as i32 + offset) as usize;
5198                    }
5199                }
5200                Opcode::StrictNeqJumpIfNot => {
5201                    let a_reg = self.read_u16_pc();
5202                    let a = self.get_reg(a_reg);
5203                    let b_reg = self.read_u16_pc();
5204                    let b = self.get_reg(b_reg);
5205                    let offset = self.read_i32_pc();
5206                    if a.strict_eq(&b) {
5207                        self.pc = (self.pc as i32 + offset) as usize;
5208                    }
5209                }
5210                Opcode::StrictNeqJumpIf => {
5211                    let a_reg = self.read_u16_pc();
5212                    let a = self.get_reg(a_reg);
5213                    let b_reg = self.read_u16_pc();
5214                    let b = self.get_reg(b_reg);
5215                    let offset = self.read_i32_pc();
5216                    if !a.strict_eq(&b) {
5217                        self.pc = (self.pc as i32 + offset) as usize;
5218                    }
5219                }
5220
5221                Opcode::DeleteProp => {
5222                    let dst = self.read_u16_pc();
5223                    let obj_reg = self.read_u16_pc();
5224                    let key_reg = self.read_u16_pc();
5225                    let obj_val = self.get_reg(obj_reg);
5226                    let key_val = self.get_reg(key_reg);
5227                    if obj_val.is_null() || obj_val.is_undefined() {
5228                        self.set_pending_type_error(
5229                            ctx,
5230                            "Cannot delete property from null or undefined",
5231                        );
5232                        if let Some(exc) = self.pending_throw.take() {
5233                            match self.dispatch_throw_value(ctx, exc) {
5234                                ThrowDispatch::Caught => {}
5235                                ThrowDispatch::Uncaught(e) => return Err(e),
5236                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
5237                            }
5238                        }
5239                        continue;
5240                    }
5241                    let atom = if key_val.is_string() {
5242                        Some(key_val.get_atom())
5243                    } else if key_val.is_symbol() {
5244                        let sym_id = key_val.get_symbol_id();
5245                        Some(crate::runtime::atom::Atom(0x40000000 | sym_id))
5246                    } else if key_val.is_int() && (obj_val.is_object() || obj_val.is_function()) {
5247                        Some(self.int_atom(key_val.get_int() as usize, ctx))
5248                    } else if key_val.is_float() && (obj_val.is_object() || obj_val.is_function()) {
5249                        let s = VM::js_to_string(&key_val, ctx);
5250                        Some(s.get_atom())
5251                    } else {
5252                        None
5253                    };
5254                    let result = if let Some(atom) = atom {
5255                        if obj_val.is_object() || obj_val.is_function() {
5256                            let js_obj = obj_val.as_object_mut();
5257                            let mut deleted = false;
5258                            if js_obj.is_dense_array() && key_val.is_int() {
5259                                let idx = key_val.get_int();
5260                                if idx >= 0 {
5261                                    let ptr = obj_val.get_ptr();
5262                                    let arr = unsafe {
5263                                        &mut *(ptr as *mut crate::object::array_obj::JSArrayObject)
5264                                    };
5265                                    if (idx as usize) < arr.elements.len() {
5266                                        arr.elements[idx as usize] = JSValue::undefined();
5267                                        deleted = true;
5268                                    }
5269                                }
5270                            }
5271                            if !deleted {
5272                                deleted = js_obj.delete(atom);
5273                            }
5274                            if deleted {
5275                                ctx.runtime_mut().gc_heap_mut().deleted_props_count += 1;
5276                            }
5277                            deleted
5278                        } else {
5279                            true
5280                        }
5281                    } else {
5282                        true
5283                    };
5284                    let is_strict = self.frames[self.frame_index].is_strict_frame;
5285                    if is_strict && !result {
5286                        self.set_pending_type_error(ctx, "Cannot delete property");
5287                        if let Some(exc) = self.pending_throw.take() {
5288                            match self.dispatch_throw_value(ctx, exc) {
5289                                ThrowDispatch::Caught => {}
5290                                ThrowDispatch::Uncaught(e) => return Err(e),
5291                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
5292                            }
5293                        }
5294                        continue;
5295                    }
5296                    self.set_reg(dst, JSValue::bool(result));
5297                }
5298                Opcode::HasProperty => {
5299                    let dst = self.read_u16_pc();
5300                    let obj_reg = self.read_u16_pc();
5301                    let key_reg = self.read_u16_pc();
5302                    let obj_val = self.get_reg(obj_reg);
5303                    let key_val = self.get_reg(key_reg);
5304                    let result = if (obj_val.is_object() || obj_val.is_function())
5305                        && key_val.is_string()
5306                    {
5307                        let js_obj = obj_val.as_object();
5308                        let atom = key_val.get_atom();
5309                        js_obj.has_property(atom)
5310                    } else if (obj_val.is_object() || obj_val.is_function()) && key_val.is_int() {
5311                        let js_obj = obj_val.as_object();
5312                        let atom = self.int_atom(key_val.get_int() as usize, ctx);
5313                        js_obj.has_property(atom)
5314                    } else if (obj_val.is_object() || obj_val.is_function()) && key_val.is_float() {
5315                        let js_obj = obj_val.as_object();
5316                        let s = VM::js_to_string(&key_val, ctx);
5317                        let atom = s.get_atom();
5318                        js_obj.has_property(atom)
5319                    } else if (obj_val.is_object() || obj_val.is_function()) && key_val.is_symbol()
5320                    {
5321                        let js_obj = obj_val.as_object();
5322                        let sym_key =
5323                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
5324                        js_obj.has_property(sym_key)
5325                    } else {
5326                        false
5327                    };
5328                    self.set_reg(dst, JSValue::bool(result));
5329                }
5330                Opcode::InstanceOf => {
5331                    let dst = self.read_u16_pc();
5332                    let obj_reg = self.read_u16_pc();
5333                    let ctor_reg = self.read_u16_pc();
5334                    let obj_val = self.get_reg(obj_reg);
5335                    let ctor_val = self.get_reg(ctor_reg);
5336                    if !ctor_val.is_object() && !ctor_val.is_function() {
5337                        self.set_pending_type_error(
5338                            ctx,
5339                            "Right-hand side of 'instanceof' is not an object",
5340                        );
5341                        if let Some(exc) = self.pending_throw.take() {
5342                            match self.dispatch_throw_value(ctx, exc) {
5343                                ThrowDispatch::Caught => {
5344                                    self.set_reg(dst, JSValue::undefined());
5345                                }
5346                                ThrowDispatch::Uncaught(e) => return Err(e),
5347                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
5348                            }
5349                        }
5350                        continue;
5351                    }
5352
5353                    if ctor_val.is_function() && (obj_val.is_object() || obj_val.is_function()) {
5354                        let ctor_ptr = ctor_val.get_ptr();
5355                        let obj_ptr = obj_val.get_ptr();
5356                        let obj_proto = unsafe {
5357                            (*(obj_ptr as *const crate::object::object::JSObject)).prototype
5358                        };
5359                        if let Some(op) = obj_proto {
5360                            if ctor_ptr == self.instanceof_cache_ctor
5361                                && op as usize == self.instanceof_cache_proto
5362                            {
5363                                self.set_reg(dst, JSValue::bool(self.instanceof_cache_result));
5364                                continue;
5365                            }
5366                        }
5367                    }
5368
5369                    let hi_atom = if self.cached_has_instance_atom.0 != 0 {
5370                        self.cached_has_instance_atom
5371                    } else {
5372                        let sym = crate::builtins::symbol::get_symbol_has_instance(ctx);
5373                        let a = if sym.is_symbol() {
5374                            crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
5375                        } else {
5376                            unreachable!()
5377                        };
5378                        self.cached_has_instance_atom = a;
5379                        a
5380                    };
5381                    let has_instance_handler = if ctor_val.is_function() {
5382                        let jf = ctor_val.as_function();
5383
5384                        if jf.has_symbol_on_base() {
5385                            jf.base.get_own(hi_atom)
5386                        } else {
5387                            None
5388                        }
5389                    } else {
5390                        ctor_val.as_object().get_own(hi_atom)
5391                    };
5392                    if let Some(handler) = has_instance_handler {
5393                        if handler.is_function() || handler.is_object() {
5394                            if handler.is_function() {
5395                                match self.call_function_with_this(
5396                                    ctx,
5397                                    handler,
5398                                    ctor_val,
5399                                    &[obj_val],
5400                                ) {
5401                                    Ok(v) => {
5402                                        self.set_reg(dst, JSValue::bool(v.is_truthy()));
5403                                        continue;
5404                                    }
5405                                    Err(_) => {
5406                                        self.set_reg(dst, JSValue::undefined());
5407                                        continue;
5408                                    }
5409                                }
5410                            }
5411                        }
5412                    }
5413                    if !ctor_val.is_function() {
5414                        self.set_pending_type_error(
5415                            ctx,
5416                            "Right-hand side of 'instanceof' is not callable",
5417                        );
5418                        if let Some(exc) = self.pending_throw.take() {
5419                            match self.dispatch_throw_value(ctx, exc) {
5420                                ThrowDispatch::Caught => {
5421                                    self.set_reg(dst, JSValue::undefined());
5422                                }
5423                                ThrowDispatch::Uncaught(e) => return Err(e),
5424                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
5425                            }
5426                        }
5427                        continue;
5428                    }
5429
5430                    let result = if obj_val.is_object() || obj_val.is_function() {
5431                        let ctor_proto_opt = if ctor_val.is_function() {
5432                            let js_func = ctor_val.as_function();
5433                            if !js_func.cached_prototype_ptr.is_null() {
5434                                Some(
5435                                    js_func.cached_prototype_ptr
5436                                        as *const crate::object::object::JSObject,
5437                                )
5438                            } else {
5439                                let proto_atom = ctx.common_atoms.prototype;
5440                                js_func.base.get(proto_atom).and_then(|v| {
5441                                    if v.is_object() {
5442                                        let ptr =
5443                                            v.get_ptr() as *mut crate::object::object::JSObject;
5444
5445                                        ctor_val.as_function_mut().cached_prototype_ptr = ptr;
5446                                        Some(ptr as *const crate::object::object::JSObject)
5447                                    } else {
5448                                        None
5449                                    }
5450                                })
5451                            }
5452                        } else {
5453                            None
5454                        };
5455                        if let Some(ctor_proto_ptr) = ctor_proto_opt {
5456                            let obj_ptr = obj_val.get_ptr();
5457                            let mut proto_opt = unsafe {
5458                                (*(obj_ptr as *const crate::object::object::JSObject)).prototype
5459                            };
5460                            let mut found = false;
5461                            let mut limit = 0;
5462                            while let Some(proto_ptr) = proto_opt {
5463                                if std::ptr::eq(proto_ptr, ctor_proto_ptr) {
5464                                    found = true;
5465                                    break;
5466                                }
5467                                proto_opt = unsafe { (*proto_ptr).prototype };
5468                                limit += 1;
5469                                if limit > 1000 {
5470                                    break;
5471                                }
5472                            }
5473                            found
5474                        } else {
5475                            false
5476                        }
5477                    } else {
5478                        false
5479                    };
5480                    if ctor_val.is_function() && (obj_val.is_object() || obj_val.is_function()) {
5481                        let ctor_ptr = ctor_val.get_ptr();
5482                        let obj_ptr = obj_val.get_ptr();
5483                        let obj_proto = unsafe {
5484                            (*(obj_ptr as *const crate::object::object::JSObject)).prototype
5485                        };
5486                        if let Some(op) = obj_proto {
5487                            self.instanceof_cache_ctor = ctor_ptr;
5488                            self.instanceof_cache_proto = op as usize;
5489                            self.instanceof_cache_result = result;
5490                        }
5491                    }
5492                    self.set_reg(dst, JSValue::bool(result));
5493                }
5494                Opcode::NewRegExp => {
5495                    let cache_key = self.cached_code_ptr as usize + self.pc;
5496                    let dst = self.read_u16_pc();
5497                    let pattern_idx = self.read_u32() as usize;
5498                    let flags_idx = self.read_u32() as usize;
5499                    let constants_len = self.frames[self.frame_index].constants_len;
5500                    let pattern_val = if pattern_idx < constants_len {
5501                        unsafe { *self.cached_const_ptr.add(pattern_idx) }
5502                    } else {
5503                        JSValue::undefined()
5504                    };
5505                    let flags_val = if flags_idx < constants_len {
5506                        unsafe { *self.cached_const_ptr.add(flags_idx) }
5507                    } else {
5508                        JSValue::undefined()
5509                    };
5510                    let pattern = if pattern_val.is_string() {
5511                        ctx.get_atom_str(pattern_val.get_atom()).to_string()
5512                    } else {
5513                        String::new()
5514                    };
5515                    let flags = if flags_val.is_string() {
5516                        ctx.get_atom_str(flags_val.get_atom()).to_string()
5517                    } else {
5518                        String::new()
5519                    };
5520
5521                    if let Some(cached_re) = self.regex_lit_cache.get(&cache_key) {
5522                        let cloned_re = cached_re.clone();
5523                        let re_val = crate::builtins::regexp::create_regexp_object_precompiled(
5524                            ctx, &pattern, &flags, cloned_re,
5525                        );
5526                        self.set_reg(dst, re_val);
5527                    } else {
5528                        let pattern_atom = ctx.intern(&pattern);
5529                        let flags_atom = ctx.intern(&flags);
5530                        let re_val = crate::builtins::regexp::regexp_constructor(
5531                            ctx,
5532                            &[
5533                                JSValue::new_string(pattern_atom),
5534                                JSValue::new_string(flags_atom),
5535                            ],
5536                        );
5537
5538                        if re_val.is_object() {
5539                            if let Some(compiled) = re_val.as_object().get_compiled_regex() {
5540                                self.regex_lit_cache.insert(cache_key, compiled.clone());
5541                            }
5542                        }
5543                        self.set_reg(dst, re_val);
5544                    }
5545                }
5546                Opcode::GetPrivate => {
5547                    let dst = self.read_u16_pc();
5548                    let obj_reg = self.read_u16_pc();
5549                    let key_reg = self.read_u16_pc();
5550                    let obj_val = self.get_reg(obj_reg);
5551                    let key_val = self.get_reg(key_reg);
5552                    let result = if (obj_val.is_object() || obj_val.is_function())
5553                        && key_val.is_string()
5554                    {
5555                        let js_obj = obj_val.as_object();
5556                        let atom = key_val.get_atom();
5557                        let atom_str = ctx.get_atom_str(atom).to_string();
5558                        let alt_atom = if let Some(stripped) = atom_str.strip_prefix('#') {
5559                            Some(ctx.intern(stripped))
5560                        } else {
5561                            None
5562                        };
5563
5564                        let own_accessor =
5565                            js_obj.get_own_private_accessor_entry(atom).or_else(|| {
5566                                alt_atom.and_then(|a| js_obj.get_own_private_accessor_entry(a))
5567                            });
5568                        if let Some(entry) = own_accessor {
5569                            if let Some(getter) = entry.get {
5570                                self.call_function_with_this(ctx, getter, obj_val, &[])
5571                                    .unwrap_or(JSValue::undefined())
5572                            } else {
5573                                JSValue::undefined()
5574                            }
5575                        } else if let Some(val) = js_obj
5576                            .get_private_field(atom)
5577                            .or_else(|| alt_atom.and_then(|a| js_obj.get_private_field(a)))
5578                        {
5579                            val
5580                        } else {
5581                            let mut getter_fn: Option<JSValue> = None;
5582                            let mut inherited_value: Option<JSValue> = None;
5583                            let mut cur = js_obj.prototype;
5584                            let mut depth = 0u32;
5585                            while let Some(p) = cur {
5586                                if p.is_null() || depth > 100 {
5587                                    break;
5588                                }
5589                                let proto_obj = unsafe { &*p };
5590                                let accessor_entry =
5591                                    proto_obj.get_own_private_accessor_entry(atom).or_else(|| {
5592                                        alt_atom.and_then(|a| {
5593                                            proto_obj.get_own_private_accessor_entry(a)
5594                                        })
5595                                    });
5596                                if let Some(entry) = accessor_entry {
5597                                    getter_fn = entry.get;
5598                                    break;
5599                                }
5600                                if let Some(v) = proto_obj.get_private_field(atom).or_else(|| {
5601                                    alt_atom.and_then(|a| proto_obj.get_private_field(a))
5602                                }) {
5603                                    inherited_value = Some(v);
5604                                    break;
5605                                }
5606                                cur = proto_obj.prototype;
5607                                depth += 1;
5608                            }
5609                            if let Some(getter) = getter_fn {
5610                                self.call_function_with_this(ctx, getter, obj_val, &[])
5611                                    .unwrap_or(JSValue::undefined())
5612                            } else if let Some(v) = inherited_value {
5613                                v
5614                            } else {
5615                                JSValue::undefined()
5616                            }
5617                        }
5618                    } else {
5619                        JSValue::undefined()
5620                    };
5621                    self.set_reg(dst, result);
5622                }
5623                Opcode::SetPrivate => {
5624                    let obj_reg = self.read_u16_pc();
5625                    let key_reg = self.read_u16_pc();
5626                    let val_reg = self.read_u16_pc();
5627                    let obj_val = self.get_reg(obj_reg);
5628                    let key_val = self.get_reg(key_reg);
5629                    let val = self.get_reg(val_reg);
5630                    if (obj_val.is_object() || obj_val.is_function()) && key_val.is_string() {
5631                        let atom = key_val.get_atom();
5632                        let atom_str = ctx.get_atom_str(atom).to_string();
5633                        let alt_atom = if let Some(stripped) = atom_str.strip_prefix('#') {
5634                            Some(ctx.intern(stripped))
5635                        } else {
5636                            None
5637                        };
5638
5639                        let own_accessor_entry = {
5640                            let js_obj = obj_val.as_object();
5641                            js_obj
5642                                .get_own_private_accessor_entry(atom)
5643                                .or_else(|| {
5644                                    alt_atom.and_then(|a| js_obj.get_own_private_accessor_entry(a))
5645                                })
5646                                .cloned()
5647                        };
5648                        if let Some(entry) = own_accessor_entry {
5649                            if let Some(setter) = entry.set {
5650                                let _ = self.call_function_with_this(ctx, setter, obj_val, &[val]);
5651                            } else {
5652                                self.set_pending_type_error(
5653                                    ctx,
5654                                    "Cannot set private property without setter",
5655                                );
5656                                if let Some(exc) = self.pending_throw.take() {
5657                                    match self.dispatch_throw_value(ctx, exc) {
5658                                        ThrowDispatch::Caught => continue,
5659                                        ThrowDispatch::Uncaught(e) => return Err(e),
5660                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
5661                                    }
5662                                }
5663                            }
5664                        } else {
5665                            let mut setter_fn: Option<JSValue> = None;
5666                            let mut accessor_found = false;
5667                            {
5668                                let js_obj = obj_val.as_object();
5669                                let mut cur = js_obj.prototype;
5670                                let mut depth = 0u32;
5671                                while let Some(p) = cur {
5672                                    if p.is_null() || depth > 100 {
5673                                        break;
5674                                    }
5675                                    let proto_obj = unsafe { &*p };
5676                                    let accessor_entry = proto_obj
5677                                        .get_own_private_accessor_entry(atom)
5678                                        .or_else(|| {
5679                                            alt_atom.and_then(|a| {
5680                                                proto_obj.get_own_private_accessor_entry(a)
5681                                            })
5682                                        });
5683                                    if let Some(entry) = accessor_entry {
5684                                        accessor_found = true;
5685                                        setter_fn = entry.set;
5686                                        break;
5687                                    }
5688                                    cur = proto_obj.prototype;
5689                                    depth += 1;
5690                                }
5691                            }
5692                            if let Some(setter) = setter_fn {
5693                                let _ = self.call_function_with_this(ctx, setter, obj_val, &[val]);
5694                            } else if accessor_found {
5695                                self.set_pending_type_error(
5696                                    ctx,
5697                                    "Cannot set private property without setter",
5698                                );
5699                                if let Some(exc) = self.pending_throw.take() {
5700                                    match self.dispatch_throw_value(ctx, exc) {
5701                                        ThrowDispatch::Caught => continue,
5702                                        ThrowDispatch::Uncaught(e) => return Err(e),
5703                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
5704                                    }
5705                                }
5706                            } else {
5707                                let js_obj = obj_val.as_object_mut();
5708                                js_obj.set_private_field(atom, val);
5709                            }
5710                        }
5711                    }
5712                }
5713                Opcode::HasPrivate => {
5714                    let dst = self.read_u16_pc();
5715                    let obj_reg = self.read_u16_pc();
5716                    let key_reg = self.read_u16_pc();
5717                    let obj_val = self.get_reg(obj_reg);
5718                    let key_val = self.get_reg(key_reg);
5719                    let result =
5720                        if (obj_val.is_object() || obj_val.is_function()) && key_val.is_string() {
5721                            let js_obj = obj_val.as_object();
5722                            let atom = key_val.get_atom();
5723                            js_obj.has_private_field(atom)
5724                        } else {
5725                            false
5726                        };
5727                    self.set_reg(dst, JSValue::bool(result));
5728                }
5729                Opcode::GatherRest => {
5730                    let dst = self.read_u16_pc();
5731                    let base = self.frames[self.frame_index].registers_base;
5732                    let arg_count = self.frames[self.frame_index].arg_count as usize;
5733                    let rest_start = dst as usize;
5734
5735                    let rest_count = if arg_count + 1 > rest_start {
5736                        arg_count + 1 - rest_start
5737                    } else {
5738                        0
5739                    };
5740                    let mut arr =
5741                        crate::object::array_obj::JSArrayObject::with_capacity(rest_count);
5742                    if let Some(proto_ptr) = ctx.get_array_prototype() {
5743                        arr.header.set_prototype_raw(proto_ptr);
5744                    }
5745                    for i in 0..rest_count {
5746                        let val = self.registers[base + rest_start + i];
5747                        arr.push(val);
5748                    }
5749                    let len_atom = ctx.common_atoms.length;
5750                    arr.header
5751                        .set(len_atom, JSValue::new_int(rest_count as i64));
5752                    let ptr = Box::into_raw(Box::new(arr)) as usize;
5753                    ctx.runtime_mut().gc_heap_mut().track_array(ptr);
5754                    self.set_reg(dst, JSValue::new_object(ptr));
5755                }
5756                Opcode::ObjectSpread => {
5757                    let dst = self.read_u16_pc();
5758                    let src = self.read_u16_pc();
5759                    let dst_val = self.get_reg(dst);
5760                    let src_val = self.get_reg(src);
5761                    if (dst_val.is_object() || dst_val.is_function())
5762                        && (src_val.is_object() || src_val.is_function())
5763                    {
5764                        let dst_obj = dst_val.as_object_mut();
5765                        let src_obj = src_val.as_object();
5766
5767                        for (atom, value) in src_obj.own_properties() {
5768                            dst_obj.set_cached(atom, value, ctx.shape_cache_mut());
5769                        }
5770                    }
5771                }
5772                Opcode::GetPropertyNames => {
5773                    let dst = self.read_u16_pc();
5774                    let obj_reg = self.read_u16_pc();
5775                    let obj_val = self.get_reg(obj_reg);
5776                    let mut arr = crate::object::array_obj::JSArrayObject::new();
5777                    if let Some(proto_ptr) = ctx.get_array_prototype() {
5778                        arr.header.set_prototype_raw(proto_ptr);
5779                    }
5780                    // Collect all enumerable properties from the object and its prototype chain
5781                    let mut seen = Vec::new();
5782                    let obj_proto_ptr = ctx.get_object_prototype().map(|p| p as usize);
5783                    let arr_proto_ptr = ctx.get_array_prototype().map(|p| p as usize);
5784                    let str_proto_ptr = ctx.get_string_prototype().map(|p| p as usize);
5785                    let mut current_ptr = if obj_val.is_object() || obj_val.is_function() {
5786                        Some(obj_val.get_ptr() as usize)
5787                    } else {
5788                        None
5789                    };
5790                    while let Some(ptr) = current_ptr {
5791                        // Stop at built-in prototypes (their methods should be non-enumerable)
5792                        if let Some(obj_proto) = obj_proto_ptr {
5793                            if ptr == obj_proto {
5794                                break;
5795                            }
5796                        }
5797                        if let Some(arr_proto) = arr_proto_ptr {
5798                            if ptr == arr_proto {
5799                                break;
5800                            }
5801                        }
5802                        if let Some(str_proto) = str_proto_ptr {
5803                            if ptr == str_proto {
5804                                break;
5805                            }
5806                        }
5807                        let js_obj = unsafe { &*(ptr as *const crate::object::object::JSObject) };
5808                        let mut int_keys: Vec<i64> = Vec::new();
5809                        let mut str_keys: Vec<crate::runtime::atom::Atom> = Vec::new();
5810                        // Handle dense array indices
5811                        if js_obj.is_dense_array() {
5812                            let arr_obj =
5813                                unsafe { &*(ptr as *mut crate::object::array_obj::JSArrayObject) };
5814                            for i in 0..arr_obj.elements.len() {
5815                                let idx_atom = ctx.intern(i.to_string().as_str());
5816                                if !seen.contains(&idx_atom.0) {
5817                                    seen.push(idx_atom.0);
5818                                    int_keys.push(i as i64);
5819                                }
5820                            }
5821                        }
5822                        for (atom, _value) in js_obj.own_properties() {
5823                            // Track ALL own properties (including non-enumerable) for shadowing
5824                            if seen.contains(&atom.0) {
5825                                continue;
5826                            }
5827                            seen.push(atom.0);
5828                            let key_str = ctx.get_atom_str(atom);
5829                            if let Ok(n) = key_str.parse::<i64>() {
5830                                if n >= 0 && key_str == n.to_string() {
5831                                    int_keys.push(n);
5832                                    continue;
5833                                }
5834                            }
5835                            str_keys.push(atom);
5836                        }
5837                        // Also track non-enumerable own properties for shadowing
5838                        for atom in js_obj.non_enumerable_property_atoms() {
5839                            if !seen.contains(&atom.0) {
5840                                seen.push(atom.0);
5841                            }
5842                        }
5843                        int_keys.sort();
5844                        for k in &int_keys {
5845                            let atom = ctx.intern(&k.to_string());
5846                            arr.push(JSValue::new_string(atom));
5847                        }
5848                        for atom in str_keys {
5849                            arr.push(JSValue::new_string(atom));
5850                        }
5851                        current_ptr = js_obj.prototype.map(|p| p as usize);
5852                    }
5853                    let len_atom = ctx.common_atoms.length;
5854                    arr.header
5855                        .set(len_atom, JSValue::new_int(arr.elements.len() as i64));
5856                    let ptr = Box::into_raw(Box::new(arr)) as usize;
5857                    ctx.runtime_mut().gc_heap_mut().track_array(ptr);
5858                    self.allocation_count += 1;
5859                    self.set_reg(dst, JSValue::new_object(ptr));
5860                    self.maybe_gc(ctx);
5861                }
5862                Opcode::ArrayExtend => {
5863                    let dst = self.read_u16_pc();
5864                    let src = self.read_u16_pc();
5865                    let dst_val = self.get_reg(dst);
5866                    let src_val = self.get_reg(src);
5867                    if dst_val.is_object() && src_val.is_object() {
5868                        let dst_ptr = dst_val.get_ptr();
5869                        let src_ptr = src_val.get_ptr();
5870                        let is_src_array = src_val.as_object().is_dense_array();
5871                        let is_dst_array = dst_val.as_object().is_dense_array();
5872                        if is_src_array && is_dst_array {
5873                            let dst_arr = unsafe {
5874                                &mut *(dst_ptr as *mut crate::object::array_obj::JSArrayObject)
5875                            };
5876                            let src_arr = unsafe {
5877                                &*(src_ptr as *const crate::object::array_obj::JSArrayObject)
5878                            };
5879                            for val in src_arr.elements.iter() {
5880                                dst_arr.push(*val);
5881                            }
5882                            let len_atom = ctx.common_atoms.length;
5883                            dst_arr
5884                                .header
5885                                .set(len_atom, JSValue::new_int(dst_arr.elements.len() as i64));
5886                        } else if is_dst_array {
5887                            let dst_arr = unsafe {
5888                                &mut *(dst_ptr as *mut crate::object::array_obj::JSArrayObject)
5889                            };
5890                            let src_obj =
5891                                unsafe { &*(src_ptr as *const crate::object::object::JSObject) };
5892                            let len_atom = ctx.common_atoms.length;
5893                            let arr_len = src_obj
5894                                .get(len_atom)
5895                                .map(|v| v.get_int() as usize)
5896                                .unwrap_or(0);
5897                            if let Some(elems) = src_obj.get_array_elements() {
5898                                for val in elems.iter() {
5899                                    dst_arr.push(*val);
5900                                }
5901                            } else {
5902                                for i in 0..arr_len {
5903                                    let key = ctx.intern(&i.to_string());
5904                                    if let Some(val) = src_obj.get(key) {
5905                                        dst_arr.push(val);
5906                                    }
5907                                }
5908                            }
5909                            dst_arr
5910                                .header
5911                                .set(len_atom, JSValue::new_int(dst_arr.elements.len() as i64));
5912                        }
5913                    }
5914                }
5915                Opcode::ArrayPush => {
5916                    let arr_reg = self.read_u16_pc();
5917                    let val_reg = self.read_u16_pc();
5918                    let arr_val = self.get_reg(arr_reg);
5919                    let val = self.get_reg(val_reg);
5920                    if arr_val.is_object() {
5921                        let arr_ptr = arr_val.get_ptr();
5922                        let arr = unsafe {
5923                            &mut *(arr_ptr as *mut crate::object::array_obj::JSArrayObject)
5924                        };
5925                        arr.push(val);
5926                        let len_atom = ctx.common_atoms.length;
5927                        let new_len = arr.elements.len() as i64;
5928                        if !arr.header.has_own(len_atom) {
5929                            arr.header.define_property(
5930                                len_atom,
5931                                crate::object::object::PropertyDescriptor {
5932                                    value: Some(JSValue::new_int(new_len)),
5933                                    writable: true,
5934                                    enumerable: false,
5935                                    configurable: false,
5936                                    get: None,
5937                                    set: None,
5938                                },
5939                            );
5940                        } else {
5941                            arr.header.set(len_atom, JSValue::new_int(new_len));
5942                        }
5943                    }
5944                }
5945                Opcode::SetProto => {
5946                    let obj_reg = self.read_u16_pc();
5947                    let proto_reg = self.read_u16_pc();
5948                    let obj_val = self.get_reg(obj_reg);
5949                    let proto_val = self.get_reg(proto_reg);
5950                    if obj_val.is_object() && proto_val.is_object() {
5951                        let obj_ref = obj_val.as_object_mut();
5952                        obj_ref.prototype =
5953                            Some(proto_val.get_ptr() as *mut crate::object::object::JSObject);
5954                    }
5955                }
5956                Opcode::GetSuper => {
5957                    let dst = self.read_u16_pc();
5958                    self.set_reg(dst, self.frames[self.frame_index].super_ctor);
5959                }
5960
5961                Opcode::DefineAccessor => {
5962                    let obj_reg = self.read_u16_pc();
5963                    let key_reg = self.read_u16_pc();
5964                    let getter_reg = self.read_u16_pc();
5965                    let setter_reg = self.read_u16_pc();
5966                    let obj_val = self.get_reg(obj_reg);
5967                    let key_val = self.get_reg(key_reg);
5968                    if obj_val.is_object_like() {
5969                        let prop_key = if key_val.is_string() {
5970                            key_val
5971                        } else {
5972                            let prim = self.ordinary_to_primitive(&key_val, "string", ctx);
5973                            if self.pending_throw.is_some() {
5974                                if let Some(exc) = self.pending_throw.take() {
5975                                    let disp = self.dispatch_throw_value(ctx, exc);
5976                                    match disp {
5977                                        ThrowDispatch::Caught => {}
5978                                        ThrowDispatch::Uncaught(e) => return Err(e),
5979                                        ThrowDispatch::AsyncComplete(o) => match o {
5980                                            ExecutionOutcome::Complete(v) => {
5981                                                self.set_reg(obj_reg, v);
5982                                            }
5983                                            _ => {}
5984                                        },
5985                                    }
5986                                }
5987                                continue;
5988                            }
5989                            if prim.is_string() {
5990                                prim
5991                            } else {
5992                                VM::js_to_string(&prim, ctx)
5993                            }
5994                        };
5995                        if prop_key.is_string() {
5996                            let atom = prop_key.get_atom();
5997                            let obj = obj_val.as_object_mut();
5998                            let getter = if getter_reg != u16::MAX {
5999                                Some(self.get_reg(getter_reg))
6000                            } else {
6001                                None
6002                            };
6003                            let setter = if setter_reg != u16::MAX {
6004                                Some(self.get_reg(setter_reg))
6005                            } else {
6006                                None
6007                            };
6008                            let existing = obj.get_own_accessor_entry(atom);
6009                            let final_getter = getter.or_else(|| existing.and_then(|e| e.get));
6010                            let final_setter = setter.or_else(|| existing.and_then(|e| e.set));
6011                            obj.define_accessor(atom, final_getter, final_setter);
6012                        } else if self.pending_throw.is_none() {
6013                            self.set_pending_type_error(ctx, "Invalid property key");
6014                        }
6015                    }
6016                }
6017
6018                Opcode::DefinePrivateAccessor => {
6019                    let obj_reg = self.read_u16_pc();
6020                    let key_reg = self.read_u16_pc();
6021                    let getter_reg = self.read_u16_pc();
6022                    let setter_reg = self.read_u16_pc();
6023                    let obj_val = self.get_reg(obj_reg);
6024                    let key_val = self.get_reg(key_reg);
6025                    if (obj_val.is_object() || obj_val.is_function()) && key_val.is_string() {
6026                        let atom = key_val.get_atom();
6027                        let obj = obj_val.as_object_mut();
6028                        let getter = if getter_reg != u16::MAX {
6029                            Some(self.get_reg(getter_reg))
6030                        } else {
6031                            None
6032                        };
6033                        let setter = if setter_reg != u16::MAX {
6034                            Some(self.get_reg(setter_reg))
6035                        } else {
6036                            None
6037                        };
6038                        obj.define_private_accessor(atom, getter, setter);
6039                    }
6040                }
6041
6042                Opcode::SetMethodProp => {
6043                    let obj_reg = self.read_u16_pc();
6044                    let key_reg = self.read_u16_pc();
6045                    let val_reg = self.read_u16_pc();
6046                    let obj_val = self.get_reg(obj_reg);
6047                    let key_val = self.get_reg(key_reg);
6048                    let value = self.get_reg(val_reg);
6049                    if obj_val.is_object_like() {
6050                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
6051                        let atom = if key_val.is_string() {
6052                            key_val.get_atom()
6053                        } else if key_val.is_symbol() {
6054                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id())
6055                        } else if key_val.is_float() {
6056                            let s = VM::js_to_string(&key_val, ctx);
6057                            s.get_atom()
6058                        } else {
6059                            self.int_atom(key_val.get_int() as usize, ctx)
6060                        };
6061                        js_obj.set_cached_non_enumerable(atom, value, ctx.shape_cache_mut());
6062                    }
6063                    self.set_reg(obj_reg, obj_val);
6064                }
6065
6066                Opcode::CallCurrent1 => {
6067                    unsafe {
6068                        (*self.frames.as_mut_ptr().add(self.frame_index)).current_pc = instr_pc;
6069                    }
6070                    let dst = self.read_u16_pc();
6071                    let arg_reg = self.read_u16_pc();
6072                    let caller = &self.frames[self.frame_index];
6073                    let function_ptr = caller.function_ptr;
6074                    if function_ptr.is_none() {
6075                        self.set_pending_type_error(ctx, "undefined is not a function");
6076                        if let Some(exc) = self.pending_throw.take() {
6077                            match self.dispatch_throw_value(ctx, exc) {
6078                                ThrowDispatch::Caught => continue,
6079                                ThrowDispatch::Uncaught(e) => return Err(e),
6080                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
6081                            }
6082                        }
6083                        continue;
6084                    }
6085                    let one_arg = [arg_reg];
6086                    self.push_frame_from_arg_regs_raw(
6087                        ctx,
6088                        caller.locals_count,
6089                        caller.bytecode_ptr,
6090                        caller.bytecode_len,
6091                        caller.constants_ptr,
6092                        caller.constants_len,
6093                        self.pc,
6094                        function_ptr,
6095                        caller.ic_table_ptr,
6096                        JSValue::undefined(),
6097                        dst,
6098                        1,
6099                        false,
6100                        caller.is_async,
6101                        self.cached_registers_base,
6102                        &one_arg,
6103                        caller.uses_arguments,
6104                    );
6105                    continue;
6106                }
6107
6108                Opcode::Call
6109                | Opcode::Call0
6110                | Opcode::Call1
6111                | Opcode::Call2
6112                | Opcode::Call3
6113                | Opcode::CallMethod
6114                | Opcode::CallNew
6115                | Opcode::CallCurrent => {
6116                    unsafe {
6117                        (*self.frames.as_mut_ptr().add(self.frame_index)).current_pc = instr_pc;
6118                    }
6119                    let is_call_current = op == Opcode::CallCurrent;
6120                    let is_call0 = op == Opcode::Call0;
6121                    let is_call1 = op == Opcode::Call1;
6122                    let is_call2 = op == Opcode::Call2;
6123                    let is_call3 = op == Opcode::Call3;
6124                    let is_call_method = op == Opcode::CallMethod;
6125                    let is_call_new = op == Opcode::CallNew;
6126                    let dst = self.read_u16_pc();
6127                    let obj_reg = if is_call_method {
6128                        self.read_u16_pc()
6129                    } else {
6130                        0
6131                    };
6132                    let func_reg = if is_call_current {
6133                        0
6134                    } else {
6135                        self.read_u16_pc()
6136                    };
6137                    let mut one_arg = [0u16; 1];
6138                    let mut two_args = [0u16; 2];
6139                    let mut three_args = [0u16; 3];
6140                    let argc = if is_call0 {
6141                        0
6142                    } else if is_call1 {
6143                        one_arg[0] = self.read_u16_pc();
6144                        1
6145                    } else if is_call2 {
6146                        two_args[0] = self.read_u16_pc();
6147                        two_args[1] = self.read_u16_pc();
6148                        2
6149                    } else if is_call3 {
6150                        three_args[0] = self.read_u16_pc();
6151                        three_args[1] = self.read_u16_pc();
6152                        three_args[2] = self.read_u16_pc();
6153                        3
6154                    } else {
6155                        self.read_u16_pc()
6156                    };
6157
6158                    let mut arg_regs_buf = [0u16; 16];
6159                    let mut arg_regs_vec = Vec::new();
6160                    let arg_regs: &[u16] = if is_call1 {
6161                        &one_arg
6162                    } else if is_call2 {
6163                        &two_args
6164                    } else if is_call3 {
6165                        &three_args
6166                    } else if argc as usize <= 16 {
6167                        for i in 0..argc {
6168                            arg_regs_buf[i as usize] = self.read_u16_pc();
6169                        }
6170                        &arg_regs_buf[..argc as usize]
6171                    } else {
6172                        arg_regs_vec.reserve(argc as usize);
6173                        for _ in 0..argc {
6174                            arg_regs_vec.push(self.read_u16_pc());
6175                        }
6176                        &arg_regs_vec
6177                    };
6178
6179                    if is_call_current {
6180                        let caller = &self.frames[self.frame_index];
6181                        let function_ptr = caller.function_ptr;
6182                        if function_ptr.is_none() {
6183                            self.set_pending_type_error(ctx, "undefined is not a function");
6184                            if let Some(exc) = self.pending_throw.take() {
6185                                match self.dispatch_throw_value(ctx, exc) {
6186                                    ThrowDispatch::Caught => continue,
6187                                    ThrowDispatch::Uncaught(e) => return Err(e),
6188                                    ThrowDispatch::AsyncComplete(o) => return Ok(o),
6189                                }
6190                            }
6191                            continue;
6192                        }
6193
6194                        self.push_frame_from_arg_regs_raw(
6195                            ctx,
6196                            caller.locals_count,
6197                            caller.bytecode_ptr,
6198                            caller.bytecode_len,
6199                            caller.constants_ptr,
6200                            caller.constants_len,
6201                            self.pc,
6202                            function_ptr,
6203                            caller.ic_table_ptr,
6204                            JSValue::undefined(),
6205                            dst,
6206                            argc,
6207                            false,
6208                            caller.is_async,
6209                            self.cached_registers_base,
6210                            arg_regs,
6211                            caller.uses_arguments,
6212                        );
6213                        continue;
6214                    }
6215
6216                    let func_val = self.get_reg(func_reg);
6217                    let this_val = if is_call_method {
6218                        self.get_reg(obj_reg)
6219                    } else if is_call_new {
6220                        let props = ctx.runtime_mut().gc_heap_mut().take_prop_vec();
6221                        let mut obj = crate::object::object::JSObject::new_typed_from_pool(
6222                            crate::object::object::ObjectType::Ordinary,
6223                            props,
6224                        );
6225                        obj.ensure_shape(ctx.shape_cache_mut());
6226                        if let Some(proto_ptr) = ctx.get_object_prototype() {
6227                            obj.prototype = Some(proto_ptr);
6228                        }
6229                        let func_val = if is_call_current {
6230                            if let Some(ptr) = self.frames[self.frame_index].function_ptr {
6231                                JSValue::new_function(ptr)
6232                            } else {
6233                                JSValue::undefined()
6234                            }
6235                        } else {
6236                            self.get_reg(func_reg)
6237                        };
6238                        if func_val.is_function() {
6239                            let js_func = func_val.as_function();
6240
6241                            if !js_func.cached_prototype_ptr.is_null() {
6242                                obj.prototype = Some(js_func.cached_prototype_ptr);
6243                            } else {
6244                                let proto_key = ctx.common_atoms.prototype;
6245                                if let Some(proto_val) = js_func.base.get(proto_key) {
6246                                    if proto_val.is_object_like() {
6247                                        let cptr = proto_val.get_ptr()
6248                                            as *mut crate::object::object::JSObject;
6249                                        func_val.as_function_mut().cached_prototype_ptr = cptr;
6250                                        obj.prototype = Some(cptr);
6251                                    }
6252                                } else if !js_func.is_builtin() {
6253                                    let mut pobj = crate::object::object::JSObject::new();
6254                                    pobj.set(ctx.common_atoms.constructor, func_val);
6255                                    if let Some(opp) = ctx.get_object_prototype() {
6256                                        pobj.prototype = Some(opp);
6257                                    }
6258                                    let pp = Box::into_raw(Box::new(pobj)) as usize;
6259                                    ctx.runtime_mut().gc_heap_mut().track(pp);
6260                                    let func_mut = func_val.as_function_mut();
6261                                    func_mut.base.set(proto_key, JSValue::new_object(pp));
6262                                    let cptr = pp as *mut crate::object::object::JSObject;
6263                                    func_mut.cached_prototype_ptr = cptr;
6264                                    obj.prototype = Some(cptr);
6265                                }
6266                            }
6267                        }
6268                        let ptr = if ctx.runtime().gc_heap().nursery_enabled_and_can_fit_object() {
6269                            ctx.runtime_mut()
6270                                .gc_heap_mut()
6271                                .alloc_object_with_value(obj)
6272                                .map(|p| p as usize)
6273                                .unwrap_or_else(|| {
6274                                    let hp = Box::into_raw(Box::new(
6275                                        crate::object::object::JSObject::new(),
6276                                    )) as usize;
6277                                    ctx.runtime_mut().gc_heap_mut().track(hp);
6278                                    hp
6279                                })
6280                        } else {
6281                            let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
6282                            ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
6283                            heap_ptr
6284                        };
6285                        self.allocation_count += 1;
6286                        JSValue::new_object(ptr)
6287                    } else {
6288                        let frame = &self.frames[self.frame_index];
6289                        if frame.is_constructor
6290                            && !frame.super_ctor.is_undefined()
6291                            && func_val.is_function()
6292                            && frame.super_ctor.is_function()
6293                        {
6294                            let sctor_ptr = frame.super_ctor.get_ptr();
6295                            let fptr = func_val.get_ptr();
6296                            if fptr == sctor_ptr {
6297                                frame.this_value
6298                            } else {
6299                                JSValue::undefined()
6300                            }
6301                        } else {
6302                            JSValue::undefined()
6303                        }
6304                    };
6305
6306                    if self.execute_call(
6307                        ctx,
6308                        func_val,
6309                        this_val,
6310                        dst,
6311                        argc,
6312                        arg_regs,
6313                        obj_reg,
6314                        is_call_new,
6315                        is_call_method,
6316                    )? {
6317                        continue;
6318                    }
6319                    if let Some(exc) = self.pending_throw.take() {
6320                        match self.dispatch_throw_value(ctx, exc) {
6321                            ThrowDispatch::Caught => continue,
6322                            ThrowDispatch::Uncaught(e) => return Err(e),
6323                            ThrowDispatch::AsyncComplete(o) => return Ok(o),
6324                        }
6325                    }
6326                }
6327
6328                Opcode::CallNamedMethod => {
6329                    unsafe {
6330                        (*self.frames.as_mut_ptr().add(self.frame_index)).current_pc = instr_pc;
6331                    }
6332                    let dst = self.read_u16_pc();
6333                    let obj_reg = self.read_u16_pc();
6334                    let atom = crate::runtime::atom::Atom(self.read_u32_pc());
6335                    let argc = self.read_u16_pc();
6336
6337                    let mut arg_regs_buf = [0u16; 16];
6338                    let mut arg_regs_vec = Vec::new();
6339                    let arg_regs: &[u16] = if argc as usize <= 16 {
6340                        for i in 0..argc {
6341                            arg_regs_buf[i as usize] = self.read_u16_pc();
6342                        }
6343                        &arg_regs_buf[..argc as usize]
6344                    } else {
6345                        arg_regs_vec.reserve(argc as usize);
6346                        for _ in 0..argc {
6347                            arg_regs_vec.push(self.read_u16_pc());
6348                        }
6349                        &arg_regs_vec
6350                    };
6351
6352                    let obj_val = self.get_reg(obj_reg);
6353                    if !self.get_named_prop_fast(ctx, obj_reg, obj_reg, atom, instr_pc) {}
6354                    let func_val = self.get_reg(obj_reg);
6355
6356                    self.set_reg(obj_reg, obj_val);
6357                    let this_val = obj_val;
6358
6359                    if self.execute_call(
6360                        ctx, func_val, this_val, dst, argc, arg_regs, obj_reg, false, true,
6361                    )? {
6362                        continue;
6363                    }
6364                    if let Some(exc) = self.pending_throw.take() {
6365                        match self.dispatch_throw_value(ctx, exc) {
6366                            ThrowDispatch::Caught => continue,
6367                            ThrowDispatch::Uncaught(e) => return Err(e),
6368                            ThrowDispatch::AsyncComplete(o) => return Ok(o),
6369                        }
6370                    }
6371                }
6372
6373                Opcode::CallSpread | Opcode::CallMethodSpread | Opcode::CallNewSpread => {
6374                    let is_call_method = op == Opcode::CallMethodSpread;
6375                    let is_call_new = op == Opcode::CallNewSpread;
6376                    let dst = self.read_u16_pc();
6377                    let obj_reg = if is_call_method {
6378                        self.read_u16_pc()
6379                    } else {
6380                        0
6381                    };
6382                    let func_reg = self.read_u16_pc();
6383                    let arr_reg = self.read_u16_pc();
6384
6385                    let func_val = self.get_reg(func_reg);
6386                    let arr_val = self.get_reg(arr_reg);
6387                    let mut args = Vec::new();
6388                    if arr_val.is_object() {
6389                        let arr_ptr = arr_val.get_ptr();
6390                        let is_jsarray = arr_val.as_object().is_dense_array();
6391                        if is_jsarray {
6392                            let arr = unsafe {
6393                                &*(arr_ptr as *const crate::object::array_obj::JSArrayObject)
6394                            };
6395                            for val in arr.elements.iter() {
6396                                args.push(*val);
6397                            }
6398                        } else {
6399                            let arr = arr_val.as_object();
6400                            let len_atom = ctx.common_atoms.length;
6401                            let arr_len =
6402                                arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
6403                            if let Some(elems) = arr.get_array_elements() {
6404                                for val in elems.iter() {
6405                                    args.push(*val);
6406                                }
6407                            } else {
6408                                for i in 0..arr_len {
6409                                    let key = ctx.intern(&i.to_string());
6410                                    if let Some(val) = arr.get(key) {
6411                                        args.push(val);
6412                                    }
6413                                }
6414                            }
6415                        }
6416                    }
6417                    let argc = args.len() as u16;
6418
6419                    let this_val = if is_call_method {
6420                        self.get_reg(obj_reg)
6421                    } else if is_call_new {
6422                        let props = ctx.runtime_mut().gc_heap_mut().take_prop_vec();
6423                        let mut obj = crate::object::object::JSObject::new_typed_from_pool(
6424                            crate::object::object::ObjectType::Ordinary,
6425                            props,
6426                        );
6427                        obj.ensure_shape(ctx.shape_cache_mut());
6428                        if let Some(proto_ptr) = ctx.get_object_prototype() {
6429                            obj.prototype = Some(proto_ptr);
6430                        }
6431                        if func_val.is_function() {
6432                            let js_func = func_val.as_function();
6433
6434                            if !js_func.cached_prototype_ptr.is_null() {
6435                                obj.prototype = Some(js_func.cached_prototype_ptr);
6436                            } else {
6437                                let proto_key = ctx.common_atoms.prototype;
6438                                if let Some(proto_val) = js_func.base.get(proto_key) {
6439                                    if proto_val.is_object_like() {
6440                                        let cptr = proto_val.get_ptr()
6441                                            as *mut crate::object::object::JSObject;
6442                                        func_val.as_function_mut().cached_prototype_ptr = cptr;
6443                                        obj.prototype = Some(cptr);
6444                                    }
6445                                } else if !js_func.is_builtin() {
6446                                    let mut pobj = crate::object::object::JSObject::new();
6447                                    pobj.set(ctx.common_atoms.constructor, func_val);
6448                                    if let Some(opp) = ctx.get_object_prototype() {
6449                                        pobj.prototype = Some(opp);
6450                                    }
6451                                    let pp = Box::into_raw(Box::new(pobj)) as usize;
6452                                    ctx.runtime_mut().gc_heap_mut().track(pp);
6453                                    let func_mut = func_val.as_function_mut();
6454                                    func_mut.base.set(proto_key, JSValue::new_object(pp));
6455                                    let cptr = pp as *mut crate::object::object::JSObject;
6456                                    func_mut.cached_prototype_ptr = cptr;
6457                                    obj.prototype = Some(cptr);
6458                                }
6459                            }
6460                        }
6461                        let ptr = if ctx.runtime().gc_heap().nursery_enabled_and_can_fit_object() {
6462                            ctx.runtime_mut()
6463                                .gc_heap_mut()
6464                                .alloc_object_with_value(obj)
6465                                .map(|p| p as usize)
6466                                .unwrap_or_else(|| {
6467                                    let hp = Box::into_raw(Box::new(
6468                                        crate::object::object::JSObject::new(),
6469                                    )) as usize;
6470                                    ctx.runtime_mut().gc_heap_mut().track(hp);
6471                                    hp
6472                                })
6473                        } else {
6474                            let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
6475                            ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
6476                            heap_ptr
6477                        };
6478                        self.allocation_count += 1;
6479                        JSValue::new_object(ptr)
6480                    } else {
6481                        let frame = &self.frames[self.frame_index];
6482                        if frame.is_constructor
6483                            && !frame.super_ctor.is_undefined()
6484                            && func_val.is_function()
6485                            && frame.super_ctor.is_function()
6486                        {
6487                            let sctor_ptr = frame.super_ctor.get_ptr();
6488                            let fptr = func_val.get_ptr();
6489                            if fptr == sctor_ptr {
6490                                frame.this_value
6491                            } else {
6492                                JSValue::undefined()
6493                            }
6494                        } else {
6495                            JSValue::undefined()
6496                        }
6497                    };
6498
6499                    if func_val.is_function() {
6500                        let ptr = func_val.get_ptr();
6501                        let js_func = func_val.as_function();
6502                        if let Some(ref rb) = js_func.bytecode {
6503                            if js_func.is_generator() {
6504                                let mut snapshot =
6505                                    vec![JSValue::undefined(); rb.locals_count as usize];
6506                                if !snapshot.is_empty() {
6507                                    snapshot[0] = this_val;
6508                                    for (i, arg) in args.iter().enumerate() {
6509                                        if i + 1 < snapshot.len() {
6510                                            snapshot[i + 1] = *arg;
6511                                        }
6512                                    }
6513                                }
6514                                let mut gen_obj = crate::object::object::JSObject::new();
6515                                gen_obj.set_is_generator(true);
6516                                if let Some(proto_ptr) = ctx.get_generator_prototype() {
6517                                    gen_obj.prototype = Some(proto_ptr);
6518                                }
6519                                gen_obj.set_generator_state(
6520                                    crate::object::object::GeneratorState {
6521                                        bytecode: Box::new(rb.clone()),
6522                                        snapshot,
6523                                        pc: 0,
6524                                        done: false,
6525                                    },
6526                                );
6527                                let gen_ptr = Box::into_raw(Box::new(gen_obj)) as usize;
6528                                ctx.runtime_mut().gc_heap_mut().track(gen_ptr);
6529                                self.set_reg(dst, JSValue::new_object(gen_ptr));
6530                                continue;
6531                            }
6532                            let return_pc = self.pc;
6533                            self.push_frame(
6534                                rb,
6535                                return_pc,
6536                                Some(ptr),
6537                                this_val,
6538                                dst,
6539                                argc,
6540                                is_call_new,
6541                                js_func.is_async(),
6542                                &args,
6543                                js_func.uses_arguments(),
6544                            );
6545                            if is_call_new {
6546                                let super_key = ctx.common_atoms.__super__;
6547                                if let Some(super_val) = js_func.base.get(super_key) {
6548                                    self.frames[self.frame_index].super_ctor = super_val;
6549                                }
6550                            }
6551                            continue;
6552                        } else if js_func.is_builtin() {
6553                            let caller_base = self.cached_registers_base;
6554                            let mut builtin_args = Vec::with_capacity(args.len() + 1);
6555                            if is_call_method {
6556                                builtin_args.push(self.registers[caller_base + obj_reg as usize]);
6557                            }
6558                            builtin_args.extend(args);
6559                            let result = if let Some(bf) = js_func.builtin_func {
6560                                ctx.call_builtin_direct(bf, &builtin_args)
6561                            } else if let Some(ba) = js_func.builtin_atom {
6562                                let name = ctx.get_atom_str(ba).to_string();
6563                                ctx.call_builtin(&name, &builtin_args)
6564                            } else {
6565                                JSValue::undefined()
6566                            };
6567                            self.set_reg(dst, result);
6568            }
6569        } else if func_val.is_object() && !is_call_new {
6570                        let result =
6571                            self.call_function_with_this(ctx, func_val, this_val, &args)?;
6572                        self.set_reg(dst, result);
6573                    } else {
6574                        let msg = format!(
6575                            "{} is not a function",
6576                            self.format_thrown_value(&func_val, ctx)
6577                        );
6578                        self.set_pending_type_error(ctx, &msg);
6579                        if let Some(exc) = self.pending_throw.take() {
6580                            match self.dispatch_throw_value(ctx, exc) {
6581                                ThrowDispatch::Caught => continue,
6582                                ThrowDispatch::Uncaught(e) => return Err(e),
6583                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
6584                            }
6585                        }
6586                    }
6587                }
6588
6589                Opcode::NewFunction
6590                | Opcode::NewGeneratorFunction
6591                | Opcode::NewAsyncFunction
6592                | Opcode::NewAsyncGeneratorFunction => {
6593                    let is_generator = op == Opcode::NewGeneratorFunction
6594                        || op == Opcode::NewAsyncGeneratorFunction;
6595                    let is_async =
6596                        op == Opcode::NewAsyncFunction || op == Opcode::NewAsyncGeneratorFunction;
6597
6598                    let newfunc_start_pc = self.pc;
6599
6600                    let cached: Option<std::sync::Arc<crate::compiler::opcode::NestedBytecode>> =
6601                        if let Some(parent_ptr) = self.frames[self.frame_index].function_ptr {
6602                            let parent_func = unsafe {
6603                                &*(parent_ptr as *const crate::object::function::JSFunction)
6604                            };
6605                            if let Some(bc) = parent_func.bytecode.as_ref() {
6606                                // First check the instance's own nested_bytecodes
6607                                let found = bc
6608                                    .nested_bytecodes
6609                                    .iter()
6610                                    .find(|(k, _)| *k == newfunc_start_pc as u32)
6611                                    .map(|(_, v)| v.clone());
6612                                if found.is_some() {
6613                                    found
6614                                } else if !bc.shared_nested_bytecodes_ptr.is_null() {
6615                                    // Fall back to the shared NestedBytecode's nested_bytecodes
6616                                    // (persists across re-creations of the parent closure)
6617                                    unsafe { &*(*bc.shared_nested_bytecodes_ptr).get() }
6618                                        .iter()
6619                                        .find(|(k, _)| *k == newfunc_start_pc as u32)
6620                                        .map(|(_, v)| v.clone())
6621                                } else {
6622                                    None
6623                                }
6624                            } else {
6625                                None
6626                            }
6627                        } else {
6628                            None
6629                        };
6630
6631                    let mut nb_for_ic: Option<
6632                        std::sync::Arc<crate::compiler::opcode::NestedBytecode>,
6633                    > = None;
6634
6635                    let (
6636                        fb_code,
6637                        fb_constants,
6638                        locals_count,
6639                        param_count,
6640                        uses_arguments,
6641                        is_strict,
6642                        func_var_name_to_slot,
6643                        upvalue_descs,
6644                        line_number_table_opt,
6645                        func_name_atom,
6646                    ) = if let Some(ref nb) = cached {
6647                        self.pc = newfunc_start_pc + nb.parent_bytecode_span as usize;
6648                        let uvdescs: Vec<(u32, u32)> = nb.upvalue_descs.clone();
6649
6650                        (
6651                            Vec::new(),
6652                            Vec::new(),
6653                            nb.locals_count,
6654                            nb.param_count,
6655                            nb.uses_arguments,
6656                            nb.is_strict,
6657                            nb.var_name_to_slot.clone(),
6658                            uvdescs,
6659                            nb.line_number_table.clone(),
6660                            nb.func_name_atom,
6661                        )
6662                    } else {
6663                        let param_count = self.read_u16_pc();
6664                        let uses_arguments = self.read_u8() != 0;
6665                        let is_strict = self.read_u8() != 0;
6666                        let locals_count = self.read_u16_pc() as u32;
6667                        let bytecode_len = self.read_u32();
6668                        let mut fb_code = Vec::with_capacity(bytecode_len as usize);
6669
6670                        unsafe {
6671                            let src = self.cached_code_ptr.add(self.pc);
6672                            fb_code.set_len(bytecode_len as usize);
6673                            std::ptr::copy_nonoverlapping(
6674                                src,
6675                                fb_code.as_mut_ptr(),
6676                                bytecode_len as usize,
6677                            );
6678                        }
6679                        self.pc += bytecode_len as usize;
6680                        let constants_len = self.read_u32();
6681                        let mut fb_constants = Vec::with_capacity(constants_len as usize);
6682                        for _ in 0..constants_len {
6683                            let const_type = self.read_u8();
6684                            match const_type {
6685                                0 => fb_constants.push(JSValue::undefined()),
6686                                1 => fb_constants.push(JSValue::null()),
6687                                2 => fb_constants.push(JSValue::bool(true)),
6688                                3 => fb_constants.push(JSValue::bool(false)),
6689                                4 => {
6690                                    let v = self.read_i64();
6691                                    fb_constants.push(JSValue::new_int(v));
6692                                }
6693                                5 => {
6694                                    let bytes = [
6695                                        self.read_u8(),
6696                                        self.read_u8(),
6697                                        self.read_u8(),
6698                                        self.read_u8(),
6699                                        self.read_u8(),
6700                                        self.read_u8(),
6701                                        self.read_u8(),
6702                                        self.read_u8(),
6703                                    ];
6704                                    let v = f64::from_le_bytes(bytes);
6705                                    fb_constants.push(JSValue::new_float(v));
6706                                }
6707                                6 => {
6708                                    let atom_id = self.read_u32() as u32;
6709                                    fb_constants.push(JSValue::new_string(
6710                                        crate::runtime::atom::Atom(atom_id),
6711                                    ));
6712                                }
6713                                _ => fb_constants.push(JSValue::undefined()),
6714                            }
6715                        }
6716                        let upvalue_count = self.read_u32();
6717                        let mut upvalue_descs = Vec::with_capacity(upvalue_count as usize);
6718                        for _ in 0..upvalue_count {
6719                            let atom_id = self.read_u32();
6720                            let local_idx = self.read_u32();
6721                            upvalue_descs.push((atom_id, local_idx));
6722                        }
6723
6724                        let line_table_entry_count = self.read_u32();
6725                        let mut line_table = crate::compiler::location::LineNumberTable::new();
6726                        for _ in 0..line_table_entry_count {
6727                            let off = self.read_u32();
6728                            let line = self.read_u32();
6729                            line_table.add_entry(off, line);
6730                        }
6731                        let line_number_table_opt = if line_table_entry_count > 0 {
6732                            Some(std::sync::Arc::new(line_table))
6733                        } else {
6734                            None
6735                        };
6736                        let func_name_atom = self.read_u32() as u32;
6737                        let var_count = self.read_u32();
6738                        let func_var_name_to_slot = std::rc::Rc::new({
6739                            let mut v = Vec::new();
6740                            for _ in 0..var_count {
6741                                let atom_id = self.read_u32();
6742                                let slot = self.read_u16_pc();
6743                                v.push((atom_id, slot));
6744                            }
6745                            v
6746                        });
6747
6748                        let parent_bytecode_span = (self.pc - newfunc_start_pc) as u32;
6749
6750                        if let Some(parent_ptr) = self.frames[self.frame_index].function_ptr {
6751                            let parent_func = unsafe {
6752                                &mut *(parent_ptr as *mut crate::object::function::JSFunction)
6753                            };
6754                            if let Some(bc) = parent_func.bytecode.as_mut() {
6755                                let key = newfunc_start_pc as u32;
6756                                // Check instance's own nested_bytecodes first
6757                                let found_nb = bc
6758                                    .nested_bytecodes
6759                                    .iter()
6760                                    .find(|(k, _)| *k == key)
6761                                    .map(|(_, v)| v.clone())
6762                                    .or_else(|| {
6763                                        if !bc.shared_nested_bytecodes_ptr.is_null() {
6764                                            // Check shared NestedBytecode's nested_bytecodes
6765                                            unsafe { &*(*bc.shared_nested_bytecodes_ptr).get() }
6766                                                .iter()
6767                                                .find(|(k, _)| *k == key)
6768                                                .map(|(_, v)| v.clone())
6769                                        } else {
6770                                            None
6771                                        }
6772                                    });
6773                                if let Some(existing) = found_nb {
6774                                    nb_for_ic = Some(existing.clone());
6775                                } else {
6776                                    // Create new NestedBytecode
6777                                    let nb = std::sync::Arc::new(
6778                                        crate::compiler::opcode::NestedBytecode {
6779                                            code: fb_code.clone(),
6780                                            constants: fb_constants.clone(),
6781                                            locals_count,
6782                                            param_count,
6783                                            uses_arguments,
6784                                            is_strict,
6785                                            var_name_to_slot: func_var_name_to_slot.clone(),
6786                                            line_number_table: line_number_table_opt.clone(),
6787                                            parent_bytecode_span,
6788                                            upvalue_count: upvalue_descs.len() as u32,
6789                                            upvalue_descs: upvalue_descs.clone(),
6790                                            func_name_atom,
6791                                            source_text: None,
6792                                            ic_table: std::cell::UnsafeCell::new(
6793                                                crate::compiler::InlineCacheTable::new(),
6794                                            ),
6795                                            nested_bytecodes: std::cell::UnsafeCell::new(Vec::new()),
6796                                        },
6797                                    );
6798                                    nb_for_ic = Some(nb.clone());
6799                                    // Store in instance's nested_bytecodes
6800                                    bc.nested_bytecodes.push((key, nb.clone()));
6801                                    // Also store in shared NestedBytecode's nested_bytecodes
6802                                    // so sub-sub-closures persist across re-creations of this closure
6803                                    if !bc.shared_nested_bytecodes_ptr.is_null() {
6804                                        let shared = unsafe {
6805                                            &mut *(*bc.shared_nested_bytecodes_ptr).get()
6806                                        };
6807                                        if !shared.iter().any(|(k, _)| *k == key) {
6808                                            shared.push((key, nb.clone()));
6809                                        }
6810                                    }
6811                                }
6812                            }
6813                        }
6814                        (
6815                            fb_code,
6816                            fb_constants,
6817                            locals_count,
6818                            param_count,
6819                            uses_arguments,
6820                            is_strict,
6821                            func_var_name_to_slot,
6822                            upvalue_descs,
6823                            line_number_table_opt,
6824                            func_name_atom,
6825                        )
6826                    };
6827
6828                    let mut func = crate::object::function::JSFunction::new();
6829
6830                    if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
6831                        func.base.prototype = Some(fn_proto_ptr);
6832                    }
6833                    func.param_count = param_count as u32;
6834                    func.arity = param_count as u32;
6835                    func.locals_count = locals_count;
6836                    func.set_is_generator(is_generator);
6837                    func.set_is_async(is_async);
6838                    func.set_uses_arguments(uses_arguments);
6839                    func.set_is_strict(is_strict);
6840                    func.line_number_table = line_number_table_opt.clone();
6841                    func.bytecode = Some(Bytecode {
6842                        code: fb_code,
6843                        constants: fb_constants,
6844                        locals_count,
6845                        param_count,
6846                        line_number_table: line_number_table_opt,
6847                        ic_table: crate::compiler::InlineCacheTable::new(),
6848                        shared_ic_table_ptr: std::ptr::null_mut(),
6849                        shared_nested_bytecodes_ptr: std::ptr::null_mut(),
6850                        shared_code_ptr: std::ptr::null(),
6851                        shared_code_len: 0,
6852                        shared_const_ptr: std::ptr::null(),
6853                        shared_const_len: 0,
6854                        uses_arguments,
6855                        is_strict,
6856                        var_name_to_slot: func_var_name_to_slot,
6857                        nested_bytecodes: Vec::new(),
6858                        is_simple_constructor: false,
6859                        simple_constructor_props: Vec::new(),
6860                        cached_constructor_final_shape: None,
6861                        cached_constructor_atoms: Vec::new(),
6862                    });
6863
6864                    let effective_nb = nb_for_ic.or_else(|| cached.clone());
6865                    if let Some(ref nb_arc) = effective_nb {
6866                        if let Some(bc) = func.bytecode.as_mut() {
6867                            bc.shared_ic_table_ptr = nb_arc.ic_table.get();
6868                            bc.shared_nested_bytecodes_ptr =
6869                                &nb_arc.nested_bytecodes as *const _ as *mut _;
6870
6871                            if bc.code.is_empty() {
6872                                bc.shared_code_ptr = nb_arc.code.as_ptr();
6873                                bc.shared_code_len = nb_arc.code.len();
6874                            }
6875                            if bc.constants.is_empty() {
6876                                bc.shared_const_ptr = nb_arc.constants.as_ptr();
6877                                bc.shared_const_len = nb_arc.constants.len();
6878                            }
6879                        }
6880                    }
6881                    func.shared_nb_for_ic = effective_nb;
6882
6883                    let inherited_sentinel = u16::MAX as usize;
6884                    let current_frame_base = self.frames[self.frame_index].registers_base;
6885                    if !upvalue_descs.is_empty() {
6886                        let n = upvalue_descs.len();
6887                        let uv = func.upvalues_mut();
6888                        uv.upvalue_slot_atoms.reserve(n);
6889                        uv.upvalue_slots.reserve(n);
6890                        uv.upvalue_cells.reserve(n);
6891                        uv.upvalue_local_indices.reserve(n);
6892                    }
6893                    for (atom_id, local_idx_raw) in &upvalue_descs {
6894                        let atom = crate::runtime::atom::Atom(*atom_id);
6895                        let local_idx = *local_idx_raw as usize;
6896                        func.upvalues_mut().upvalue_slot_atoms.push(atom);
6897
6898                        let cell = if local_idx != inherited_sentinel {
6899                            func.upvalues_mut()
6900                                .upvalue_local_indices
6901                                .insert(atom, local_idx);
6902                            let initial_value =
6903                                if local_idx < self.frames[self.frame_index].registers_count {
6904                                    self.registers[current_frame_base + local_idx]
6905                                } else {
6906                                    JSValue::undefined()
6907                                };
6908                            let current_frame = &mut self.frames[self.frame_index];
6909                            let local_idx_u16 = local_idx as u16;
6910                            if let Some(existing) = current_frame
6911                                .upvalue_sync_map
6912                                .as_ref()
6913                                .and_then(|m| m.get(&local_idx_u16))
6914                            {
6915                                existing.clone()
6916                            } else {
6917                                let new_cell =
6918                                    std::rc::Rc::new(std::cell::Cell::new(initial_value));
6919                                // Always create the sync map entry so that
6920                                // later assignments to the register also
6921                                // update the upvalue cell. This is needed
6922                                // for closures that capture let variables
6923                                // before initialization (function hoisting).
6924                                current_frame
6925                                    .upvalue_sync_map
6926                                    .get_or_insert_with(|| Box::new(FxHashMap::default()))
6927                                    .insert(local_idx_u16, new_cell.clone());
6928                                if local_idx_u16 < 64 {
6929                                    current_frame.upvalue_sync_bitset |= 1u64 << local_idx_u16;
6930                                    self.cached_upvalue_sync_bitset |= 1u64 << local_idx_u16;
6931                                }
6932                                self.cached_has_upvalue_sync = true;
6933                                new_cell
6934                            }
6935                        } else if let Some(parent_ptr) = self.frames[self.frame_index].function_ptr
6936                        {
6937                            let parent_func = unsafe {
6938                                &*(parent_ptr as *const crate::object::function::JSFunction)
6939                            };
6940
6941                            if let Some(parent_cell) = parent_func
6942                                .upvalues_ref()
6943                                .and_then(|u| u.upvalue_cells.get(&atom))
6944                            {
6945                                parent_cell.clone()
6946                            } else {
6947                                std::rc::Rc::new(std::cell::Cell::new(JSValue::undefined()))
6948                            }
6949                        } else {
6950                            std::rc::Rc::new(std::cell::Cell::new(JSValue::undefined()))
6951                        };
6952                        func.upvalues_mut().upvalue_slots.push(cell.clone());
6953                        func.upvalues_mut().upvalue_cells.insert(atom, cell);
6954                    }
6955
6956                    if func_name_atom != 0 {
6957                        let name_atom = crate::runtime::atom::Atom(func_name_atom);
6958                        func.name = name_atom;
6959                        let name_str = JSValue::new_string(name_atom);
6960                        func.base.set(ctx.common_atoms.name, name_str);
6961                    }
6962
6963                    {
6964                        let length_desc = crate::object::object::PropertyDescriptor {
6965                            value: Some(JSValue::new_int(param_count as i64)),
6966                            writable: false,
6967                            enumerable: false,
6968                            configurable: true,
6969                            get: None,
6970                            set: None,
6971                        };
6972                        func.base.define_property(ctx.common_atoms.length, length_desc);
6973                    }
6974
6975                    let is_closure = !upvalue_descs.is_empty();
6976
6977                    let proto_atom = ctx.common_atoms.prototype;
6978                    let func_ptr = Box::into_raw(Box::new(func)) as usize;
6979                    ctx.runtime_mut().gc_heap_mut().track_function(func_ptr);
6980                    let unboxed_func =
6981                        unsafe { &mut *(func_ptr as *mut crate::object::function::JSFunction) };
6982                    if !is_closure {
6983                        let mut proto_obj = crate::object::object::JSObject::new();
6984                        // Per spec, the prototype property of a function is
6985                        // a new object with Object.prototype as its prototype.
6986                        if let Some(obj_pp) = ctx.get_object_prototype() {
6987                            proto_obj.prototype = Some(obj_pp);
6988                        }
6989                        let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
6990                        ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
6991                        let func_value = JSValue::new_function(func_ptr);
6992                        unsafe {
6993                            let proto_obj_mut =
6994                                &mut *(proto_ptr as *mut crate::object::object::JSObject);
6995                            proto_obj_mut.set(ctx.common_atoms.constructor, func_value);
6996                        }
6997                        unboxed_func
6998                            .base
6999                            .set(proto_atom, JSValue::new_object(proto_ptr));
7000                        unboxed_func.cached_prototype_ptr =
7001                            proto_ptr as *mut crate::object::object::JSObject;
7002                    }
7003                    if let Some(fn_pp) = ctx.get_function_prototype() {
7004                        unboxed_func.base.prototype = Some(fn_pp);
7005                    } else if let Some(obj_pp) = ctx.get_object_prototype() {
7006                        unboxed_func.base.prototype = Some(obj_pp);
7007                    }
7008                    self.allocation_count += 1;
7009                    let dst_reg = self.read_u16_pc();
7010                    self.set_reg(dst_reg, JSValue::new_function(func_ptr));
7011                    self.maybe_gc(ctx);
7012                }
7013                Opcode::LoadTdz => {
7014                    let dst = self.read_u16_pc();
7015                    self.set_reg(dst, JSValue::new_tdz());
7016                }
7017                Opcode::CheckTdz => {
7018                    let reg = self.read_u16_pc();
7019                    if self.get_reg(reg).is_tdz() {
7020                        self.set_pending_reference_error(
7021                            ctx,
7022                            "Cannot access variable before initialization",
7023                        );
7024                        if let Some(exc) = self.pending_throw.take() {
7025                            match self.dispatch_throw_value(ctx, exc) {
7026                                ThrowDispatch::Caught => continue,
7027                                ThrowDispatch::Uncaught(e) => return Err(e),
7028                                ThrowDispatch::AsyncComplete(_) => continue,
7029                            }
7030                        }
7031                    }
7032                }
7033                Opcode::CheckRef => {
7034                    let reg = self.read_u16_pc();
7035                    let idx = self.read_u32() as usize;
7036
7037                    let reg_val = self.get_reg(reg);
7038                    if !reg_val.is_undefined()
7039                        && !reg_val.is_tdz()
7040                        && self.eval_binding_frames == 0
7041                        && self.caller_vm.is_none()
7042                    {
7043                        continue;
7044                    }
7045                    let name = if idx < self.frames[self.frame_index].constants_len {
7046                        unsafe { *self.cached_const_ptr.add(idx) }
7047                    } else {
7048                        JSValue::undefined()
7049                    };
7050                    let atom = name.get_atom();
7051                    let mut exists = false;
7052                    let has_eval = self.eval_binding_frames > 0 || self.caller_vm.is_some();
7053                    if has_eval {
7054                        for fi in (0..=self.frame_index).rev() {
7055                            if let Some(ref eb) = self.frames[fi].eval_bindings {
7056                                if eb.contains_key(&atom.0) {
7057                                    exists = true;
7058                                    break;
7059                                }
7060                            }
7061                        }
7062                        if !exists && self.get_var_in_caller_vm(atom.0).is_some() {
7063                            exists = true;
7064                        }
7065                    }
7066                    if !exists {
7067                        let global = ctx.global();
7068                        if global.is_object() {
7069                            exists = global.as_object().get_own(atom).is_some();
7070                        }
7071                    }
7072                    if !exists {
7073                        let ref_err_atom = ctx.intern("ReferenceError");
7074                        let name_str = ctx.get_atom_str(atom).to_string();
7075                        let err_msg = format!("{} is not defined", name_str);
7076                        let msg_atom = ctx.intern(&err_msg);
7077                        let mut err = crate::object::object::JSObject::new();
7078                        err.set(ctx.intern("name"), JSValue::new_string(ref_err_atom));
7079                        err.set(ctx.intern("message"), JSValue::new_string(msg_atom));
7080                        if let Some(proto) = ctx.get_reference_error_prototype() {
7081                            err.prototype = Some(proto as *mut _);
7082                        } else if let Some(proto) = ctx.get_error_prototype() {
7083                            err.prototype = Some(proto as *mut _);
7084                        }
7085                        let ptr = Box::into_raw(Box::new(err)) as usize;
7086                        ctx.runtime_mut().gc_heap_mut().track(ptr);
7087                        self.pending_throw = Some(JSValue::new_object(ptr));
7088                        if let Some(exc) = self.pending_throw.take() {
7089                            match self.dispatch_throw_value(ctx, exc) {
7090                                ThrowDispatch::Caught => continue,
7091                                ThrowDispatch::Uncaught(e) => return Err(e),
7092                                ThrowDispatch::AsyncComplete(_) => continue,
7093                            }
7094                        }
7095                    }
7096                }
7097                Opcode::CheckObjectCoercible => {
7098                    let reg = self.read_u16_pc();
7099                    let val = self.get_reg(reg);
7100                    if val.is_null() || val.is_undefined() {
7101                        let type_err_atom = ctx.intern("TypeError");
7102                        let msg = "Cannot destructure 'undefined' or 'null'.";
7103                        let msg_atom = ctx.intern(msg);
7104                        let mut err = crate::object::object::JSObject::new();
7105                        err.set(ctx.intern("name"), JSValue::new_string(type_err_atom));
7106                        err.set(ctx.intern("message"), JSValue::new_string(msg_atom));
7107                        if let Some(proto) = ctx.get_type_error_prototype() {
7108                            err.prototype = Some(proto as *mut _);
7109                        } else if let Some(proto) = ctx.get_error_prototype() {
7110                            err.prototype = Some(proto as *mut _);
7111                        }
7112                        let ptr = Box::into_raw(Box::new(err)) as usize;
7113                        ctx.runtime_mut().gc_heap_mut().track(ptr);
7114                        let exc = JSValue::new_object(ptr);
7115                        self.pending_throw = Some(exc);
7116                        if let Some(thrown) = self.pending_throw.take() {
7117                            match self.dispatch_throw_value(ctx, thrown) {
7118                                ThrowDispatch::Caught => continue,
7119                                ThrowDispatch::Uncaught(e) => {
7120                                    self.pending_throw = Some(exc);
7121                                    return Err(e);
7122                                }
7123                                ThrowDispatch::AsyncComplete(_) => continue,
7124                            }
7125                        }
7126                    }
7127                }
7128                Opcode::GetIterator => {
7129                    let dst = self.read_u16_pc();
7130                    let src = self.read_u16_pc();
7131                    let iterable = self.get_reg(src);
7132                    let result = if iterable.is_string() {
7133                        self.create_iter_object(ctx, iterable)
7134                    } else if iterable.is_object() || iterable.is_function() {
7135                        let is_array = iterable.as_object().is_dense_array();
7136                        if is_array {
7137                            self.create_iter_object(ctx, iterable)
7138                        } else {
7139                            let obj: &crate::object::object::JSObject = if iterable.is_function() {
7140                                let func = iterable.as_function();
7141                                &func.base
7142                            } else {
7143                                iterable.as_object()
7144                            };
7145
7146                            let mut iter_fn = None;
7147                            // Try to find Symbol.iterator via the well-known symbol
7148                            let sym_iter = crate::builtins::symbol::get_symbol_iterator(ctx);
7149                            if sym_iter.is_symbol() {
7150                                let sym_atom = crate::runtime::atom::Atom(
7151                                    0x40000000 | sym_iter.get_symbol_id(),
7152                                );
7153                                iter_fn = obj.get(sym_atom);
7154                                if iter_fn.is_none() {
7155                                    let mut current = obj.prototype;
7156                                    while let Some(p) = current {
7157                                        let pobj = unsafe { &*p };
7158                                        iter_fn = pobj.get(sym_atom);
7159                                        if iter_fn.is_some() {
7160                                            break;
7161                                        }
7162                                        current = pobj.prototype;
7163                                    }
7164                                }
7165                            }
7166
7167                            if let Some(fn_val) = iter_fn {
7168                                if fn_val.is_function() {
7169                                    match self.call_function_with_this(ctx, fn_val, iterable, &[]) {
7170                                        Ok(iterator) => {
7171                                            if iterator.is_object() {
7172                                                iterator
7173                                            } else {
7174                                                JSValue::undefined()
7175                                            }
7176                                        }
7177                                        Err(e) => return Err(e),
7178                                    }
7179                                } else {
7180                                    JSValue::undefined()
7181                                }
7182                            } else {
7183                                JSValue::undefined()
7184                            }
7185                        }
7186                    } else {
7187                        JSValue::undefined()
7188                    };
7189                    if result.is_undefined() {
7190                        let msg = format!(
7191                            "{} is not iterable",
7192                            self.format_thrown_value(&iterable, ctx)
7193                        );
7194                        self.set_pending_type_error(ctx, &msg);
7195                        if let Some(exc) = self.pending_throw.take() {
7196                            let disp = self.dispatch_throw_value(ctx, exc);
7197                            match disp {
7198                                ThrowDispatch::Caught => {}
7199                                ThrowDispatch::Uncaught(e) => return Err(e),
7200                                ThrowDispatch::AsyncComplete(o) => match o {
7201                                    ExecutionOutcome::Complete(_) => {}
7202                                    _ => return Err("get_iterator error".to_string()),
7203                                },
7204                            }
7205                        }
7206                    } else {
7207                        self.set_reg(dst, result);
7208                    }
7209                }
7210                Opcode::IteratorNext => {
7211                    let dst_val = self.read_u16_pc();
7212                    let dst_done = self.read_u16_pc();
7213                    let iter_reg = self.read_u16_pc();
7214                    let iter_val = self.get_reg(iter_reg);
7215
7216                    if !iter_val.is_object() {
7217                        self.set_reg(dst_val, JSValue::undefined());
7218                        self.set_reg(dst_done, JSValue::bool(true));
7219                        continue;
7220                    }
7221
7222                    let iter_ptr = iter_val.get_ptr();
7223                    let iter_obj =
7224                        unsafe { &mut *(iter_ptr as *mut crate::object::object::JSObject) };
7225                    let arr_atom = ctx.common_atoms.__iter_arr__;
7226
7227                    if let Some(arr_val) = iter_obj.get(arr_atom) {
7228                        let idx_atom = ctx.common_atoms.__iter_idx__;
7229                        let idx = iter_obj.get(idx_atom).map(|v| v.get_int()).unwrap_or(0) as usize;
7230
7231                        if arr_val.is_string() {
7232                            let atom = arr_val.get_atom();
7233                            let s = ctx.atom_table().get(atom);
7234                            let chars: Vec<char> = s.chars().collect();
7235                            if idx < chars.len() {
7236                                let ch = chars[idx].to_string();
7237                                let ch_atom = ctx.atom_table_mut().intern(&ch);
7238                                let ch_val = JSValue::new_string(ch_atom);
7239                                iter_obj.set_cached(
7240                                    idx_atom,
7241                                    JSValue::new_int((idx + 1) as i64),
7242                                    ctx.shape_cache_mut(),
7243                                );
7244                                self.set_reg(dst_val, ch_val);
7245                                self.set_reg(dst_done, JSValue::bool(false));
7246                            } else {
7247                                self.set_reg(dst_val, JSValue::undefined());
7248                                self.set_reg(dst_done, JSValue::bool(true));
7249                            }
7250                        } else if arr_val.is_object() {
7251                            let arr_ptr = arr_val.get_ptr();
7252                            let is_jsarray =
7253                                unsafe { &*(arr_ptr as *const crate::object::object::JSObject) }
7254                                    .is_dense_array();
7255                            let length = if is_jsarray {
7256                                let arr = unsafe {
7257                                    &*(arr_ptr as *const crate::object::array_obj::JSArrayObject)
7258                                };
7259                                arr.len()
7260                            } else {
7261                                let obj = unsafe {
7262                                    &*(arr_ptr as *const crate::object::object::JSObject)
7263                                };
7264                                let len_atom = ctx.common_atoms.length;
7265                                obj.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
7266                            };
7267
7268                            if idx < length {
7269                                let value = if is_jsarray {
7270                                    let arr = unsafe {
7271                                        &*(arr_ptr
7272                                            as *const crate::object::array_obj::JSArrayObject)
7273                                    };
7274                                    arr.get(idx).unwrap_or(JSValue::undefined())
7275                                } else {
7276                                    let obj = unsafe {
7277                                        &*(arr_ptr as *const crate::object::object::JSObject)
7278                                    };
7279                                    let key = self.int_atom(idx, ctx);
7280                                    obj.get(key).unwrap_or(JSValue::undefined())
7281                                };
7282                                iter_obj.set_cached(
7283                                    idx_atom,
7284                                    JSValue::new_int((idx + 1) as i64),
7285                                    ctx.shape_cache_mut(),
7286                                );
7287                                self.set_reg(dst_val, value);
7288                                self.set_reg(dst_done, JSValue::bool(false));
7289                            } else {
7290                                self.set_reg(dst_val, JSValue::undefined());
7291                                self.set_reg(dst_done, JSValue::bool(true));
7292                            }
7293                        } else {
7294                            self.set_reg(dst_val, JSValue::undefined());
7295                            self.set_reg(dst_done, JSValue::bool(true));
7296                        }
7297                    } else {
7298                        let next_atom = ctx.intern("next");
7299                        let next_fn = iter_obj.get(next_atom).or_else(|| {
7300                            let mut proto = iter_obj.prototype;
7301                            while let Some(p) = proto {
7302                                let pobj = unsafe { &*p };
7303                                if let Some(v) = pobj.get(next_atom) {
7304                                    return Some(v);
7305                                }
7306                                proto = pobj.prototype;
7307                            }
7308                            None
7309                        });
7310
7311                        if let Some(next_fn_val) = next_fn {
7312                            if next_fn_val.is_function() {
7313                                match self.call_function_with_this(ctx, next_fn_val, iter_val, &[])
7314                                {
7315                                    Ok(result) => {
7316                                        if result.is_object() {
7317                                            let result_obj = result.as_object();
7318                                            let value = result_obj
7319                                                .get(ctx.intern("value"))
7320                                                .unwrap_or(JSValue::undefined());
7321                                            let done = result_obj
7322                                                .get(ctx.intern("done"))
7323                                                .unwrap_or(JSValue::bool(false));
7324                                            self.set_reg(dst_val, value);
7325                                            self.set_reg(dst_done, done);
7326                                        } else {
7327                                            self.set_reg(dst_val, JSValue::undefined());
7328                                            self.set_reg(dst_done, JSValue::bool(true));
7329                                        }
7330                                    }
7331                                    Err(e) => {
7332                                        self.set_reg(dst_val, JSValue::undefined());
7333                                        self.set_reg(dst_done, JSValue::bool(true));
7334                                        return Err(e);
7335                                    }
7336                                }
7337                            } else {
7338                                self.set_reg(dst_val, JSValue::undefined());
7339                                self.set_reg(dst_done, JSValue::bool(true));
7340                            }
7341                        } else {
7342                            self.set_reg(dst_val, JSValue::undefined());
7343                            self.set_reg(dst_done, JSValue::bool(true));
7344                        }
7345                    }
7346                }
7347                Opcode::GetArguments => {
7348                    let dst = self.read_u16_pc();
7349                    let fi = self.frame_index;
7350                    if let Some(cached_ptr) = self.frames[fi].cached_arguments {
7351                        self.set_reg(dst, JSValue::new_object(cached_ptr));
7352                    } else {
7353                        let frame = &self.frames[fi];
7354                        let func_ptr = frame.function_ptr;
7355                        let saved_args = &frame.saved_args;
7356                        let arg_count = saved_args.len();
7357                        let is_strict = frame.is_strict_frame;
7358                        let use_mapped = !is_strict && func_ptr.is_some();
7359                        use crate::object::object::{
7360                            ATTR_CONFIGURABLE, ATTR_WRITABLE, PropertyDescriptor,
7361                        };
7362                        let mut args_obj = crate::object::object::JSObject::new();
7363                        if let Some(obj_proto) = ctx.get_object_prototype() {
7364                            args_obj.prototype = Some(obj_proto);
7365                        }
7366
7367                        let args_shape = ctx.get_or_create_args_length_shape();
7368                        args_obj.set_first_prop_with_shape(
7369                            ctx.common_atoms.length,
7370                            JSValue::new_int(arg_count as i64),
7371                            ATTR_WRITABLE | ATTR_CONFIGURABLE,
7372                            args_shape,
7373                        );
7374                        let declared_param_count = if use_mapped {
7375                            if let Some(ptr) = func_ptr {
7376                                let js_func = unsafe { JSValue::function_from_ptr(ptr) };
7377                                js_func.param_count
7378                            } else {
7379                                0
7380                            }
7381                        } else {
7382                            0
7383                        };
7384                        if use_mapped {
7385                            args_obj
7386                                .set_obj_type(crate::object::object::ObjectType::MappedArguments);
7387                            {
7388                                let extra = args_obj.ensure_extra();
7389                                extra.mapped_args_frame_index = fi;
7390                                extra.mapped_args_param_count = declared_param_count;
7391                            }
7392                            if arg_count > 0 {
7393                                args_obj
7394                                    .ensure_elements()
7395                                    .resize(arg_count, JSValue::undefined());
7396                                for (i, val) in saved_args.iter().enumerate() {
7397                                    args_obj.set_indexed(i, *val);
7398                                }
7399                            }
7400                        } else {
7401                            if arg_count > 0 {
7402                                args_obj
7403                                    .ensure_elements()
7404                                    .resize(arg_count, JSValue::undefined());
7405                                for (i, val) in saved_args.iter().enumerate() {
7406                                    args_obj.set_indexed(i, *val);
7407                                }
7408                            }
7409                        }
7410                        let callee_atom = ctx.common_atoms.callee;
7411                        if let Some(ptr) = func_ptr {
7412                            if is_strict {
7413                                let mut thrower = crate::object::function::JSFunction::new_builtin(
7414                                    ctx.intern("callee"),
7415                                    0,
7416                                );
7417                                thrower.builtin_atom = Some(ctx.intern("throw_type_error_callee"));
7418                                thrower.builtin_func =
7419                                    ctx.get_builtin_func("throw_type_error_callee");
7420                                let thrower_ptr = Box::into_raw(Box::new(thrower)) as usize;
7421                                ctx.runtime_mut().gc_heap_mut().track_function(thrower_ptr);
7422                                args_obj.define_accessor(
7423                                    callee_atom,
7424                                    Some(JSValue::new_function(thrower_ptr)),
7425                                    None,
7426                                );
7427                            } else {
7428                                args_obj.define_property(
7429                                    callee_atom,
7430                                    PropertyDescriptor {
7431                                        value: Some(JSValue::new_function(ptr)),
7432                                        writable: true,
7433                                        enumerable: false,
7434                                        configurable: true,
7435                                        get: None,
7436                                        set: None,
7437                                    },
7438                                );
7439                            }
7440                        }
7441
7442                        let ptr = if ctx
7443                            .runtime_mut()
7444                            .gc_heap_mut()
7445                            .nursery_enabled_and_can_fit_object()
7446                        {
7447                            ctx.runtime_mut()
7448                                .gc_heap_mut()
7449                                .alloc_object_with_value(args_obj)
7450                                .expect("nursery alloc failed despite can_fit")
7451                                as usize
7452                        } else {
7453                            let hp = Box::into_raw(Box::new(args_obj)) as usize;
7454                            ctx.runtime_mut().gc_heap_mut().track(hp);
7455                            hp
7456                        };
7457                        self.frames[fi].cached_arguments = Some(ptr);
7458                        self.set_reg(dst, JSValue::new_object(ptr));
7459                        self.allocation_count += 1;
7460                        self.maybe_gc(ctx);
7461                    }
7462                }
7463                Opcode::GetCurrentFunction => {
7464                    let dst = self.read_u16_pc();
7465                    let value = if let Some(ptr) = self.frames[self.frame_index].function_ptr {
7466                        JSValue::new_function(ptr)
7467                    } else {
7468                        JSValue::undefined()
7469                    };
7470                    self.set_reg(dst, value);
7471                }
7472            }
7473        }
7474    }
7475
7476    pub fn execute_generator_step(
7477        &mut self,
7478        ctx: &mut JSContext,
7479        bytecode: &Bytecode,
7480        registers_snapshot: &[JSValue],
7481        start_pc: usize,
7482    ) -> Result<(JSValue, Vec<JSValue>, usize, bool), String> {
7483        self.ctx_ptr = ctx;
7484
7485        let needed = bytecode.locals_count as usize;
7486        if needed > self.registers.len() {
7487            self.registers.resize(needed, JSValue::undefined());
7488        }
7489        for (i, val) in registers_snapshot.iter().enumerate() {
7490            if i < self.registers.len() {
7491                self.registers[i] = *val;
7492            }
7493        }
7494        for i in registers_snapshot.len()..needed {
7495            if i < self.registers.len() {
7496                self.registers[i] = JSValue::undefined();
7497            }
7498        }
7499
7500        let result = self.execute_inner(ctx, bytecode, true, start_pc, true)?;
7501        match result {
7502            ExecutionOutcome::Complete(val) => Ok((val, self.registers.clone(), self.pc, true)),
7503            ExecutionOutcome::Yield(val) => Ok((val, self.registers.clone(), self.pc, false)),
7504        }
7505    }
7506
7507    #[inline(always)]
7508    fn read_u16_pc(&mut self) -> u16 {
7509        let val =
7510            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const u16) };
7511        self.pc += 2;
7512        val
7513    }
7514
7515    #[inline(always)]
7516    fn read_u32_pc(&mut self) -> u32 {
7517        let val =
7518            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const u32) };
7519        self.pc += 4;
7520        val
7521    }
7522
7523    #[inline(always)]
7524    fn read_i32_pc(&mut self) -> i32 {
7525        let val =
7526            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const i32) };
7527        self.pc += 4;
7528        val
7529    }
7530
7531    #[inline(always)]
7532    fn get_named_prop_result(
7533        &mut self,
7534        ctx: &mut JSContext,
7535        dst: u16,
7536        obj_val: JSValue,
7537        atom: crate::runtime::atom::Atom,
7538        ic_pc: usize,
7539    ) -> Option<JSValue> {
7540        if !obj_val.is_object_like()
7541            && !obj_val.is_function()
7542            && (obj_val.is_int()
7543                || obj_val.is_float()
7544                || obj_val.is_string()
7545                || obj_val.is_bool()
7546                || obj_val.is_symbol())
7547        {
7548            if obj_val.is_string() && atom == ctx.common_atoms.length {
7549                let len = ctx.string_char_count(obj_val.get_atom()) as i64;
7550                let v = JSValue::new_int(len);
7551                self.set_reg(dst, v);
7552                return Some(v);
7553            }
7554            let start_proto = if obj_val.is_string() {
7555                ctx.get_string_prototype()
7556            } else if obj_val.is_int() || obj_val.is_float() {
7557                ctx.get_number_prototype()
7558            } else if obj_val.is_symbol() {
7559                ctx.get_symbol_prototype()
7560            } else {
7561                ctx.get_object_prototype()
7562            };
7563            let mut current = start_proto;
7564            while let Some(ptr) = current {
7565                let pobj = unsafe { &*ptr };
7566                if let Some(getter) = pobj.get_own_accessor_value(atom) {
7567                    if getter.is_function() {
7568                        let js_func = getter.as_function();
7569                        let fn_this = if !js_func.is_strict()
7570                            && (obj_val.is_int()
7571                                || obj_val.is_float()
7572                                || obj_val.is_string()
7573                                || obj_val.is_bool())
7574                        {
7575                            let mut wrapper = crate::object::object::JSObject::new();
7576                            if let Some(opp) = ctx.get_object_prototype() {
7577                                wrapper.set_prototype_raw(opp);
7578                            }
7579                            if obj_val.is_string() || obj_val.is_int() || obj_val.is_float() {
7580                                wrapper.set_cached(
7581                                    crate::runtime::atom::Atom(0),
7582                                    obj_val,
7583                                    ctx.shape_cache_mut(),
7584                                );
7585                            } else if obj_val.is_symbol() {
7586                                if let Some(sp) = ctx.get_symbol_prototype() {
7587                                    wrapper.set_prototype_raw(sp);
7588                                }
7589                                wrapper.set_cached(
7590                                    crate::runtime::atom::Atom(0),
7591                                    obj_val,
7592                                    ctx.shape_cache_mut(),
7593                                );
7594                            }
7595                            JSValue::new_object(Box::into_raw(Box::new(wrapper)) as usize)
7596                        } else {
7597                            obj_val
7598                        };
7599                        if let Ok(v) = self.call_function_with_this(ctx, getter, fn_this, &[]) {
7600                            self.set_reg(dst, v);
7601                            return Some(v);
7602                        }
7603                    }
7604                    self.set_reg(dst, JSValue::undefined());
7605                    return Some(JSValue::undefined());
7606                }
7607                if let Some(val) = pobj.get_own(atom) {
7608                    self.set_reg(dst, val);
7609
7610                    let ic_table_ptr = self.cached_ic_table_ptr;
7611                    if !ic_table_ptr.is_null() {
7612                        let pc = ic_pc;
7613                        let pseudo_id = if obj_val.is_string() {
7614                            PRIM_STRING_SHAPE_ID
7615                        } else {
7616                            PRIM_NUMBER_SHAPE_ID
7617                        };
7618                        if let Some(offset) = pobj.find_offset(atom) {
7619                            unsafe {
7620                                (*ic_table_ptr).ensure_capacity(pc + 1);
7621                                if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
7622                                    ic.insert(
7623                                        crate::object::shape::ShapeId(pseudo_id),
7624                                        offset as u32,
7625                                        Some(ptr as usize),
7626                                    );
7627                                }
7628                            }
7629                        }
7630                    }
7631                    return Some(val);
7632                }
7633                current = pobj.prototype;
7634            }
7635            self.set_reg(dst, JSValue::undefined());
7636            return Some(JSValue::undefined());
7637        }
7638        if obj_val.is_object_like() {
7639            let ptr = obj_val.get_ptr();
7640            let js_obj = unsafe { JSValue::object_from_ptr(ptr) };
7641            let ic_table_ptr = self.cached_ic_table_ptr;
7642
7643            let pc = ic_pc;
7644            let mut ic_hit = false;
7645            if let Some(shape_id) = js_obj.get_shape_id() {
7646                if !ic_table_ptr.is_null() {
7647                    let ic_table = unsafe { &*ic_table_ptr };
7648                    if let Some(ic) = ic_table.get(pc) {
7649                        if let Some((offset, proto_ptr)) = ic.get(shape_id) {
7650                            if offset == u32::MAX && proto_ptr.is_none() {
7651                                self.set_reg(dst, JSValue::undefined());
7652                                ic_hit = true;
7653                            } else {
7654                                let val = if let Some(proto) = proto_ptr {
7655                                    let mut proto_matches = js_obj
7656                                        .prototype
7657                                        .map(|p| !p.is_null() && p as usize == proto)
7658                                        .unwrap_or(false);
7659                                    if !proto_matches {
7660                                        let mut cur = js_obj.prototype;
7661                                        let mut depth = 0u32;
7662                                        while let Some(p) = cur {
7663                                            if p.is_null() || depth > 1000 {
7664                                                break;
7665                                            }
7666                                            if p as usize == proto {
7667                                                proto_matches = true;
7668                                                break;
7669                                            }
7670                                            depth += 1;
7671                                            unsafe {
7672                                                cur = (*p).prototype;
7673                                            }
7674                                        }
7675                                    }
7676                                    if proto_matches {
7677                                        let proto_obj = unsafe {
7678                                            &*(proto as *const crate::object::object::JSObject)
7679                                        };
7680                                        proto_obj.get_by_offset(offset as usize)
7681                                    } else {
7682                                        None
7683                                    }
7684                                } else {
7685                                    js_obj.get_by_offset(offset as usize)
7686                                };
7687                                if let Some(v) = val {
7688                                    self.set_reg(dst, v);
7689                                    ic_hit = true;
7690                                }
7691                            }
7692                        }
7693                    }
7694                }
7695            }
7696
7697            if !ic_hit {
7698                if let Some(shape_id) = js_obj.get_shape_id() {
7699                    if let Some(offset) = js_obj.find_offset(atom) {
7700                        let v = js_obj.get_by_offset(offset).unwrap_or(JSValue::undefined());
7701                        self.set_reg(dst, v);
7702                        if !ic_table_ptr.is_null() {
7703                            unsafe {
7704                                (*ic_table_ptr).ensure_capacity(pc + 1);
7705                                if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
7706                                    ic.insert(shape_id, offset as u32, None);
7707                                }
7708                            }
7709                        }
7710                    } else {
7711                        if let Some(getter) = js_obj.get_own_accessor_value(atom) {
7712                            if getter.is_function() {
7713                                match self.call_function_with_this(ctx, getter, obj_val, &[]) {
7714                                    Ok(ret) => {
7715                                        self.set_reg(dst, ret);
7716                                    }
7717                                    Err(msg) => {
7718                                        self.rethrow_last_caught();
7719                                        return None;
7720                                    }
7721                                }
7722                            } else {
7723                                self.set_pending_type_error(
7724                                    ctx,
7725                                    "Property getter is not a function",
7726                                );
7727                            }
7728                        } else {
7729                            let mut current = js_obj.prototype;
7730                            let mut depth = 0u32;
7731                            let mut found = false;
7732                            while let Some(proto_ptr) = current {
7733                                if proto_ptr.is_null() || depth > 1000 {
7734                                    break;
7735                                }
7736                                depth += 1;
7737                                unsafe {
7738                                    let proto = &*proto_ptr;
7739                                    if let Some(offset) = proto.find_offset(atom) {
7740                                        let v = proto
7741                                            .get_by_offset(offset)
7742                                            .unwrap_or(JSValue::undefined());
7743                                        self.set_reg(dst, v);
7744                                        if !ic_table_ptr.is_null() {
7745                                            (*ic_table_ptr).ensure_capacity(pc + 1);
7746                                            if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
7747                                                ic.insert(
7748                                                    shape_id,
7749                                                    offset as u32,
7750                                                    Some(proto_ptr as usize),
7751                                                );
7752                                            }
7753                                        }
7754                                        found = true;
7755                                        break;
7756                                    }
7757                                    if let Some(getter) = proto.get_own_accessor_value(atom) {
7758                                        if getter.is_function() {
7759                                            match self.call_function_with_this(
7760                                                ctx,
7761                                                getter,
7762                                                obj_val,
7763                                                &[],
7764                                            ) {
7765                                                Ok(ret) => {
7766                                                    self.set_reg(dst, ret);
7767                                                }
7768                                                Err(_msg) => {
7769                                                    self.rethrow_last_caught();
7770                                                    return None;
7771                                                }
7772                                            }
7773                                        } else {
7774                                            self.set_pending_type_error(
7775                                                ctx,
7776                                                "Property getter is not a function",
7777                                            );
7778                                        }
7779                                        found = true;
7780                                        break;
7781                                    }
7782                                    current = proto.prototype;
7783                                }
7784                            }
7785                            if !found && obj_val.is_function() && js_obj.prototype.is_none() {
7786                                if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
7787                                    let fn_proto = unsafe { &*fn_proto_ptr };
7788                                    if let Some(offset) = fn_proto.find_offset(atom) {
7789                                        let v = fn_proto
7790                                            .get_by_offset(offset)
7791                                            .unwrap_or(JSValue::undefined());
7792                                        self.set_reg(dst, v);
7793                                        found = true;
7794                                    } else if let Some(getter) =
7795                                        fn_proto.get_own_accessor_value(atom)
7796                                    {
7797                                        if getter.is_function() {
7798                                            match self.call_function_with_this(
7799                                                ctx,
7800                                                getter,
7801                                                obj_val,
7802                                                &[],
7803                                            ) {
7804                                                Ok(ret) => {
7805                                                    self.set_reg(dst, ret);
7806                                                }
7807                                                Err(msg) => {
7808                                                    let mut err =
7809                                                        crate::object::object::JSObject::new();
7810                                                    err.set(
7811                                                        ctx.intern("name"),
7812                                                        JSValue::new_string(
7813                                                            ctx.intern("ReferenceError"),
7814                                                        ),
7815                                                    );
7816                                                    err.set(
7817                                                        ctx.intern("message"),
7818                                                        JSValue::new_string(ctx.intern(&msg)),
7819                                                    );
7820                                                    if let Some(proto) =
7821                                                        ctx.get_reference_error_prototype()
7822                                                    {
7823                                                        err.prototype = Some(proto);
7824                                                    }
7825                                                    let ptr = Box::into_raw(Box::new(err)) as usize;
7826                                                    ctx.runtime_mut().gc_heap_mut().track(ptr);
7827                                                    self.pending_throw =
7828                                                        Some(JSValue::new_object(ptr));
7829                                                    return None;
7830                                                }
7831                                            }
7832                                        } else {
7833                                            self.set_pending_type_error(
7834                                                ctx,
7835                                                "Property getter is not a function",
7836                                            );
7837                                        }
7838                                        found = true;
7839                                    }
7840                                }
7841                            }
7842                            if !found {
7843                                self.set_reg(dst, JSValue::undefined());
7844                                if !ic_table_ptr.is_null() {
7845                                    unsafe {
7846                                        (*ic_table_ptr).ensure_capacity(pc + 1);
7847                                        if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
7848                                            ic.insert(shape_id, u32::MAX, None);
7849                                        }
7850                                    }
7851                                }
7852                            }
7853                        }
7854                    }
7855                } else {
7856                    let mut value = JSValue::undefined();
7857                    let mut found = false;
7858
7859                    if let Some(offset) = js_obj.find_offset(atom) {
7860                        value = js_obj.get_by_offset(offset).unwrap_or(JSValue::undefined());
7861                        found = true;
7862                    } else if let Some(getter) = js_obj.get_own_accessor_value(atom) {
7863                        value = if getter.is_function() {
7864                            match self.call_function_with_this(ctx, getter, obj_val, &[]) {
7865                                Ok(ret) => ret,
7866                                Err(msg) => {
7867                                    self.set_pending_type_error(ctx, &msg);
7868                                    JSValue::undefined()
7869                                }
7870                            }
7871                        } else {
7872                            self.set_pending_type_error(ctx, "Property getter is not a function");
7873                            JSValue::undefined()
7874                        };
7875                        found = true;
7876                    } else {
7877                        let mut current = js_obj.prototype;
7878                        let mut depth = 0u32;
7879                        while let Some(proto_ptr) = current {
7880                            if proto_ptr.is_null() || depth > 1000 {
7881                                break;
7882                            }
7883                            depth += 1;
7884                            unsafe {
7885                                let proto = &*proto_ptr;
7886                                if let Some(offset) = proto.find_offset(atom) {
7887                                    value =
7888                                        proto.get_by_offset(offset).unwrap_or(JSValue::undefined());
7889                                    found = true;
7890                                    break;
7891                                }
7892                                if let Some(getter) = proto.get_own_accessor_value(atom) {
7893                                    value = if getter.is_function() {
7894                                        match self.call_function_with_this(
7895                                            ctx,
7896                                            getter,
7897                                            obj_val,
7898                                            &[],
7899                                        ) {
7900                                            Ok(ret) => ret,
7901                                            Err(msg) => {
7902                                                self.set_pending_type_error(ctx, &msg);
7903                                                JSValue::undefined()
7904                                            }
7905                                        }
7906                                    } else {
7907                                        self.set_pending_type_error(
7908                                            ctx,
7909                                            "Property getter is not a function",
7910                                        );
7911                                        JSValue::undefined()
7912                                    };
7913                                    found = true;
7914                                    break;
7915                                }
7916                                current = proto.prototype;
7917                            }
7918                        }
7919                    }
7920
7921                    if !found && obj_val.is_function() && js_obj.prototype.is_none() {
7922                        if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
7923                            let fn_proto = unsafe { &*fn_proto_ptr };
7924                            if let Some(offset) = fn_proto.find_offset(atom) {
7925                                value = fn_proto
7926                                    .get_by_offset(offset)
7927                                    .unwrap_or(JSValue::undefined());
7928                                found = true;
7929                            } else if let Some(getter) = fn_proto.get_own_accessor_value(atom) {
7930                                value = if getter.is_function() {
7931                                    match self.call_function_with_this(ctx, getter, obj_val, &[]) {
7932                                        Ok(ret) => ret,
7933                                        Err(msg) => {
7934                                            self.set_pending_type_error(ctx, &msg);
7935                                            JSValue::undefined()
7936                                        }
7937                                    }
7938                                } else {
7939                                    self.set_pending_type_error(
7940                                        ctx,
7941                                        "Property getter is not a function",
7942                                    );
7943                                    JSValue::undefined()
7944                                };
7945                                found = true;
7946                            }
7947                        }
7948                    }
7949
7950                    if found {
7951                        self.set_reg(dst, value);
7952                    } else {
7953                        self.set_reg(dst, JSValue::undefined());
7954                    }
7955                }
7956            }
7957
7958            None
7959        } else if obj_val.is_string() {
7960            if atom == ctx.common_atoms.length {
7961                Some(JSValue::new_int(
7962                    ctx.string_char_count(obj_val.get_atom()) as i64
7963                ))
7964            } else if let Some(proto_ptr) = ctx.get_string_prototype() {
7965                let proto_obj = unsafe { &*proto_ptr };
7966                Some(proto_obj.get(atom).unwrap_or(JSValue::undefined()))
7967            } else {
7968                Some(JSValue::undefined())
7969            }
7970        } else if obj_val.is_int() || obj_val.is_float() || obj_val.is_bool() {
7971            if let Some(proto_ptr) = ctx.get_number_prototype() {
7972                let proto_obj = unsafe { &*proto_ptr };
7973                Some(proto_obj.get(atom).unwrap_or(JSValue::undefined()))
7974            } else {
7975                Some(JSValue::undefined())
7976            }
7977        } else if obj_val.is_symbol() {
7978            let prop_str = ctx.get_atom_str(atom);
7979            if prop_str == "description" {
7980                let desc_atom = obj_val.get_atom();
7981                if desc_atom.0 == ctx.common_atoms.empty.0 {
7982                    Some(JSValue::undefined())
7983                } else {
7984                    Some(JSValue::new_string(desc_atom))
7985                }
7986            } else if let Some(proto_ptr) = ctx.get_symbol_prototype() {
7987                let proto = unsafe { &*proto_ptr };
7988                Some(proto.get(atom).unwrap_or(JSValue::undefined()))
7989            } else {
7990                Some(JSValue::undefined())
7991            }
7992        } else {
7993            Some(JSValue::undefined())
7994        }
7995    }
7996
7997    #[inline(always)]
7998    fn get_named_prop_fast(
7999        &mut self,
8000        ctx: &mut JSContext,
8001        dst: u16,
8002        obj_reg: u16,
8003        atom: crate::runtime::atom::Atom,
8004        instr_pc: usize,
8005    ) -> bool {
8006        let obj_val = self.get_reg(obj_reg);
8007
8008        if obj_val.is_object_like() {
8009            let js_obj = unsafe { crate::value::JSValue::object_from_ptr(obj_val.get_ptr()) };
8010
8011            if js_obj.is_dense_array() && atom == ctx.common_atoms.length {
8012                if js_obj.props_len() > 0 {
8013                    self.set_reg(dst, js_obj.get_by_offset_fast(0));
8014                } else {
8015                    self.set_reg(dst, crate::value::JSValue::new_int(0));
8016                }
8017                return true;
8018            }
8019            let shape_id = js_obj.shape_id_cache;
8020            if shape_id != usize::MAX {
8021                let ic_table_ptr = self.cached_ic_table_ptr;
8022                if !ic_table_ptr.is_null() {
8023                    let (ic_hit, r0_offset, r0_proto) =
8024                        unsafe { (*ic_table_ptr).get_reads0_values(instr_pc, shape_id) };
8025                    if ic_hit {
8026                        if r0_proto == 0 {
8027                            let val = if r0_offset == u32::MAX {
8028                                crate::value::JSValue::undefined()
8029                            } else {
8030                                let off = r0_offset as usize;
8031
8032                                if off < crate::object::object::INLINE_PROPS
8033                                    && js_obj.has_no_deleted_props()
8034                                {
8035                                    js_obj.get_by_offset_fast(off)
8036                                } else if let Some(v) = js_obj.get_by_offset(off) {
8037                                    v
8038                                } else {
8039                                    return self
8040                                        .get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc);
8041                                }
8042                            };
8043                            self.set_reg(dst, val);
8044                            return true;
8045                        } else {
8046                            if let Some(proto_raw) = js_obj.prototype {
8047                                if proto_raw as usize == r0_proto {
8048                                    let proto_obj = unsafe { &*proto_raw };
8049                                    let off = r0_offset as usize;
8050                                    let v = if off < crate::object::object::INLINE_PROPS
8051                                        && proto_obj.has_no_deleted_props()
8052                                    {
8053                                        Some(proto_obj.get_by_offset_fast(off))
8054                                    } else {
8055                                        proto_obj.get_by_offset(off)
8056                                    };
8057                                    if let Some(v) = v {
8058                                        self.set_reg(dst, v);
8059                                        return true;
8060                                    }
8061                                    return self
8062                                        .get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc);
8063                                }
8064                            }
8065                            return self.get_inherited_fast(
8066                                ctx, dst, obj_val, atom, instr_pc, r0_offset, r0_proto,
8067                            );
8068                        }
8069                    } else {
8070                        return self
8071                            .get_named_prop_poly_hit(ctx, dst, obj_val, atom, instr_pc, shape_id);
8072                    }
8073                }
8074            }
8075        }
8076        self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc)
8077    }
8078
8079    #[inline(never)]
8080    fn get_named_prop_poly_hit(
8081        &mut self,
8082        ctx: &mut JSContext,
8083        dst: u16,
8084        obj_val: JSValue,
8085        atom: crate::runtime::atom::Atom,
8086        instr_pc: usize,
8087        shape_id: usize,
8088    ) -> bool {
8089        let ic_table_ptr = self.cached_ic_table_ptr;
8090        if !ic_table_ptr.is_null() {
8091            let (ic_hit, r_offset, r_proto) =
8092                unsafe { (*ic_table_ptr).get_reads123_values(instr_pc, shape_id) };
8093            if ic_hit {
8094                let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
8095                if r_proto == 0 {
8096                    let val = if r_offset == u32::MAX {
8097                        JSValue::undefined()
8098                    } else {
8099                        let off = r_offset as usize;
8100                        if off < crate::object::object::INLINE_PROPS
8101                            && js_obj.has_no_deleted_props()
8102                        {
8103                            js_obj.get_by_offset_fast(off)
8104                        } else if let Some(v) = js_obj.get_by_offset(off) {
8105                            v
8106                        } else {
8107                            return self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc);
8108                        }
8109                    };
8110                    self.set_reg(dst, val);
8111                    return true;
8112                } else {
8113                    if let Some(p) = js_obj.prototype {
8114                        if p as usize == r_proto {
8115                            let proto_obj = unsafe { &*p };
8116                            let off = r_offset as usize;
8117                            let v = if off < crate::object::object::INLINE_PROPS
8118                                && proto_obj.has_no_deleted_props()
8119                            {
8120                                Some(proto_obj.get_by_offset_fast(off))
8121                            } else {
8122                                proto_obj.get_by_offset(off)
8123                            };
8124                            if let Some(v) = v {
8125                                self.set_reg(dst, v);
8126                                return true;
8127                            }
8128                        }
8129                    }
8130
8131                    return self
8132                        .get_inherited_fast(ctx, dst, obj_val, atom, instr_pc, r_offset, r_proto);
8133                }
8134            }
8135        }
8136        self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc)
8137    }
8138
8139    #[inline(never)]
8140    fn get_inherited_fast(
8141        &mut self,
8142        ctx: &mut JSContext,
8143        dst: u16,
8144        obj_val: crate::value::JSValue,
8145        atom: crate::runtime::atom::Atom,
8146        instr_pc: usize,
8147        offset: u32,
8148        proto_ptr: usize,
8149    ) -> bool {
8150        let js_obj = unsafe { crate::value::JSValue::object_from_ptr(obj_val.get_ptr()) };
8151        let mut cur = js_obj.prototype;
8152        let mut depth = 0u32;
8153        while let Some(p) = cur {
8154            if depth > 1000 {
8155                break;
8156            }
8157            if p as usize == proto_ptr {
8158                let proto_obj = unsafe { &*p };
8159                if let Some(v) = proto_obj.get_by_offset(offset as usize) {
8160                    self.set_reg(dst, v);
8161                    return true;
8162                }
8163
8164                break;
8165            }
8166            depth += 1;
8167            cur = unsafe { (*p).prototype };
8168        }
8169
8170        self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc)
8171    }
8172
8173    #[inline(never)]
8174    fn get_named_prop_slow(
8175        &mut self,
8176        ctx: &mut JSContext,
8177        dst: u16,
8178        obj_val: JSValue,
8179        atom: crate::runtime::atom::Atom,
8180        instr_pc: usize,
8181    ) -> bool {
8182        if (obj_val.is_string() && atom != ctx.common_atoms.length)
8183            || obj_val.is_int()
8184            || obj_val.is_float()
8185        {
8186            let pseudo_id = if obj_val.is_string() {
8187                PRIM_STRING_SHAPE_ID
8188            } else {
8189                PRIM_NUMBER_SHAPE_ID
8190            };
8191            let ic_table_ptr = self.cached_ic_table_ptr;
8192            if !ic_table_ptr.is_null() {
8193                let (ic_hit, r0_offset, r0_proto) =
8194                    unsafe { (*ic_table_ptr).get_reads0_values(instr_pc, pseudo_id) };
8195                if ic_hit {
8196                    if r0_proto != 0 {
8197                        let proto_obj =
8198                            unsafe { &*(r0_proto as *const crate::object::object::JSObject) };
8199                        if let Some(v) = proto_obj.get_by_offset(r0_offset as usize) {
8200                            self.set_reg(dst, v);
8201                            return true;
8202                        }
8203                    } else if r0_offset == u32::MAX {
8204                        self.set_reg(dst, JSValue::undefined());
8205                        return true;
8206                    }
8207                }
8208            }
8209        }
8210        if let Some(result) = self.get_named_prop_result(ctx, dst, obj_val, atom, instr_pc) {
8211            self.set_reg(dst, result);
8212            return true;
8213        }
8214        false
8215    }
8216
8217    #[inline(always)]
8218    fn set_named_prop_fast(
8219        &mut self,
8220        ctx: &mut JSContext,
8221        obj_reg: u16,
8222        val_reg: u16,
8223        atom: crate::runtime::atom::Atom,
8224        instr_pc: usize,
8225    ) -> bool {
8226        let obj_val = self.get_reg(obj_reg);
8227        let value = self.get_reg(val_reg);
8228        self.set_named_prop(ctx, obj_val, value, atom, instr_pc)
8229    }
8230
8231    #[inline(always)]
8232    fn proto_chain_has_accessors(
8233        &self,
8234        obj_val: JSValue,
8235        atom: crate::runtime::atom::Atom,
8236    ) -> (bool, Option<JSValue>) {
8237        if obj_val.is_object_like() {
8238            let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
8239            if let Some(entry) = js_obj.get_own_accessor_entry(atom) {
8240                return (true, entry.set);
8241            }
8242            let mut proto = js_obj.prototype;
8243            while let Some(proto_ptr) = proto {
8244                if proto_ptr.is_null() {
8245                    break;
8246                }
8247                let proto_obj = unsafe { &*proto_ptr };
8248                if let Some(entry) = proto_obj.get_own_accessor_entry(atom) {
8249                    return (true, entry.set);
8250                }
8251                proto = proto_obj.prototype;
8252            }
8253        }
8254        (false, None)
8255    }
8256
8257    #[inline(always)]
8258    fn set_named_prop(
8259        &mut self,
8260        ctx: &mut JSContext,
8261        obj_val: JSValue,
8262        value: JSValue,
8263        atom: crate::runtime::atom::Atom,
8264        ic_pc: usize,
8265    ) -> bool {
8266        if !obj_val.is_object_like() {
8267            if self.frames[self.frame_index].is_strict_frame {
8268                self.set_pending_type_error(ctx, "Cannot assign to a primitive value");
8269            }
8270            return false;
8271        }
8272        if obj_val.is_object_like() {
8273            let ptr = obj_val.get_ptr();
8274            let js_obj = unsafe { JSValue::object_from_ptr_mut(ptr) };
8275            let ic_table_ptr = self.cached_ic_table_ptr;
8276
8277            // For shapeless objects with no properties yet, treat as root shape for IC
8278            // (all new objects start at root shape before any property is assigned)
8279            let effective_shape_id = js_obj.get_shape_id().or_else(|| {
8280                if js_obj.props_len() == 0 {
8281                    Some(crate::object::shape::ShapeId::root())
8282                } else {
8283                    None
8284                }
8285            });
8286            if let Some(shape_id) = effective_shape_id {
8287                if !ic_table_ptr.is_null() {
8288                    let ic_table = unsafe { &*ic_table_ptr };
8289                    if let Some(ic) = ic_table.get(ic_pc) {
8290                        if let Some((offset, new_shape_ptr)) = ic.get_transition(shape_id) {
8291                            if new_shape_ptr.is_null() {
8292                                let off = offset as usize;
8293                                if off < crate::object::object::INLINE_PROPS
8294                                    && js_obj.has_no_deleted_props()
8295                                {
8296                                    js_obj.set_by_offset_fast(off, value);
8297                                } else {
8298                                    js_obj.set_by_offset(off, value);
8299                                }
8300                            } else {
8301                                let new_shape = unsafe {
8302                                    std::ptr::NonNull::new_unchecked(
8303                                        new_shape_ptr as *mut crate::object::shape::Shape,
8304                                    )
8305                                };
8306                                js_obj.push_prop_with_shape(
8307                                    offset as usize,
8308                                    atom,
8309                                    value,
8310                                    new_shape,
8311                                );
8312                            }
8313                            return true;
8314                        }
8315                    }
8316                }
8317            }
8318
8319            let pre_offset = js_obj.find_offset(atom);
8320
8321            if !js_obj.is_prop_writable_at(pre_offset) {
8322                let msg = format!(
8323                    "Cannot assign to read only property '{}'",
8324                    ctx.get_atom_str(atom)
8325                );
8326                self.set_pending_type_error(ctx, &msg);
8327                return false;
8328            }
8329
8330            let (has_accessor, setter) = if !ic_table_ptr.is_null() {
8331                let ic_table = unsafe { &*ic_table_ptr };
8332                if let Some(shape_id) = js_obj.get_shape_id() {
8333                    if ic_table.get_write_no_accessor(ic_pc, shape_id) {
8334                        (false, None)
8335                    } else {
8336                        let r = self.proto_chain_has_accessors(obj_val, atom);
8337                        if !r.0 {
8338                            unsafe {
8339                                (*ic_table_ptr).set_write_no_accessor(ic_pc);
8340                            }
8341                        }
8342                        r
8343                    }
8344                } else {
8345                    self.proto_chain_has_accessors(obj_val, atom)
8346                }
8347            } else {
8348                self.proto_chain_has_accessors(obj_val, atom)
8349            };
8350            if has_accessor {
8351                if let Some(setter_fn) = setter {
8352                    let _ = self.call_function_with_this(ctx, setter_fn, obj_val, &[value]);
8353                }
8354                return false;
8355            }
8356
8357            if pre_offset.is_none() && !js_obj.extensible() {
8358                let msg = format!(
8359                    "Cannot define property '{}', object is not extensible",
8360                    ctx.get_atom_str(atom)
8361                );
8362                self.set_pending_type_error(ctx, &msg);
8363                return false;
8364            }
8365
8366            self.set_named_prop_slow(ctx, obj_val, ptr, js_obj, value, atom, ic_pc);
8367        }
8368        false
8369    }
8370
8371    #[cold]
8372    fn set_named_prop_slow(
8373        &mut self,
8374        ctx: &mut JSContext,
8375        obj_val: JSValue,
8376        ptr: usize,
8377        js_obj: &mut crate::object::object::JSObject,
8378        value: JSValue,
8379        atom: crate::runtime::atom::Atom,
8380        ic_pc: usize,
8381    ) {
8382        let ic_table_ptr = self.cached_ic_table_ptr;
8383
8384        let pre_shape_id = js_obj.get_shape_id();
8385        let pre_props_len = js_obj.props_len();
8386        let pre_offset = js_obj.find_offset(atom);
8387        js_obj.set_cached_with_offset(atom, value, ctx.shape_cache_mut(), pre_offset);
8388        if let Some(shape_id) = js_obj.get_shape_id() {
8389            let offset = pre_offset.or_else(|| js_obj.find_offset(atom));
8390            if let Some(offset) = offset {
8391                if !ic_table_ptr.is_null() && ic_pc != usize::MAX {
8392                    unsafe {
8393                        (*ic_table_ptr).ensure_capacity((ic_pc) + 1);
8394                        if let Some(ic) = (*ic_table_ptr).get_mut(ic_pc) {
8395                            let was_transition = offset == pre_props_len;
8396                            if was_transition {
8397                                if let Some(pre_id) = pre_shape_id {
8398                                    if let Some(new_shape_ptr) = js_obj.get_shape_ptr() {
8399                                        ic.insert_transition(pre_id, offset as u32, new_shape_ptr);
8400                                    } else {
8401                                        ic.insert(shape_id, offset as u32, None);
8402                                    }
8403                                } else if pre_props_len == 0 {
8404                                    // Object was shapeless with no props (at root state)
8405                                    // Record transition from root shape so future IC lookups hit
8406                                    if let Some(new_shape_ptr) = js_obj.get_shape_ptr() {
8407                                        ic.insert_transition(
8408                                            crate::object::shape::ShapeId::root(),
8409                                            offset as u32,
8410                                            new_shape_ptr,
8411                                        );
8412                                    }
8413                                } else {
8414                                    ic.insert(shape_id, offset as u32, None);
8415                                }
8416                            } else {
8417                                ic.insert_transition_null(shape_id, offset as u32);
8418                            }
8419                        }
8420                    }
8421                }
8422            }
8423        }
8424
8425        if obj_val.is_function() && atom == ctx.common_atoms.prototype {
8426            let js_func = unsafe { JSValue::function_from_ptr_mut(ptr) };
8427            if value.is_object() {
8428                js_func.cached_prototype_ptr =
8429                    value.get_ptr() as *mut crate::object::object::JSObject;
8430            } else {
8431                js_func.cached_prototype_ptr = std::ptr::null_mut();
8432            }
8433        }
8434
8435        if obj_val.is_function() && atom.0 & 0x40000000 != 0 {
8436            let js_func = unsafe { JSValue::function_from_ptr_mut(ptr) };
8437            js_func.mark_has_symbol_prop();
8438        }
8439    }
8440
8441    fn int_atom(&self, idx: usize, ctx: &mut JSContext) -> crate::runtime::atom::Atom {
8442        ctx.int_atom_mut(idx)
8443    }
8444
8445    #[cold]
8446    fn add_slow(&mut self, a: &JSValue, b: &JSValue, ctx: &mut JSContext) -> JSValue {
8447        if a.is_int() && b.is_float() {
8448            return JSValue::new_float_raw(a.get_int() as f64 + b.get_float());
8449        }
8450        if a.is_float() && b.is_int() {
8451            return JSValue::new_float_raw(a.get_float() + b.get_int() as f64);
8452        }
8453
8454        let a = self.ordinary_to_primitive(a, "default", ctx);
8455        if self.pending_throw.is_some() {
8456            return JSValue::undefined();
8457        }
8458        let b = self.ordinary_to_primitive(b, "default", ctx);
8459        if self.pending_throw.is_some() {
8460            return JSValue::undefined();
8461        }
8462
8463        if a.is_bigint() && b.is_bigint() {
8464            let a_int = Self::get_bigint_int(&a).unwrap_or(0);
8465            let b_int = Self::get_bigint_int(&b).unwrap_or(0);
8466            Self::create_bigint(a_int + b_int)
8467        } else if a.is_symbol() || b.is_symbol() {
8468            self.set_pending_type_error(ctx, "Cannot convert a Symbol value to a string");
8469            JSValue::undefined()
8470        } else if b.is_string() || a.is_string() {
8471            let a_str = if a.is_object() || a.is_function() {
8472                self.object_to_string(&a, ctx)
8473            } else {
8474                Self::js_to_string(&a, ctx)
8475            };
8476            let b_str = if b.is_object() || b.is_function() {
8477                self.object_to_string(&b, ctx)
8478            } else {
8479                Self::js_to_string(&b, ctx)
8480            };
8481            Self::js_add_string(&a_str, &b_str, ctx)
8482        } else if a.is_bigint() || b.is_bigint() {
8483            self.set_pending_type_error(ctx, "Cannot mix BigInt and other types");
8484            JSValue::undefined()
8485        } else if a.is_float() && b.is_float() {
8486            JSValue::new_float_raw(a.get_float() + b.get_float())
8487        } else if a.is_int() && b.is_float() {
8488            JSValue::new_float_raw(a.get_int() as f64 + b.get_float())
8489        } else if a.is_float() && b.is_int() {
8490            JSValue::new_float_raw(a.get_float() + b.get_int() as f64)
8491        } else {
8492            let fa = Self::js_to_number(&a, ctx);
8493            let fb = Self::js_to_number(&b, ctx);
8494            JSValue::new_float_raw(fa + fb)
8495        }
8496    }
8497
8498    fn get_method_for_primitive(
8499        &self,
8500        obj: &crate::object::JSObject,
8501        method_atom: crate::runtime::atom::Atom,
8502        ctx: &JSContext,
8503    ) -> Option<JSValue> {
8504        if let Some(v) = obj.get(method_atom) {
8505            return Some(v);
8506        }
8507
8508        if obj.obj_type() == crate::object::object::ObjectType::Function {
8509            if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
8510                unsafe {
8511                    if let Some(v) = (*fn_proto_ptr).get(method_atom) {
8512                        return Some(v);
8513                    }
8514                }
8515            }
8516        }
8517        None
8518    }
8519
8520    fn get_symbol_to_primitive_atom(
8521        &self,
8522        ctx: &mut JSContext,
8523    ) -> Option<crate::runtime::atom::Atom> {
8524        let global = ctx.global();
8525        if !global.is_object() {
8526            return None;
8527        }
8528        let sym_val = global.as_object().get(ctx.intern("Symbol.toPrimitive"))?;
8529        if !sym_val.is_symbol() {
8530            return None;
8531        }
8532        let sym_id = sym_val.get_symbol_id();
8533        Some(crate::runtime::atom::Atom(0x40000000 | sym_id))
8534    }
8535
8536    fn call_function_safe(
8537        &mut self,
8538        ctx: &mut JSContext,
8539        func: JSValue,
8540        this_val: JSValue,
8541        args: &[JSValue],
8542    ) -> Result<JSValue, String> {
8543        let saved_handlers = std::mem::take(&mut self.exception_handlers);
8544        let result = self.call_function_with_this(ctx, func, this_val, args);
8545        self.exception_handlers = saved_handlers;
8546        if let Some(exc) = ctx.pending_exception.take() {
8547            self.pending_throw = Some(exc);
8548            return Err("exception propagated".to_string());
8549        }
8550        result
8551    }
8552
8553    pub fn ordinary_to_primitive(&mut self, v: &JSValue, hint: &str, ctx: &mut JSContext) -> JSValue {
8554        if !v.is_object() && !v.is_function() {
8555            return *v;
8556        }
8557        let obj: &crate::object::JSObject = if v.is_object() {
8558            v.as_object()
8559        } else if v.is_function() {
8560            let func = v.as_function();
8561
8562            if let Some(custom_fn) = func.base.get(ctx.common_atoms.value_of) {
8563                if custom_fn.is_function() {
8564                    if let Ok(r) = self.call_function_with_this(ctx, custom_fn, *v, &[]) {
8565                        if !r.is_object() {
8566                            return r;
8567                        }
8568                    }
8569                }
8570            }
8571
8572            if let Some(custom_fn) = func.base.get(ctx.common_atoms.to_string) {
8573                if custom_fn.is_function() {
8574                    if let Ok(r) = self.call_function_with_this(ctx, custom_fn, *v, &[]) {
8575                        if !r.is_object() {
8576                            return r;
8577                        }
8578                    }
8579                }
8580            }
8581            let name = ctx.get_atom_str(func.name);
8582            let params: Vec<String> = (0..func.arity as usize)
8583                .map(|i| format!("a{}", i))
8584                .collect();
8585            let param_str = params.join(", ");
8586            let s = if name.is_empty() {
8587                format!("function({}) {{ [user code] }}", param_str)
8588            } else {
8589                format!("function {}({}) {{ [user code] }}", name, param_str)
8590            };
8591            return JSValue::new_string(ctx.intern(&s));
8592        } else {
8593            return *v;
8594        };
8595
8596        let mut has_tp = false;
8597
8598        let has_own_tp = self.get_symbol_to_primitive_atom(ctx).map_or(false, |a| {
8599            let mut cur: Option<*mut crate::object::object::JSObject> = Some(v.get_ptr() as *mut _);
8600            while let Some(p) = cur {
8601                unsafe {
8602                    if let Some(v) = (*p).get_own_value(a) {
8603                        return v.is_function();
8604                    }
8605                    cur = (*p).prototype;
8606                }
8607            }
8608            false
8609        });
8610        if has_own_tp {
8611            has_tp = true;
8612        } else if let Some(tp_atom) = self.get_symbol_to_primitive_atom(ctx) {
8613            let tp_val = {
8614                let saved_tp = std::mem::take(&mut self.exception_handlers);
8615                let result = obj
8616                    .get_own_descriptor(tp_atom)
8617                    .and_then(|desc| {
8618                        if let Some(getter) = desc.get {
8619                            self.call_function_with_this(ctx, getter, *v, &[]).ok()
8620                        } else {
8621                            desc.value
8622                        }
8623                    })
8624                    .or_else(|| {
8625                        let mut current = obj.prototype;
8626                        while let Some(proto_ptr) = current {
8627                            unsafe {
8628                                let proto = &*proto_ptr;
8629                                if let Some(desc) = proto.get_own_descriptor(tp_atom) {
8630                                    if let Some(getter) = desc.get {
8631                                        return self
8632                                            .call_function_with_this(ctx, getter, *v, &[])
8633                                            .ok();
8634                                    }
8635                                    return desc.value;
8636                                }
8637                                current = proto.prototype;
8638                            }
8639                        }
8640                        None
8641                    });
8642                self.exception_handlers = saved_tp;
8643                result
8644            };
8645            if self.pending_throw.is_some() {
8646                return JSValue::undefined();
8647            }
8648            has_tp = tp_val.map_or(false, |v| v.is_function());
8649        }
8650        if !has_tp {
8651            if let Some(prim) = obj.get(ctx.common_atoms.__value__) {
8652                if !prim.is_object() && !prim.is_function() {
8653                    return prim;
8654                }
8655            }
8656        }
8657
8658        if let Some(tp_atom) = self.get_symbol_to_primitive_atom(ctx) {
8659            let tp_method = (|| -> Option<JSValue> {
8660                let from_own = obj.get_own_descriptor(tp_atom).and_then(|desc| {
8661                    if let Some(getter) = desc.get {
8662                        let saved = std::mem::take(&mut self.exception_handlers);
8663                        let result = self.call_function_with_this(ctx, getter, *v, &[]).ok();
8664                        self.exception_handlers = saved;
8665                        result
8666                    } else {
8667                        desc.value
8668                    }
8669                });
8670                if from_own.is_some() {
8671                    return from_own;
8672                }
8673
8674                let mut current = obj.prototype;
8675                while let Some(proto_ptr) = current {
8676                    unsafe {
8677                        let proto = &*proto_ptr;
8678                        if let Some(desc) = proto.get_own_descriptor(tp_atom) {
8679                            if let Some(getter) = desc.get {
8680                                let saved = std::mem::take(&mut self.exception_handlers);
8681                                let result =
8682                                    self.call_function_with_this(ctx, getter, *v, &[]).ok();
8683                                self.exception_handlers = saved;
8684                                return result;
8685                            }
8686                            return desc.value;
8687                        }
8688                        current = proto.prototype;
8689                    }
8690                }
8691                None
8692            })();
8693            if let Some(tp_fn) = tp_method {
8694                if tp_fn.is_function() {
8695                    let hint_atom = ctx.intern(hint);
8696                    let result =
8697                        self.call_function_safe(ctx, tp_fn, *v, &[JSValue::new_string(hint_atom)]);
8698                    match result {
8699                        Ok(r) if !r.is_object() => return r,
8700                        Ok(_) => {
8701                            self.set_pending_type_error(
8702                                ctx,
8703                                "Cannot convert object to primitive value",
8704                            );
8705                            return JSValue::undefined();
8706                        }
8707                        Err(_) => {
8708                            return JSValue::undefined();
8709                        }
8710                    }
8711                }
8712            }
8713        }
8714
8715        let hint = if hint == "default" { "number" } else { hint };
8716        let to_string_atom = ctx.common_atoms.to_string;
8717        let value_of_atom = ctx.common_atoms.value_of;
8718        let (first_try, second_try) = if hint == "string" {
8719            (to_string_atom, value_of_atom)
8720        } else {
8721            (value_of_atom, to_string_atom)
8722        };
8723        for &method_atom in &[first_try, second_try] {
8724            if let Some(f) = self.get_method_for_primitive(obj, method_atom, ctx) {
8725                if f.is_function() {
8726                    let result = self.call_function_safe(ctx, f, *v, &[]);
8727                    match result {
8728                        Ok(r) if !r.is_object() => return r,
8729                        Ok(_) => {}
8730                        Err(_) => {
8731                            return JSValue::undefined();
8732                        }
8733                    }
8734                }
8735            }
8736        }
8737
8738        if v.is_function() {
8739            let func = v.as_function();
8740            let name = ctx.get_atom_str(func.name);
8741            if !name.is_empty() {
8742                return JSValue::new_string(
8743                    ctx.intern(&format!("function {}() {{ [native code] }}", name)),
8744                );
8745            }
8746            return JSValue::new_string(ctx.intern(&format!("function() {{ [native code] }}")));
8747        }
8748
8749        let name_atom = ctx.common_atoms.name;
8750        let message_atom = ctx.common_atoms.message;
8751        let mut err = crate::object::object::JSObject::new();
8752        err.set(name_atom, JSValue::new_string(ctx.intern("TypeError")));
8753        err.set(
8754            message_atom,
8755            JSValue::new_string(ctx.intern("Cannot convert object to primitive value")),
8756        );
8757        if let Some(proto) = ctx.get_type_error_prototype() {
8758            err.prototype = Some(proto);
8759        }
8760        let ptr = Box::into_raw(Box::new(err)) as usize;
8761        ctx.runtime_mut().gc_heap_mut().track(ptr);
8762        self.pending_throw = Some(JSValue::new_object(ptr));
8763        JSValue::undefined()
8764    }
8765
8766    fn js_to_string(v: &JSValue, ctx: &mut JSContext) -> JSValue {
8767        if v.is_string() {
8768            return *v;
8769        }
8770        if v.is_int() {
8771            return JSValue::new_string(ctx.intern(&v.get_int().to_string()));
8772        }
8773        if v.is_float() {
8774            return JSValue::new_string(ctx.intern(&v.get_float().to_string()));
8775        }
8776        if v.is_bool() {
8777            return JSValue::new_string(ctx.intern(&v.get_bool().to_string()));
8778        }
8779        if v.is_null() {
8780            return JSValue::new_string(ctx.common_atoms.null);
8781        }
8782        if v.is_undefined() {
8783            return JSValue::new_string(ctx.common_atoms.undefined);
8784        }
8785        if v.is_bigint() {
8786            let val = VM::get_bigint_int(v).unwrap_or(0);
8787            return JSValue::new_string(ctx.intern(&val.to_string()));
8788        }
8789        if v.is_symbol() {
8790            return JSValue::new_string(ctx.intern("Symbol()"));
8791        }
8792        JSValue::new_string(ctx.intern(""))
8793    }
8794
8795    fn object_to_string(&mut self, v: &JSValue, ctx: &mut JSContext) -> JSValue {
8796        if !v.is_object() && !v.is_function() {
8797            return Self::js_to_string(v, ctx);
8798        }
8799        let obj: &crate::object::JSObject = if v.is_object() {
8800            v.as_object()
8801        } else if v.is_function() {
8802            &v.as_function().base
8803        } else {
8804            return Self::js_to_string(v, ctx);
8805        };
8806        let to_str_atom = ctx.common_atoms.to_string;
8807
8808        if v.is_function() {
8809            let fn_builtin = ctx.get_builtin_func("function_toString");
8810            if let Some(f) = fn_builtin {
8811                let result = f(ctx, &[*v]);
8812                if result.is_string() {
8813                    return result;
8814                }
8815            }
8816
8817            let func = v.as_function();
8818            let name = ctx.get_atom_str(func.name);
8819            if name.is_empty() {
8820                return JSValue::new_string(ctx.intern("function () { [native code] }"));
8821            } else {
8822                return JSValue::new_string(
8823                    ctx.intern(&format!("function {}() {{ [native code] }}", name)),
8824                );
8825            }
8826        }
8827        if let Some(to_str_fn) = obj.get(to_str_atom) {
8828            if to_str_fn.is_function() {
8829                match self.call_function_with_this(ctx, to_str_fn, *v, &[]) {
8830                    Ok(r) if r.is_string() => return r,
8831                    Ok(r) => return Self::js_to_string(&r, ctx),
8832                    Err(_) => {}
8833                }
8834            }
8835        }
8836
8837        let obj_proto = if let Some(p) = ctx.get_object_prototype() {
8838            p
8839        } else {
8840            return Self::js_to_string(v, ctx);
8841        };
8842        let to_str = unsafe { (*obj_proto).get(to_str_atom) };
8843        if let Some(f) = to_str {
8844            if f.is_function() {
8845                match self.call_function_with_this(ctx, f, *v, &[]) {
8846                    Ok(r) => return r,
8847                    Err(_) => {}
8848                }
8849            }
8850        }
8851        Self::js_to_string(v, ctx)
8852    }
8853
8854    fn js_to_number(v: &JSValue, ctx: &mut JSContext) -> f64 {
8855        if v.is_int() {
8856            return v.get_int() as f64;
8857        } else if v.is_float() {
8858            return v.get_float();
8859        } else if v.is_bool() {
8860            return if v.get_bool() { 1.0 } else { 0.0 };
8861        } else if v.is_null() {
8862            return 0.0;
8863        } else if v.is_undefined() {
8864            return f64::NAN;
8865        } else if v.is_string() {
8866            let s = ctx.get_atom_str(v.get_atom());
8867            return Self::string_to_number(s);
8868        } else if v.is_bigint() {
8869            let val = VM::get_bigint_int(v).unwrap_or(0);
8870            return val as f64;
8871        } else if v.is_symbol() {
8872            return f64::NAN;
8873        } else if v.is_object() {
8874            if let Some(prim) = v.as_object().get(ctx.common_atoms.__value__) {
8875                return Self::js_to_number(&prim, ctx);
8876            }
8877        }
8878        f64::NAN
8879    }
8880
8881    pub fn string_to_number(s: &str) -> f64 {
8882        let trimmed = s.trim();
8883        if trimmed.is_empty() {
8884            return 0.0;
8885        }
8886        if trimmed == "Infinity" || trimmed == "+Infinity" {
8887            return f64::INFINITY;
8888        }
8889        if trimmed == "-Infinity" {
8890            return f64::NEG_INFINITY;
8891        }
8892        if (trimmed.starts_with("0x") || trimmed.starts_with("0X"))
8893            && trimmed.len() > 2
8894        {
8895            let hex_part = &trimmed[2..];
8896            if let Ok(n) = u64::from_str_radix(hex_part, 16) {
8897                return n as f64;
8898            }
8899            return f64::NAN;
8900        }
8901        if (trimmed.starts_with("0o") || trimmed.starts_with("0O"))
8902            && trimmed.len() > 2
8903        {
8904            let oct_part = &trimmed[2..];
8905            if let Ok(n) = u64::from_str_radix(oct_part, 8) {
8906                return n as f64;
8907            }
8908            return f64::NAN;
8909        }
8910        if (trimmed.starts_with("0b") || trimmed.starts_with("0B"))
8911            && trimmed.len() > 2
8912        {
8913            let bin_part = &trimmed[2..];
8914            if let Ok(n) = u64::from_str_radix(bin_part, 2) {
8915                return n as f64;
8916            }
8917            return f64::NAN;
8918        }
8919        let lower = trimmed.to_lowercase();
8920        if lower.contains("inf") || lower.contains("nan") {
8921            return f64::NAN;
8922        }
8923        trimmed.parse::<f64>().unwrap_or(f64::NAN)
8924    }
8925
8926    fn to_int32(na: f64) -> i32 {
8927        if na.is_nan() || na.is_infinite() || na == 0.0 {
8928            return 0;
8929        }
8930        let pos = na.abs().floor() as u64;
8931        let u32_val = if na > 0.0 {
8932            pos % 0x1_0000_0000
8933        } else {
8934            let rem = pos % 0x1_0000_0000;
8935            if rem == 0 { 0 } else { 0x1_0000_0000 - rem }
8936        };
8937        u32_val as i32
8938    }
8939
8940    fn get_bigint_int(v: &JSValue) -> Option<i128> {
8941        if v.is_bigint() {
8942            Some(v.as_object().get_bigint_value())
8943        } else {
8944            None
8945        }
8946    }
8947
8948    fn create_bigint(value: i128) -> JSValue {
8949        let mut bigint_obj = crate::object::object::JSObject::new_bigint();
8950        bigint_obj.set_bigint_value(value);
8951        let ptr = Box::into_raw(Box::new(bigint_obj)) as usize;
8952        JSValue::new_bigint(ptr)
8953    }
8954
8955    fn js_add_string(b: &JSValue, a: &JSValue, ctx: &mut JSContext) -> JSValue {
8956        if b.is_string() && a.is_string() {
8957            let atom = ctx.intern_concat_atoms(b.get_atom(), a.get_atom());
8958            return JSValue::new_string(atom);
8959        }
8960
8961        fn stringify(v: &JSValue, ctx: &JSContext) -> (Option<[u8; 24]>, usize, Option<String>) {
8962            if v.is_string() {
8963                let s = ctx.get_atom_str(v.get_atom());
8964                let bytes = s.as_bytes();
8965                let len = bytes.len();
8966                if len <= 24 {
8967                    let mut buf = [0u8; 24];
8968                    buf[..len].copy_from_slice(bytes);
8969                    return (Some(buf), len, None);
8970                } else {
8971                    return (None, len, Some(s.to_string()));
8972                }
8973            } else if v.is_int() {
8974                let n = v.get_int();
8975                let mut buf = [0u8; 24];
8976                let mut tmp = n;
8977                let mut len = 0;
8978                let negative = tmp < 0;
8979                if negative {
8980                    tmp = -tmp;
8981                }
8982                if tmp == 0 {
8983                    buf[0] = b'0';
8984                    len = 1;
8985                } else {
8986                    while tmp > 0 {
8987                        buf[len] = (tmp % 10) as u8 + b'0';
8988                        len += 1;
8989                        tmp /= 10;
8990                    }
8991                }
8992                if negative {
8993                    buf[len] = b'-';
8994                    len += 1;
8995                }
8996
8997                for i in 0..len / 2 {
8998                    buf.swap(i, len - 1 - i);
8999                }
9000                return (Some(buf), len, None);
9001            } else if v.is_float() {
9002                let f = v.get_float();
9003                let s = if f.fract() == 0.0 && f.abs() < 1e15 {
9004                    format!("{}", f as i64)
9005                } else {
9006                    format!("{}", f)
9007                };
9008                let bytes = s.as_bytes();
9009                let len = bytes.len();
9010                if len <= 24 {
9011                    let mut buf = [0u8; 24];
9012                    buf[..len].copy_from_slice(bytes);
9013                    return (Some(buf), len, None);
9014                } else {
9015                    return (None, len, Some(s));
9016                }
9017            } else if v.is_bool() {
9018                if v.get_bool() {
9019                    let mut buf = [0u8; 24];
9020                    buf[..4].copy_from_slice(b"true");
9021                    return (Some(buf), 4, None);
9022                } else {
9023                    let mut buf = [0u8; 24];
9024                    buf[..5].copy_from_slice(b"false");
9025                    return (Some(buf), 5, None);
9026                }
9027            } else if v.is_null() {
9028                let mut buf = [0u8; 24];
9029                buf[..4].copy_from_slice(b"null");
9030                return (Some(buf), 4, None);
9031            } else if v.is_object() || v.is_function() {
9032                let mut buf = [0u8; 24];
9033                let s = b"[object Object]";
9034                buf[..s.len()].copy_from_slice(s);
9035                return (Some(buf), s.len(), None);
9036            } else if v.is_bigint() {
9037                let val = crate::runtime::vm::VM::get_bigint_int(v);
9038                let n = val.unwrap_or(0);
9039                let mut buf = [0u8; 24];
9040                let s = n.to_string();
9041                let bytes = s.as_bytes();
9042                let len = bytes.len();
9043                if len <= 24 {
9044                    buf[..len].copy_from_slice(bytes);
9045                    (Some(buf), len, None)
9046                } else {
9047                    (None, len, Some(s))
9048                }
9049            } else {
9050                let mut buf = [0u8; 24];
9051                let s = b"undefined";
9052                buf[..s.len()].copy_from_slice(s);
9053                return (Some(buf), s.len(), None);
9054            }
9055        }
9056
9057        let (buf_b, len_b, str_b) = stringify(b, ctx);
9058        let (buf_a, len_a, str_a) = stringify(a, ctx);
9059
9060        let total = len_b + len_a;
9061        if total <= 128 {
9062            let mut combined = [0u8; 128];
9063            if let Some(buf) = buf_b {
9064                combined[..len_b].copy_from_slice(&buf[..len_b]);
9065            } else if let Some(ref s) = str_b {
9066                combined[..len_b].copy_from_slice(s.as_bytes());
9067            }
9068            if let Some(buf) = buf_a {
9069                combined[len_b..total].copy_from_slice(&buf[..len_a]);
9070            } else if let Some(ref s) = str_a {
9071                combined[len_b..total].copy_from_slice(s.as_bytes());
9072            }
9073            let atom = ctx.intern(unsafe { std::str::from_utf8_unchecked(&combined[..total]) });
9074            JSValue::new_string(atom)
9075        } else {
9076            let mut combined = String::with_capacity(total);
9077            if let Some(buf) = buf_b {
9078                combined.push_str(unsafe { std::str::from_utf8_unchecked(&buf[..len_b]) });
9079            } else if let Some(ref s) = str_b {
9080                combined.push_str(s);
9081            }
9082            if let Some(buf) = buf_a {
9083                combined.push_str(unsafe { std::str::from_utf8_unchecked(&buf[..len_a]) });
9084            } else if let Some(ref s) = str_a {
9085                combined.push_str(s);
9086            }
9087            let atom = ctx.intern(&combined);
9088            JSValue::new_string(atom)
9089        }
9090    }
9091}
9092
9093#[inline(always)]
9094fn loose_equal(ctx: &JSContext, a: JSValue, b: JSValue) -> bool {
9095    if a.raw_bits() == b.raw_bits() {
9096        return if a.is_float() {
9097            !a.get_float().is_nan()
9098        } else {
9099            true
9100        };
9101    }
9102
9103    if (a.is_object() || a.is_function()) && b.is_null_or_undefined() {
9104        return false;
9105    }
9106    if (b.is_object() || b.is_function()) && a.is_null_or_undefined() {
9107        return false;
9108    }
9109
9110    if JSValue::both_int(&a, &b) {
9111        return false;
9112    }
9113
9114    if JSValue::both_object(&a, &b) {
9115        return false;
9116    }
9117
9118    if a.is_null_or_undefined() {
9119        return b.is_null_or_undefined();
9120    }
9121    if b.is_null() || b.is_undefined() {
9122        return false;
9123    }
9124
9125    loose_equal_slow(ctx, a, b)
9126}
9127
9128#[cold]
9129fn loose_equal_slow(ctx: &JSContext, a: JSValue, b: JSValue) -> bool {
9130    if a.is_undefined() && b.is_undefined() {
9131        return true;
9132    }
9133    if a.is_null() && b.is_null() {
9134        return true;
9135    }
9136    if a.is_bool() && b.is_bool() {
9137        return a.get_bool() == b.get_bool();
9138    }
9139    if a.is_string() && b.is_string() {
9140        return a.get_atom().0 == b.get_atom().0;
9141    }
9142    if a.is_bigint() && b.is_bigint() {
9143        return a.strict_eq(&b);
9144    }
9145    if a.is_null() && b.is_undefined() || a.is_undefined() && b.is_null() {
9146        return true;
9147    }
9148
9149    if a.is_object() || a.is_function() {
9150        if (a.is_object() && b.is_object()) || (a.is_function() && b.is_function()) {
9151            return a.strict_eq(&b);
9152        }
9153        if b.is_bool() {
9154            return loose_equal(ctx, a, JSValue::new_int(if b.get_bool() { 1 } else { 0 }));
9155        }
9156        if b.is_string() || b.is_int() || b.is_float() {
9157            let obj = a.as_object();
9158            if let Some(v) = obj.get(ctx.common_atoms.__value__) {
9159                if !v.is_object() && !v.is_function() {
9160                    return loose_equal(ctx, v, b);
9161                }
9162            }
9163        }
9164        return false;
9165    }
9166    if b.is_object() || b.is_function() {
9167        if a.is_bool() {
9168            return loose_equal(ctx, JSValue::new_int(if a.get_bool() { 1 } else { 0 }), b);
9169        }
9170        if a.is_string() || a.is_int() || a.is_float() {
9171            let obj = b.as_object();
9172            if let Some(v) = obj.get(ctx.common_atoms.__value__) {
9173                if !v.is_object() && !v.is_function() {
9174                    return loose_equal(ctx, a, v);
9175                }
9176            }
9177        }
9178        return false;
9179    }
9180
9181    if a.is_float() && b.is_float() {
9182        return a.get_float() == b.get_float();
9183    }
9184    if a.is_int() && b.is_float() {
9185        return (a.get_int() as f64) == b.get_float();
9186    }
9187    if a.is_float() && b.is_int() {
9188        return a.get_float() == (b.get_int() as f64);
9189    }
9190
9191    if a.is_bool() && (b.is_int() || b.is_float()) {
9192        return loose_equal(ctx, JSValue::new_int(if a.get_bool() { 1 } else { 0 }), b);
9193    }
9194    if (a.is_int() || a.is_float()) && b.is_bool() {
9195        return loose_equal(ctx, a, JSValue::new_int(if b.get_bool() { 1 } else { 0 }));
9196    }
9197
9198    if a.is_bool() && b.is_string() {
9199        let n = JSValue::new_int(if a.get_bool() { 1 } else { 0 });
9200        return loose_equal(ctx, n, b);
9201    }
9202    if a.is_string() && b.is_bool() {
9203        return loose_equal(ctx, b, a);
9204    }
9205
9206    if a.is_string() && (b.is_int() || b.is_float()) {
9207        let s = ctx.get_atom_str(a.get_atom());
9208        if let Ok(n) = s.parse::<f64>() {
9209            if n.is_nan() {
9210                return false;
9211            }
9212            if b.is_int() {
9213                return n == (b.get_int() as f64);
9214            }
9215            return n == b.get_float();
9216        }
9217        return false;
9218    }
9219    if (a.is_int() || a.is_float()) && b.is_string() {
9220        return loose_equal(ctx, b, a);
9221    }
9222
9223    false
9224}
9225
9226#[cfg(test)]
9227mod tests {
9228    use super::*;
9229
9230    fn make_bytecode(code: Vec<u8>, constants: Vec<JSValue>, locals_count: u32) -> Bytecode {
9231        Bytecode {
9232            code,
9233            constants,
9234            locals_count,
9235            param_count: 0,
9236            line_number_table: None,
9237            ic_table: crate::compiler::InlineCacheTable::new(),
9238            shared_ic_table_ptr: std::ptr::null_mut(),
9239            uses_arguments: false,
9240            is_strict: false,
9241            var_name_to_slot: std::rc::Rc::new(Vec::new()),
9242            nested_bytecodes: Vec::new(),
9243            is_simple_constructor: false,
9244            simple_constructor_props: Vec::new(),
9245            cached_constructor_final_shape: None,
9246            cached_constructor_atoms: Vec::new(),
9247            shared_code_ptr: std::ptr::null(),
9248            shared_code_len: 0,
9249            shared_const_ptr: std::ptr::null(),
9250            shared_const_len: 0,
9251            shared_nested_bytecodes_ptr: std::ptr::null_mut(),
9252        }
9253    }
9254
9255    fn emit_u16(code: &mut Vec<u8>, v: u16) {
9256        code.extend_from_slice(&v.to_le_bytes());
9257    }
9258
9259    fn emit_u32(code: &mut Vec<u8>, v: u32) {
9260        code.extend_from_slice(&v.to_le_bytes());
9261    }
9262
9263    fn emit_i32(code: &mut Vec<u8>, v: i32) {
9264        code.extend_from_slice(&v.to_le_bytes());
9265    }
9266
9267    fn run_bytecode(code: Vec<u8>, constants: Vec<JSValue>, locals_count: u32) -> JSValue {
9268        let bc = make_bytecode(code, constants, locals_count);
9269        let mut rt = JSRuntime::new();
9270        let mut ctx = JSContext::new(&mut rt);
9271        let mut vm = VM::new();
9272        match vm.execute(&mut ctx, &bc).unwrap() {
9273            ExecutionOutcome::Complete(v) => v,
9274            ExecutionOutcome::Yield(v) => v,
9275        }
9276    }
9277
9278    #[test]
9279    fn test_vm_new_encoding_add() {
9280        let mut code = Vec::new();
9281
9282        code.push(Opcode::LoadInt as u8);
9283        emit_u16(&mut code, 1);
9284        emit_i32(&mut code, 40);
9285
9286        code.push(Opcode::LoadInt as u8);
9287        emit_u16(&mut code, 2);
9288        emit_i32(&mut code, 2);
9289
9290        code.push(Opcode::Add as u8);
9291        emit_u16(&mut code, 3);
9292        emit_u16(&mut code, 1);
9293        emit_u16(&mut code, 2);
9294
9295        code.push(Opcode::Move as u8);
9296        emit_u16(&mut code, 0);
9297        emit_u16(&mut code, 3);
9298
9299        code.push(Opcode::End as u8);
9300
9301        let result = run_bytecode(code, vec![], 4);
9302        assert_eq!(result.get_int(), 42);
9303    }
9304
9305    #[test]
9306    fn test_vm_new_encoding_set_get_prop() {
9307        let mut rt = JSRuntime::new();
9308        let mut ctx = JSContext::new(&mut rt);
9309        let atom_x = ctx.atom_table_mut().intern("x");
9310
9311        let mut code = Vec::new();
9312
9313        code.push(Opcode::NewObject as u8);
9314        emit_u16(&mut code, 1);
9315
9316        code.push(Opcode::LoadConst as u8);
9317        emit_u16(&mut code, 2);
9318        emit_u32(&mut code, 0);
9319
9320        code.push(Opcode::LoadInt as u8);
9321        emit_u16(&mut code, 3);
9322        emit_i32(&mut code, 42);
9323
9324        code.push(Opcode::SetProp as u8);
9325        emit_u16(&mut code, 1);
9326        emit_u16(&mut code, 2);
9327        emit_u16(&mut code, 3);
9328
9329        code.push(Opcode::GetProp as u8);
9330        emit_u16(&mut code, 4);
9331        emit_u16(&mut code, 1);
9332        emit_u16(&mut code, 2);
9333
9334        code.push(Opcode::Move as u8);
9335        emit_u16(&mut code, 0);
9336        emit_u16(&mut code, 4);
9337
9338        code.push(Opcode::End as u8);
9339
9340        let bc = make_bytecode(code, vec![JSValue::new_string(atom_x)], 5);
9341        let mut vm = VM::new();
9342        let result = match vm.execute(&mut ctx, &bc).unwrap() {
9343            ExecutionOutcome::Complete(v) => v,
9344            ExecutionOutcome::Yield(v) => v,
9345        };
9346        assert_eq!(result.get_int(), 42);
9347    }
9348
9349    fn builtin_const_99(_ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
9350        JSValue::new_int(99)
9351    }
9352
9353    fn builtin_inc(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
9354        let v = args.first().copied().unwrap_or_else(JSValue::undefined);
9355        if v.is_int() {
9356            JSValue::new_int(v.get_int() + 1)
9357        } else {
9358            JSValue::new_int(1)
9359        }
9360    }
9361
9362    #[test]
9363    fn test_vm_call0_new_encoding() {
9364        let mut rt = JSRuntime::new();
9365        let mut ctx = JSContext::new(&mut rt);
9366
9367        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f0"), 0);
9368        func.builtin_func = Some(builtin_const_99);
9369        let func_ptr = Box::into_raw(Box::new(func)) as usize;
9370        ctx.runtime_mut().gc_heap_mut().track_function(func_ptr);
9371
9372        let mut code = Vec::new();
9373        code.push(Opcode::LoadConst as u8);
9374        emit_u16(&mut code, 1);
9375        emit_u32(&mut code, 0);
9376
9377        code.push(Opcode::Call0 as u8);
9378        emit_u16(&mut code, 2);
9379        emit_u16(&mut code, 1);
9380
9381        code.push(Opcode::Move as u8);
9382        emit_u16(&mut code, 0);
9383        emit_u16(&mut code, 2);
9384
9385        code.push(Opcode::End as u8);
9386
9387        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 3);
9388        let mut vm = VM::new();
9389        let result = match vm.execute(&mut ctx, &bc).unwrap() {
9390            ExecutionOutcome::Complete(v) => v,
9391            ExecutionOutcome::Yield(v) => v,
9392        };
9393        assert_eq!(result.get_int(), 99);
9394    }
9395
9396    #[test]
9397    fn test_vm_call1_new_encoding() {
9398        let mut rt = JSRuntime::new();
9399        let mut ctx = JSContext::new(&mut rt);
9400
9401        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f1"), 1);
9402        func.builtin_func = Some(builtin_inc);
9403        let func_ptr = Box::into_raw(Box::new(func)) as usize;
9404
9405        let mut code = Vec::new();
9406        code.push(Opcode::LoadConst as u8);
9407        emit_u16(&mut code, 1);
9408        emit_u32(&mut code, 0);
9409
9410        code.push(Opcode::LoadInt as u8);
9411        emit_u16(&mut code, 2);
9412        emit_i32(&mut code, 41);
9413
9414        code.push(Opcode::Call1 as u8);
9415        emit_u16(&mut code, 3);
9416        emit_u16(&mut code, 1);
9417        emit_u16(&mut code, 2);
9418
9419        code.push(Opcode::Move as u8);
9420        emit_u16(&mut code, 0);
9421        emit_u16(&mut code, 3);
9422
9423        code.push(Opcode::End as u8);
9424
9425        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 4);
9426        let mut vm = VM::new();
9427        let result = match vm.execute(&mut ctx, &bc).unwrap() {
9428            ExecutionOutcome::Complete(v) => v,
9429            ExecutionOutcome::Yield(v) => v,
9430        };
9431        assert_eq!(result.get_int(), 42);
9432    }
9433
9434    #[test]
9435    fn test_vm_call2_new_encoding() {
9436        let mut rt = JSRuntime::new();
9437        let mut ctx = JSContext::new(&mut rt);
9438
9439        fn builtin_add2(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
9440            let a = args.first().copied().unwrap_or_else(JSValue::undefined);
9441            let b = args.get(1).copied().unwrap_or_else(JSValue::undefined);
9442            JSValue::new_int(a.get_int() + b.get_int())
9443        }
9444
9445        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f2"), 2);
9446        func.builtin_func = Some(builtin_add2);
9447        let func_ptr = Box::into_raw(Box::new(func)) as usize;
9448
9449        let mut code = Vec::new();
9450        code.push(Opcode::LoadConst as u8);
9451        emit_u16(&mut code, 1);
9452        emit_u32(&mut code, 0);
9453
9454        code.push(Opcode::LoadInt as u8);
9455        emit_u16(&mut code, 2);
9456        emit_i32(&mut code, 40);
9457
9458        code.push(Opcode::LoadInt as u8);
9459        emit_u16(&mut code, 3);
9460        emit_i32(&mut code, 2);
9461
9462        code.push(Opcode::Call2 as u8);
9463        emit_u16(&mut code, 4);
9464        emit_u16(&mut code, 1);
9465        emit_u16(&mut code, 2);
9466        emit_u16(&mut code, 3);
9467
9468        code.push(Opcode::Move as u8);
9469        emit_u16(&mut code, 0);
9470        emit_u16(&mut code, 4);
9471
9472        code.push(Opcode::End as u8);
9473
9474        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 5);
9475        let mut vm = VM::new();
9476        let result = match vm.execute(&mut ctx, &bc).unwrap() {
9477            ExecutionOutcome::Complete(v) => v,
9478            ExecutionOutcome::Yield(v) => v,
9479        };
9480        assert_eq!(result.get_int(), 42);
9481    }
9482
9483    #[test]
9484    fn test_vm_call3_new_encoding() {
9485        let mut rt = JSRuntime::new();
9486        let mut ctx = JSContext::new(&mut rt);
9487
9488        fn builtin_add3(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
9489            let a = args.first().copied().unwrap_or_else(JSValue::undefined);
9490            let b = args.get(1).copied().unwrap_or_else(JSValue::undefined);
9491            let c = args.get(2).copied().unwrap_or_else(JSValue::undefined);
9492            JSValue::new_int(a.get_int() + b.get_int() + c.get_int())
9493        }
9494
9495        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f3"), 3);
9496        func.builtin_func = Some(builtin_add3);
9497        let func_ptr = Box::into_raw(Box::new(func)) as usize;
9498
9499        let mut code = Vec::new();
9500        code.push(Opcode::LoadConst as u8);
9501        emit_u16(&mut code, 1);
9502        emit_u32(&mut code, 0);
9503
9504        code.push(Opcode::LoadInt as u8);
9505        emit_u16(&mut code, 2);
9506        emit_i32(&mut code, 39);
9507
9508        code.push(Opcode::LoadInt as u8);
9509        emit_u16(&mut code, 3);
9510        emit_i32(&mut code, 2);
9511
9512        code.push(Opcode::LoadInt as u8);
9513        emit_u16(&mut code, 4);
9514        emit_i32(&mut code, 1);
9515
9516        code.push(Opcode::Call3 as u8);
9517        emit_u16(&mut code, 5);
9518        emit_u16(&mut code, 1);
9519        emit_u16(&mut code, 2);
9520        emit_u16(&mut code, 3);
9521        emit_u16(&mut code, 4);
9522
9523        code.push(Opcode::Move as u8);
9524        emit_u16(&mut code, 0);
9525        emit_u16(&mut code, 5);
9526
9527        code.push(Opcode::End as u8);
9528
9529        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 6);
9530        let mut vm = VM::new();
9531        let result = match vm.execute(&mut ctx, &bc).unwrap() {
9532            ExecutionOutcome::Complete(v) => v,
9533            ExecutionOutcome::Yield(v) => v,
9534        };
9535        assert_eq!(result.get_int(), 42);
9536    }
9537
9538    #[test]
9539    fn test_vm_inc_local_new_encoding() {
9540        let mut code = Vec::new();
9541
9542        code.push(Opcode::LoadInt as u8);
9543        emit_u16(&mut code, 1);
9544        emit_i32(&mut code, 41);
9545
9546        code.push(Opcode::IncLocal as u8);
9547        emit_u16(&mut code, 1);
9548
9549        code.push(Opcode::Move as u8);
9550        emit_u16(&mut code, 0);
9551        emit_u16(&mut code, 1);
9552
9553        code.push(Opcode::End as u8);
9554
9555        let result = run_bytecode(code, vec![], 2);
9556        assert_eq!(result.get_int(), 42);
9557    }
9558
9559    #[test]
9560    fn test_vm_dec_local_new_encoding() {
9561        let mut code = Vec::new();
9562
9563        code.push(Opcode::LoadInt as u8);
9564        emit_u16(&mut code, 1);
9565        emit_i32(&mut code, 41);
9566
9567        code.push(Opcode::DecLocal as u8);
9568        emit_u16(&mut code, 1);
9569
9570        code.push(Opcode::Move as u8);
9571        emit_u16(&mut code, 0);
9572        emit_u16(&mut code, 1);
9573
9574        code.push(Opcode::End as u8);
9575
9576        let result = run_bytecode(code, vec![], 2);
9577        assert_eq!(result.get_int(), 40);
9578    }
9579}
9580
9581impl Drop for VM {
9582    fn drop(&mut self) {
9583        for frame in self.frames.iter_mut() {
9584            frame.saved_args = Vec::new();
9585            frame.upvalue_sync_map = None;
9586            frame.eval_bindings = None;
9587            frame.cached_arguments = None;
9588        }
9589    }
9590}