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