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                    self.set_reg(dst, result);
3816                }
3817                Opcode::SetUpvalue => {
3818                    let slot = self.read_u16_pc() as usize;
3819                    let src = self.read_u16_pc();
3820                    let val = self.get_reg(src);
3821                    if !self.cached_upvalue_slot_ptr.is_null() && slot < self.cached_upvalues_len {
3822                        let rc = unsafe { &*self.cached_upvalue_slot_ptr.add(slot) };
3823                        unsafe { (*std::rc::Rc::as_ptr(rc)).as_ptr().write(val) };
3824                    }
3825                }
3826
3827                Opcode::NewObject => {
3828                    let dst = self.read_u16_pc();
3829                    let ptr = if let Some(proto_ptr) = ctx.get_object_prototype() {
3830                        if let Some(nursery_ptr) = ctx.runtime_mut().gc_heap_mut().alloc_object() {
3831                            unsafe {
3832                                (*nursery_ptr).prototype = Some(proto_ptr);
3833                                (*nursery_ptr).ensure_shape(ctx.shape_cache_mut());
3834                            }
3835                            nursery_ptr as usize
3836                        } else {
3837                            let mut obj = crate::object::object::JSObject::new();
3838                            obj.prototype = Some(proto_ptr);
3839                            obj.ensure_shape(ctx.shape_cache_mut());
3840                            let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
3841                            ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
3842                            heap_ptr
3843                        }
3844                    } else {
3845                        let mut obj = crate::object::object::JSObject::new();
3846                        obj.ensure_shape(ctx.shape_cache_mut());
3847                        let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
3848                        ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
3849                        heap_ptr
3850                    };
3851                    self.allocation_count += 1;
3852                    self.set_reg(dst, JSValue::new_object(ptr));
3853                    self.maybe_gc(ctx);
3854                }
3855                Opcode::NewArray => {
3856                    let dst = self.read_u16_pc();
3857                    let count = self.read_u16_pc();
3858                    let ptr = if let Some(proto_ptr) = ctx.get_array_prototype() {
3859                        if let Some(nursery_ptr) = ctx.runtime_mut().gc_heap_mut().alloc_array() {
3860                            unsafe {
3861                                (*nursery_ptr).header.set_prototype_raw(proto_ptr);
3862                                (*nursery_ptr).elements.reserve(count as usize);
3863                                (*nursery_ptr).header.ensure_shape(ctx.shape_cache_mut());
3864                            }
3865                            nursery_ptr as usize
3866                        } else {
3867                            let mut arr = crate::object::array_obj::JSArrayObject::with_capacity(
3868                                count as usize,
3869                            );
3870                            arr.header.set_prototype_raw(proto_ptr);
3871                            arr.header.ensure_shape(ctx.shape_cache_mut());
3872                            let heap_ptr = Box::into_raw(Box::new(arr)) as usize;
3873                            ctx.runtime_mut().gc_heap_mut().track_array(heap_ptr);
3874                            heap_ptr
3875                        }
3876                    } else {
3877                        let mut arr =
3878                            crate::object::array_obj::JSArrayObject::with_capacity(count as usize);
3879                        arr.header.ensure_shape(ctx.shape_cache_mut());
3880                        let heap_ptr = Box::into_raw(Box::new(arr)) as usize;
3881                        ctx.runtime_mut().gc_heap_mut().track_array(heap_ptr);
3882                        heap_ptr
3883                    };
3884                    self.allocation_count += 1;
3885                    self.set_reg(dst, JSValue::new_object(ptr));
3886                    self.maybe_gc(ctx);
3887                }
3888                Opcode::GetField => {
3889                    let dst = self.read_u16_pc();
3890                    let obj_reg = self.read_u16_pc();
3891                    let key_reg = self.read_u16_pc();
3892                    let obj_val = self.get_reg(obj_reg);
3893                    let key_val = self.get_reg(key_reg);
3894                    let result = if obj_val.is_object_like() && key_val.is_int() {
3895                        if obj_val.is_object() {
3896                            let js_obj_check =
3897                                unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
3898                            if js_obj_check.is_mapped_arguments() {
3899                                let fi = js_obj_check.mapped_args_frame_index();
3900                                let param_count = js_obj_check.mapped_args_param_count();
3901                                let idx = key_val.get_int();
3902                                if idx >= 0 && fi < self.frames.len() {
3903                                    let idx_u = idx as usize;
3904                                    if (idx as u32) < param_count {
3905                                        let base = self.frames[fi].registers_base;
3906                                        let reg_idx = base + 1 + idx_u;
3907                                        if reg_idx < self.registers.len() {
3908                                            self.set_reg(dst, self.registers[reg_idx]);
3909                                            continue;
3910                                        }
3911                                    } else {
3912                                        let saved = &self.frames[fi].saved_args;
3913                                        if idx_u < saved.len() {
3914                                            self.set_reg(dst, saved[idx_u]);
3915                                            continue;
3916                                        }
3917                                    }
3918                                }
3919                            }
3920                        }
3921                        let ptr = obj_val.get_ptr();
3922                        let js_obj = unsafe { JSValue::object_from_ptr(ptr) };
3923                        if js_obj.is_dense_array() {
3924                            let idx = key_val.get_int() as usize;
3925                            let arr = unsafe {
3926                                &*(ptr as *const crate::object::array_obj::JSArrayObject)
3927                            };
3928                            if idx < arr.elements.len() {
3929                                arr.elements[idx]
3930                            } else {
3931                                JSValue::undefined()
3932                            }
3933                        } else {
3934                            let idx = key_val.get_int();
3935                            if idx >= 0 {
3936                                if let Some(val) = js_obj.get_indexed(idx as usize) {
3937                                    self.set_reg(dst, val);
3938                                    continue;
3939                                }
3940                            }
3941                            let atom = self.int_atom(idx as usize, ctx);
3942                            js_obj.get(atom).unwrap_or(JSValue::undefined())
3943                        }
3944                    } else if obj_val.is_string() && key_val.is_int() {
3945                        let s = ctx.get_atom_str(obj_val.get_atom());
3946                        let idx = key_val.get_int();
3947                        if idx < 0 {
3948                            JSValue::undefined()
3949                        } else if let Some(ch) = s.chars().nth(idx as usize) {
3950                            let chs = ch.to_string();
3951                            JSValue::new_string(ctx.intern(&chs))
3952                        } else {
3953                            JSValue::undefined()
3954                        }
3955                    } else if obj_val.is_string() && key_val.is_string() {
3956                        let str_atom = obj_val.get_atom();
3957                        let key = ctx.get_atom_str(key_val.get_atom());
3958                        if key == "length" {
3959                            JSValue::new_int(ctx.string_char_count(str_atom) as i64)
3960                        } else if let Ok(idx) = key.parse::<usize>() {
3961                            let s = ctx.get_atom_str(str_atom);
3962                            if let Some(ch) = s.chars().nth(idx) {
3963                                let chs = ch.to_string();
3964                                JSValue::new_string(ctx.intern(&chs))
3965                            } else {
3966                                JSValue::undefined()
3967                            }
3968                        } else if let Some(proto_ptr) = ctx.get_string_prototype() {
3969                            let proto_obj = unsafe { &*proto_ptr };
3970                            proto_obj
3971                                .get(key_val.get_atom())
3972                                .unwrap_or(JSValue::undefined())
3973                        } else {
3974                            JSValue::undefined()
3975                        }
3976                    } else if obj_val.is_object_like() && key_val.is_string() {
3977                        let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
3978                        let atom = key_val.get_atom();
3979
3980                        if let Some(getter) = js_obj.get_own_accessor_value(atom) {
3981                            if getter.is_function() {
3982                                match self.call_function_with_this(ctx, getter, obj_val, &[]) {
3983                                    Ok(v) => v,
3984                                    Err(e) => {
3985                                        let mut err = crate::object::object::JSObject::new();
3986                                        err.set(
3987                                            ctx.intern("name"),
3988                                            JSValue::new_string(ctx.intern("ReferenceError")),
3989                                        );
3990                                        err.set(
3991                                            ctx.intern("message"),
3992                                            JSValue::new_string(ctx.intern(&e)),
3993                                        );
3994                                        if let Some(proto) = ctx.get_reference_error_prototype() {
3995                                            err.prototype = Some(proto);
3996                                        }
3997                                        let ptr = Box::into_raw(Box::new(err)) as usize;
3998                                        ctx.runtime_mut().gc_heap_mut().track(ptr);
3999                                        let exc = JSValue::new_object(ptr);
4000                                        match self.dispatch_throw_value(ctx, exc) {
4001                                            ThrowDispatch::Caught => JSValue::undefined(),
4002                                            ThrowDispatch::Uncaught(_) => {
4003                                                return Err(e);
4004                                            }
4005                                            ThrowDispatch::AsyncComplete(o) => {
4006                                                return Ok(o);
4007                                            }
4008                                        }
4009                                    }
4010                                }
4011                            } else {
4012                                JSValue::undefined()
4013                            }
4014                        } else {
4015                            js_obj.get(atom).unwrap_or(JSValue::undefined())
4016                        }
4017                    } else if obj_val.is_object_like() && key_val.is_symbol() {
4018                        let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4019                        let sym_key =
4020                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4021                        js_obj.get(sym_key).unwrap_or(JSValue::undefined())
4022                    } else if obj_val.is_object_like() && key_val.is_float() {
4023                        let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4024                        let s = VM::js_to_string(&key_val, ctx);
4025                        let atom = s.get_atom();
4026                        js_obj.get(atom).unwrap_or(JSValue::undefined())
4027                    } else {
4028                        JSValue::undefined()
4029                    };
4030                    self.set_reg(dst, result);
4031                }
4032                Opcode::SetField => {
4033                    let obj_reg = self.read_u16_pc();
4034                    let key_reg = self.read_u16_pc();
4035                    let val_reg = self.read_u16_pc();
4036                    let obj_val = self.get_reg(obj_reg);
4037                    let key_val = self.get_reg(key_reg);
4038                    let value = self.get_reg(val_reg);
4039                    if obj_val.is_object_like() && key_val.is_int() {
4040                        let ptr = obj_val.get_ptr();
4041                        let js_obj = unsafe { JSValue::object_from_ptr_mut(ptr) };
4042                        if js_obj.is_dense_array() {
4043                            let idx = key_val.get_int() as usize;
4044                            let arr = unsafe {
4045                                &mut *(ptr as *mut crate::object::array_obj::JSArrayObject)
4046                            };
4047                            if idx < arr.elements.len() {
4048                                arr.elements[idx] = value;
4049                            } else {
4050                                while arr.elements.len() < idx {
4051                                    arr.elements.push(JSValue::undefined());
4052                                }
4053                                arr.elements.push(value);
4054                                let len_atom = ctx.common_atoms.length;
4055
4056                                let new_elements_len = arr.elements.len();
4057                                let old_len = arr
4058                                    .header
4059                                    .get(len_atom)
4060                                    .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
4061                                    .unwrap_or(0);
4062                                let new_len = new_elements_len.max(old_len);
4063                                if new_len != old_len {
4064                                    arr.header.set_length_ic(
4065                                        len_atom,
4066                                        JSValue::new_int(new_len as i64),
4067                                        ctx.shape_cache_mut(),
4068                                    );
4069                                }
4070                            }
4071                        } else {
4072                            let idx = key_val.get_int();
4073                            if idx >= 0 && js_obj.maybe_set_indexed(idx as usize, value) {
4074                            } else {
4075                                let atom = self.int_atom(idx as usize, ctx);
4076                                js_obj.set_cached(atom, value, ctx.shape_cache_mut());
4077                            }
4078                        }
4079                    } else if obj_val.is_object_like() && key_val.is_string() {
4080                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4081                        let atom = key_val.get_atom();
4082
4083                        let setter = js_obj.get_own_accessor_entry(atom).and_then(|e| e.set);
4084                        if let Some(s) = setter {
4085                            if s.is_function() {
4086                                match self.call_function_with_this(ctx, s, obj_val, &[value]) {
4087                                    Ok(_) => {}
4088                                    Err(_) => {}
4089                                }
4090                            }
4091                        } else {
4092                            js_obj.set_cached(atom, value, ctx.shape_cache_mut());
4093                        }
4094                    } else if obj_val.is_object_like() && key_val.is_float() {
4095                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4096                        let s = VM::js_to_string(&key_val, ctx);
4097                        let atom = s.get_atom();
4098
4099                        let setter = js_obj.get_own_accessor_entry(atom).and_then(|e| e.set);
4100                        if let Some(s) = setter {
4101                            if s.is_function() {
4102                                match self.call_function_with_this(ctx, s, obj_val, &[value]) {
4103                                    Ok(_) => {}
4104                                    Err(_) => {}
4105                                }
4106                            }
4107                        } else {
4108                            js_obj.set_cached(atom, value, ctx.shape_cache_mut());
4109                        }
4110                    } else if obj_val.is_object_like() && key_val.is_symbol() {
4111                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4112                        let sym_key =
4113                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4114
4115                        let setter = js_obj.get_own_accessor_entry(sym_key).and_then(|e| e.set);
4116                        if let Some(s) = setter {
4117                            if s.is_function() {
4118                                match self.call_function_with_this(ctx, s, obj_val, &[value]) {
4119                                    Ok(_) => {}
4120                                    Err(_) => {}
4121                                }
4122                            }
4123                        } else {
4124                            js_obj.set_cached(sym_key, value, ctx.shape_cache_mut());
4125                        }
4126                    }
4127                    self.set_reg(obj_reg, obj_val);
4128                }
4129                Opcode::GetProp => {
4130                    let dst = self.read_u16_pc();
4131                    let obj_reg = self.read_u16_pc();
4132                    let key_reg = self.read_u16_pc();
4133                    let obj_val = self.get_reg(obj_reg);
4134                    let key_val = self.get_reg(key_reg);
4135
4136                    if !obj_val.is_object_like()
4137                        && !obj_val.is_function()
4138                        && (obj_val.is_int()
4139                            || obj_val.is_float()
4140                            || obj_val.is_string()
4141                            || obj_val.is_bool())
4142                        && key_val.is_string()
4143                    {
4144                        let atom = key_val.get_atom();
4145                        let start_proto = if obj_val.is_string() {
4146                            ctx.get_string_prototype()
4147                        } else if obj_val.is_int() || obj_val.is_float() {
4148                            ctx.get_number_prototype()
4149                        } else {
4150                            ctx.get_object_prototype()
4151                        };
4152                        let mut current = start_proto;
4153                        let mut found = false;
4154                        while let Some(ptr) = current {
4155                            let pobj = unsafe { &*ptr };
4156                            if let Some(getter) = pobj.get_own_accessor_value(atom) {
4157                                if getter.is_function() {
4158                                    let r = self
4159                                        .call_function_with_this(ctx, getter, obj_val, &[])
4160                                        .unwrap_or(JSValue::undefined());
4161                                    self.set_reg(dst, r);
4162                                } else {
4163                                    self.set_reg(dst, JSValue::undefined());
4164                                }
4165                                found = true;
4166                                break;
4167                            }
4168                            if let Some(val) = pobj.get_own(atom) {
4169                                self.set_reg(dst, val);
4170                                found = true;
4171                                break;
4172                            }
4173                            current = pobj.prototype;
4174                        }
4175                        if !found {
4176                            self.set_reg(dst, JSValue::undefined());
4177                        }
4178                        continue;
4179                    }
4180                    let result = if key_val.is_string() {
4181                        if let Some(result) = self.get_named_prop_result(
4182                            ctx,
4183                            dst,
4184                            obj_val,
4185                            key_val.get_atom(),
4186                            instr_pc,
4187                        ) {
4188                            result
4189                        } else {
4190                            continue;
4191                        }
4192                    } else if key_val.is_symbol() {
4193                        if obj_val.is_object_like() {
4194                            let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4195                            let sym_key =
4196                                crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4197                            js_obj.get(sym_key).unwrap_or(JSValue::undefined())
4198                        } else {
4199                            JSValue::undefined()
4200                        }
4201                    } else if key_val.is_int() && obj_val.is_object_like() {
4202                        if obj_val.is_object() {
4203                            let idx = key_val.get_int();
4204                            let js_obj_check =
4205                                unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4206                            if js_obj_check.is_mapped_arguments() {
4207                                let fi = js_obj_check.mapped_args_frame_index();
4208                                let param_count = js_obj_check.mapped_args_param_count();
4209                                if idx >= 0 && fi < self.frames.len() {
4210                                    let idx_u = idx as usize;
4211                                    if (idx as u32) < param_count {
4212                                        let base = self.frames[fi].registers_base;
4213                                        let reg_idx = base + 1 + idx_u;
4214                                        if reg_idx < self.registers.len() {
4215                                            self.set_reg(dst, self.registers[reg_idx]);
4216                                            continue;
4217                                        }
4218                                    } else {
4219                                        let saved = &self.frames[fi].saved_args;
4220                                        if idx_u < saved.len() {
4221                                            self.set_reg(dst, saved[idx_u]);
4222                                            continue;
4223                                        }
4224                                    }
4225                                }
4226                            }
4227
4228                            if idx >= 0 {
4229                                if let Some(val) = js_obj_check.get_indexed(idx as usize) {
4230                                    self.set_reg(dst, val);
4231                                    continue;
4232                                }
4233                            }
4234                        }
4235                        let atom = self.int_atom(key_val.get_int() as usize, ctx);
4236                        if let Some(result) =
4237                            self.get_named_prop_result(ctx, dst, obj_val, atom, instr_pc)
4238                        {
4239                            result
4240                        } else {
4241                            continue;
4242                        }
4243                    } else if key_val.is_float() && obj_val.is_object_like() {
4244                        let s = VM::js_to_string(&key_val, ctx);
4245                        let atom = s.get_atom();
4246                        if let Some(result) =
4247                            self.get_named_prop_result(ctx, dst, obj_val, atom, instr_pc)
4248                        {
4249                            result
4250                        } else {
4251                            continue;
4252                        }
4253                    } else {
4254                        JSValue::undefined()
4255                    };
4256                    self.set_reg(dst, result);
4257                }
4258                Opcode::GetNamedProp => {
4259                    let dst = self.read_u16_pc();
4260                    let obj_reg = self.read_u16_pc();
4261                    let atom = crate::runtime::atom::Atom(self.read_u32_pc());
4262                    if !self.get_named_prop_fast(ctx, dst, obj_reg, atom, instr_pc) {
4263                        if let Some(exc) = self.pending_throw.take() {
4264                            match self.dispatch_throw_value(ctx, exc) {
4265                                ThrowDispatch::Caught => continue,
4266                                ThrowDispatch::Uncaught(e) => return Err(e),
4267                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4268                            }
4269                        }
4270                        continue;
4271                    }
4272                }
4273                Opcode::SetProp => {
4274                    let obj_reg = self.read_u16_pc();
4275                    let key_reg = self.read_u16_pc();
4276                    let val_reg = self.read_u16_pc();
4277                    let obj_val = self.get_reg(obj_reg);
4278                    let key_val = self.get_reg(key_reg);
4279                    let value = self.get_reg(val_reg);
4280
4281                    if key_val.is_string() {
4282                        self.set_named_prop(ctx, obj_val, value, key_val.get_atom(), usize::MAX);
4283                    } else if key_val.is_symbol() && obj_val.is_object_like() {
4284                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
4285                        let sym_key =
4286                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4287                        js_obj.set_cached(sym_key, value, ctx.shape_cache_mut());
4288                    } else if key_val.is_int() && obj_val.is_object_like() {
4289                        if obj_val.is_object() {
4290                            let js_obj_check =
4291                                unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
4292                            if js_obj_check.is_mapped_arguments() {
4293                                let fi = js_obj_check.mapped_args_frame_index();
4294                                let param_count = js_obj_check.mapped_args_param_count();
4295                                let idx = key_val.get_int();
4296                                if idx >= 0 && (idx as u32) < param_count && fi < self.frames.len()
4297                                {
4298                                    let base = self.frames[fi].registers_base;
4299                                    let reg_idx = base + 1 + idx as usize;
4300                                    if reg_idx < self.registers.len() {
4301                                        self.registers[reg_idx] = value;
4302                                    }
4303                                }
4304                            }
4305                        }
4306                        let atom = self.int_atom(key_val.get_int() as usize, ctx);
4307                        self.set_named_prop(ctx, obj_val, value, atom, usize::MAX);
4308                    } else if key_val.is_float() && obj_val.is_object_like() {
4309                        let s = VM::js_to_string(&key_val, ctx);
4310                        let atom = s.get_atom();
4311                        self.set_named_prop(ctx, obj_val, value, atom, usize::MAX);
4312                    }
4313                    self.set_reg(obj_reg, obj_val);
4314                    if let Some(exc) = self.pending_throw.take() {
4315                        match self.dispatch_throw_value(ctx, exc) {
4316                            ThrowDispatch::Caught => continue,
4317                            ThrowDispatch::Uncaught(e) => return Err(e),
4318                            ThrowDispatch::AsyncComplete(_) => continue,
4319                        }
4320                    }
4321                }
4322                Opcode::SetNamedProp => {
4323                    let obj_reg = self.read_u16_pc();
4324                    let val_reg = self.read_u16_pc();
4325                    let atom = crate::runtime::atom::Atom(self.read_u32_pc());
4326                    self.set_named_prop_fast(ctx, obj_reg, val_reg, atom, instr_pc);
4327                    if let Some(exc) = self.pending_throw.take() {
4328                        match self.dispatch_throw_value(ctx, exc) {
4329                            ThrowDispatch::Caught => continue,
4330                            ThrowDispatch::Uncaught(e) => return Err(e),
4331                            ThrowDispatch::AsyncComplete(_) => continue,
4332                        }
4333                    }
4334                }
4335
4336                Opcode::LtJumpIfNot => {
4337                    let a_reg = self.read_u16_pc();
4338                    let a = self.get_reg(a_reg);
4339                    let b_reg = self.read_u16_pc();
4340                    let b = self.get_reg(b_reg);
4341                    let offset = self.read_i32_pc();
4342                    let cmp = if JSValue::both_int(&a, &b) {
4343                        a.get_int() < b.get_int()
4344                    } else if a.is_float() && b.is_float() {
4345                        a.get_float() < b.get_float()
4346                    } else if a.is_bigint() && b.is_bigint() {
4347                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4348                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4349                        a_int < b_int
4350                    } else {
4351                        a.to_number() < b.to_number()
4352                    };
4353                    if !cmp {
4354                        self.pc = (self.pc as i64 + offset as i64) as usize;
4355                    }
4356                }
4357                Opcode::LtJumpIf => {
4358                    let a_reg = self.read_u16_pc();
4359                    let a = self.get_reg(a_reg);
4360                    let b_reg = self.read_u16_pc();
4361                    let b = self.get_reg(b_reg);
4362                    let offset = self.read_i32_pc();
4363                    let cmp = if JSValue::both_int(&a, &b) {
4364                        a.get_int() < b.get_int()
4365                    } else if a.is_float() && b.is_float() {
4366                        a.get_float() < b.get_float()
4367                    } else if a.is_bigint() && b.is_bigint() {
4368                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4369                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4370                        a_int < b_int
4371                    } else {
4372                        a.to_number() < b.to_number()
4373                    };
4374                    if cmp {
4375                        self.pc = (self.pc as i64 + offset as i64) as usize;
4376                    }
4377                }
4378                Opcode::LteJumpIfNot => {
4379                    let a_reg = self.read_u16_pc();
4380                    let a = self.get_reg(a_reg);
4381                    let b_reg = self.read_u16_pc();
4382                    let b = self.get_reg(b_reg);
4383                    let offset = self.read_i32_pc();
4384                    let cmp = if JSValue::both_int(&a, &b) {
4385                        a.get_int() <= b.get_int()
4386                    } else if a.is_float() && b.is_float() {
4387                        a.get_float() <= b.get_float()
4388                    } else if a.is_bigint() && b.is_bigint() {
4389                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4390                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4391                        a_int <= b_int
4392                    } else {
4393                        a.to_number() <= b.to_number()
4394                    };
4395                    if !cmp {
4396                        self.pc = (self.pc as i64 + offset as i64) as usize;
4397                    }
4398                }
4399                Opcode::LteJumpIf => {
4400                    let a_reg = self.read_u16_pc();
4401                    let a = self.get_reg(a_reg);
4402                    let b_reg = self.read_u16_pc();
4403                    let b = self.get_reg(b_reg);
4404                    let offset = self.read_i32_pc();
4405                    let cmp = if JSValue::both_int(&a, &b) {
4406                        a.get_int() <= b.get_int()
4407                    } else if a.is_float() && b.is_float() {
4408                        a.get_float() <= b.get_float()
4409                    } else if a.is_bigint() && b.is_bigint() {
4410                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4411                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4412                        a_int <= b_int
4413                    } else {
4414                        a.to_number() <= b.to_number()
4415                    };
4416                    if cmp {
4417                        self.pc = (self.pc as i64 + offset as i64) as usize;
4418                    }
4419                }
4420                Opcode::GtJumpIfNot => {
4421                    let a_reg = self.read_u16_pc();
4422                    let a = self.get_reg(a_reg);
4423                    let b_reg = self.read_u16_pc();
4424                    let b = self.get_reg(b_reg);
4425                    let offset = self.read_i32_pc();
4426                    let cmp = if JSValue::both_int(&a, &b) {
4427                        a.get_int() > b.get_int()
4428                    } else if a.is_float() && b.is_float() {
4429                        a.get_float() > b.get_float()
4430                    } else if a.is_bigint() && b.is_bigint() {
4431                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4432                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4433                        a_int > b_int
4434                    } else {
4435                        a.to_number() > b.to_number()
4436                    };
4437                    if !cmp {
4438                        self.pc = (self.pc as i64 + offset as i64) as usize;
4439                    }
4440                }
4441                Opcode::GtJumpIf => {
4442                    let a_reg = self.read_u16_pc();
4443                    let a = self.get_reg(a_reg);
4444                    let b_reg = self.read_u16_pc();
4445                    let b = self.get_reg(b_reg);
4446                    let offset = self.read_i32_pc();
4447                    let cmp = if JSValue::both_int(&a, &b) {
4448                        a.get_int() > b.get_int()
4449                    } else if a.is_float() && b.is_float() {
4450                        a.get_float() > b.get_float()
4451                    } else if a.is_bigint() && b.is_bigint() {
4452                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4453                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4454                        a_int > b_int
4455                    } else {
4456                        a.to_number() > b.to_number()
4457                    };
4458                    if cmp {
4459                        self.pc = (self.pc as i64 + offset as i64) as usize;
4460                    }
4461                }
4462                Opcode::GteJumpIfNot => {
4463                    let a_reg = self.read_u16_pc();
4464                    let a = self.get_reg(a_reg);
4465                    let b_reg = self.read_u16_pc();
4466                    let b = self.get_reg(b_reg);
4467                    let offset = self.read_i32_pc();
4468                    let cmp = if JSValue::both_int(&a, &b) {
4469                        a.get_int() >= b.get_int()
4470                    } else if a.is_float() && b.is_float() {
4471                        a.get_float() >= b.get_float()
4472                    } else if a.is_bigint() && b.is_bigint() {
4473                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4474                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4475                        a_int >= b_int
4476                    } else {
4477                        a.to_number() >= b.to_number()
4478                    };
4479                    if !cmp {
4480                        self.pc = (self.pc as i64 + offset as i64) as usize;
4481                    }
4482                }
4483                Opcode::GteJumpIf => {
4484                    let a_reg = self.read_u16_pc();
4485                    let a = self.get_reg(a_reg);
4486                    let b_reg = self.read_u16_pc();
4487                    let b = self.get_reg(b_reg);
4488                    let offset = self.read_i32_pc();
4489                    let cmp = if JSValue::both_int(&a, &b) {
4490                        a.get_int() >= b.get_int()
4491                    } else if a.is_float() && b.is_float() {
4492                        a.get_float() >= b.get_float()
4493                    } else if a.is_bigint() && b.is_bigint() {
4494                        let a_int = Self::get_bigint_int(&a).unwrap_or(0);
4495                        let b_int = Self::get_bigint_int(&b).unwrap_or(0);
4496                        a_int >= b_int
4497                    } else {
4498                        a.to_number() >= b.to_number()
4499                    };
4500                    if cmp {
4501                        self.pc = (self.pc as i64 + offset as i64) as usize;
4502                    }
4503                }
4504                Opcode::EqJumpIfNot => {
4505                    let a_reg = self.read_u16_pc();
4506                    let a = self.get_reg(a_reg);
4507                    let b_reg = self.read_u16_pc();
4508                    let b = self.get_reg(b_reg);
4509                    let offset = self.read_i32_pc();
4510                    if !loose_equal(ctx, a, b) {
4511                        self.pc = (self.pc as i32 + offset) as usize;
4512                    }
4513                }
4514                Opcode::EqJumpIf => {
4515                    let a_reg = self.read_u16_pc();
4516                    let a = self.get_reg(a_reg);
4517                    let b_reg = self.read_u16_pc();
4518                    let b = self.get_reg(b_reg);
4519                    let offset = self.read_i32_pc();
4520                    if loose_equal(ctx, a, b) {
4521                        self.pc = (self.pc as i32 + offset) as usize;
4522                    }
4523                }
4524                Opcode::NeqJumpIfNot => {
4525                    let a_reg = self.read_u16_pc();
4526                    let a = self.get_reg(a_reg);
4527                    let b_reg = self.read_u16_pc();
4528                    let b = self.get_reg(b_reg);
4529                    let offset = self.read_i32_pc();
4530                    if loose_equal(ctx, a, b) {
4531                        self.pc = (self.pc as i32 + offset) as usize;
4532                    }
4533                }
4534                Opcode::NeqJumpIf => {
4535                    let a_reg = self.read_u16_pc();
4536                    let a = self.get_reg(a_reg);
4537                    let b_reg = self.read_u16_pc();
4538                    let b = self.get_reg(b_reg);
4539                    let offset = self.read_i32_pc();
4540                    if !loose_equal(ctx, a, b) {
4541                        self.pc = (self.pc as i32 + offset) as usize;
4542                    }
4543                }
4544                Opcode::StrictEqJumpIfNot => {
4545                    let a_reg = self.read_u16_pc();
4546                    let a = self.get_reg(a_reg);
4547                    let b_reg = self.read_u16_pc();
4548                    let b = self.get_reg(b_reg);
4549                    let offset = self.read_i32_pc();
4550                    if !a.strict_eq(&b) {
4551                        self.pc = (self.pc as i32 + offset) as usize;
4552                    }
4553                }
4554                Opcode::StrictEqJumpIf => {
4555                    let a_reg = self.read_u16_pc();
4556                    let a = self.get_reg(a_reg);
4557                    let b_reg = self.read_u16_pc();
4558                    let b = self.get_reg(b_reg);
4559                    let offset = self.read_i32_pc();
4560                    if a.strict_eq(&b) {
4561                        self.pc = (self.pc as i32 + offset) as usize;
4562                    }
4563                }
4564                Opcode::StrictNeqJumpIfNot => {
4565                    let a_reg = self.read_u16_pc();
4566                    let a = self.get_reg(a_reg);
4567                    let b_reg = self.read_u16_pc();
4568                    let b = self.get_reg(b_reg);
4569                    let offset = self.read_i32_pc();
4570                    if a.strict_eq(&b) {
4571                        self.pc = (self.pc as i32 + offset) as usize;
4572                    }
4573                }
4574                Opcode::StrictNeqJumpIf => {
4575                    let a_reg = self.read_u16_pc();
4576                    let a = self.get_reg(a_reg);
4577                    let b_reg = self.read_u16_pc();
4578                    let b = self.get_reg(b_reg);
4579                    let offset = self.read_i32_pc();
4580                    if !a.strict_eq(&b) {
4581                        self.pc = (self.pc as i32 + offset) as usize;
4582                    }
4583                }
4584
4585                Opcode::DeleteProp => {
4586                    let dst = self.read_u16_pc();
4587                    let obj_reg = self.read_u16_pc();
4588                    let key_reg = self.read_u16_pc();
4589                    let obj_val = self.get_reg(obj_reg);
4590                    let key_val = self.get_reg(key_reg);
4591                    if obj_val.is_null() || obj_val.is_undefined() {
4592                        self.set_pending_type_error(
4593                            ctx,
4594                            "Cannot delete property from null or undefined",
4595                        );
4596                        if let Some(exc) = self.pending_throw.take() {
4597                            match self.dispatch_throw_value(ctx, exc) {
4598                                ThrowDispatch::Caught => {}
4599                                ThrowDispatch::Uncaught(e) => return Err(e),
4600                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4601                            }
4602                        }
4603                        continue;
4604                    }
4605                    let atom = if key_val.is_string() {
4606                        Some(key_val.get_atom())
4607                    } else if key_val.is_int() && (obj_val.is_object() || obj_val.is_function()) {
4608                        Some(self.int_atom(key_val.get_int() as usize, ctx))
4609                    } else if key_val.is_float() && (obj_val.is_object() || obj_val.is_function()) {
4610                        let s = VM::js_to_string(&key_val, ctx);
4611                        Some(s.get_atom())
4612                    } else {
4613                        None
4614                    };
4615                    let result = if let Some(atom) = atom {
4616                        if obj_val.is_object() || obj_val.is_function() {
4617                            let js_obj = obj_val.as_object_mut();
4618                            let mut deleted = false;
4619                            if js_obj.is_dense_array() && key_val.is_int() {
4620                                let idx = key_val.get_int();
4621                                if idx >= 0 {
4622                                    let ptr = obj_val.get_ptr();
4623                                    let arr = unsafe {
4624                                        &mut *(ptr as *mut crate::object::array_obj::JSArrayObject)
4625                                    };
4626                                    if (idx as usize) < arr.elements.len() {
4627                                        arr.elements[idx as usize] = JSValue::undefined();
4628                                        deleted = true;
4629                                    }
4630                                }
4631                            }
4632                            if !deleted {
4633                                deleted = js_obj.delete(atom);
4634                            }
4635                            if deleted {
4636                                ctx.runtime_mut().gc_heap_mut().deleted_props_count += 1;
4637                            }
4638                            deleted
4639                        } else {
4640                            true
4641                        }
4642                    } else {
4643                        true
4644                    };
4645                    let is_strict = self.frames[self.frame_index].is_strict_frame;
4646                    if is_strict && !result {
4647                        self.set_pending_type_error(ctx, "Cannot delete property");
4648                        if let Some(exc) = self.pending_throw.take() {
4649                            match self.dispatch_throw_value(ctx, exc) {
4650                                ThrowDispatch::Caught => {}
4651                                ThrowDispatch::Uncaught(e) => return Err(e),
4652                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4653                            }
4654                        }
4655                        continue;
4656                    }
4657                    self.set_reg(dst, JSValue::bool(result));
4658                }
4659                Opcode::HasProperty => {
4660                    let dst = self.read_u16_pc();
4661                    let obj_reg = self.read_u16_pc();
4662                    let key_reg = self.read_u16_pc();
4663                    let obj_val = self.get_reg(obj_reg);
4664                    let key_val = self.get_reg(key_reg);
4665                    let result = if (obj_val.is_object() || obj_val.is_function())
4666                        && key_val.is_string()
4667                    {
4668                        let js_obj = obj_val.as_object();
4669                        let atom = key_val.get_atom();
4670                        js_obj.has_property(atom)
4671                    } else if (obj_val.is_object() || obj_val.is_function()) && key_val.is_int() {
4672                        let js_obj = obj_val.as_object();
4673                        let atom = self.int_atom(key_val.get_int() as usize, ctx);
4674                        js_obj.has_property(atom)
4675                    } else if (obj_val.is_object() || obj_val.is_function()) && key_val.is_float() {
4676                        let js_obj = obj_val.as_object();
4677                        let s = VM::js_to_string(&key_val, ctx);
4678                        let atom = s.get_atom();
4679                        js_obj.has_property(atom)
4680                    } else if (obj_val.is_object() || obj_val.is_function()) && key_val.is_symbol()
4681                    {
4682                        let js_obj = obj_val.as_object();
4683                        let sym_key =
4684                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id());
4685                        js_obj.has_property(sym_key)
4686                    } else {
4687                        false
4688                    };
4689                    self.set_reg(dst, JSValue::bool(result));
4690                }
4691                Opcode::InstanceOf => {
4692                    let dst = self.read_u16_pc();
4693                    let obj_reg = self.read_u16_pc();
4694                    let ctor_reg = self.read_u16_pc();
4695                    let obj_val = self.get_reg(obj_reg);
4696                    let ctor_val = self.get_reg(ctor_reg);
4697                    if !ctor_val.is_object() && !ctor_val.is_function() {
4698                        self.set_pending_type_error(
4699                            ctx,
4700                            "Right-hand side of 'instanceof' is not an object",
4701                        );
4702                        if let Some(exc) = self.pending_throw.take() {
4703                            match self.dispatch_throw_value(ctx, exc) {
4704                                ThrowDispatch::Caught => {
4705                                    self.set_reg(dst, JSValue::undefined());
4706                                }
4707                                ThrowDispatch::Uncaught(e) => return Err(e),
4708                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4709                            }
4710                        }
4711                        continue;
4712                    }
4713
4714                    let hi_atom = if self.cached_has_instance_atom.0 != 0 {
4715                        self.cached_has_instance_atom
4716                    } else {
4717                        let sym = crate::builtins::symbol::get_symbol_has_instance(ctx);
4718                        let a = if sym.is_symbol() {
4719                            crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
4720                        } else {
4721                            unreachable!()
4722                        };
4723                        self.cached_has_instance_atom = a;
4724                        a
4725                    };
4726                    let has_instance_handler = if ctor_val.is_function() {
4727                        let jf = ctor_val.as_function();
4728
4729                        if jf.has_symbol_on_base() {
4730                            jf.base.get_own(hi_atom)
4731                        } else {
4732                            None
4733                        }
4734                    } else {
4735                        ctor_val.as_object().get_own(hi_atom)
4736                    };
4737                    let has_instance_handler = has_instance_handler;
4738                    if let Some(handler) = has_instance_handler {
4739                        if handler.is_function() || handler.is_object() {
4740                            if handler.is_function() {
4741                                match self.call_function_with_this(
4742                                    ctx,
4743                                    handler,
4744                                    ctor_val,
4745                                    &[obj_val],
4746                                ) {
4747                                    Ok(v) => {
4748                                        self.set_reg(dst, JSValue::bool(v.is_truthy()));
4749                                        continue;
4750                                    }
4751                                    Err(_) => {
4752                                        self.set_reg(dst, JSValue::undefined());
4753                                        continue;
4754                                    }
4755                                }
4756                            }
4757                        }
4758                    }
4759                    if !ctor_val.is_function() {
4760                        self.set_pending_type_error(
4761                            ctx,
4762                            "Right-hand side of 'instanceof' is not callable",
4763                        );
4764                        if let Some(exc) = self.pending_throw.take() {
4765                            match self.dispatch_throw_value(ctx, exc) {
4766                                ThrowDispatch::Caught => {
4767                                    self.set_reg(dst, JSValue::undefined());
4768                                }
4769                                ThrowDispatch::Uncaught(e) => return Err(e),
4770                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
4771                            }
4772                        }
4773                        continue;
4774                    }
4775
4776                    let result = if obj_val.is_object() || obj_val.is_function() {
4777                        let ctor_proto_opt = if ctor_val.is_function() {
4778                            let js_func = ctor_val.as_function();
4779                            if !js_func.cached_prototype_ptr.is_null() {
4780                                Some(
4781                                    js_func.cached_prototype_ptr
4782                                        as *const crate::object::object::JSObject,
4783                                )
4784                            } else {
4785                                let proto_atom = ctx.common_atoms.prototype;
4786                                js_func.base.get(proto_atom).and_then(|v| {
4787                                    if v.is_object() {
4788                                        let ptr =
4789                                            v.get_ptr() as *mut crate::object::object::JSObject;
4790
4791                                        ctor_val.as_function_mut().cached_prototype_ptr = ptr;
4792                                        Some(ptr as *const crate::object::object::JSObject)
4793                                    } else {
4794                                        None
4795                                    }
4796                                })
4797                            }
4798                        } else {
4799                            None
4800                        };
4801                        if let Some(ctor_proto_ptr) = ctor_proto_opt {
4802                            let obj_ptr = obj_val.get_ptr();
4803                            let mut proto_opt = unsafe {
4804                                (*(obj_ptr as *const crate::object::object::JSObject)).prototype
4805                            };
4806                            let mut found = false;
4807                            let mut limit = 0;
4808                            while let Some(proto_ptr) = proto_opt {
4809                                if std::ptr::eq(proto_ptr, ctor_proto_ptr) {
4810                                    found = true;
4811                                    break;
4812                                }
4813                                proto_opt = unsafe { (*proto_ptr).prototype };
4814                                limit += 1;
4815                                if limit > 1000 {
4816                                    break;
4817                                }
4818                            }
4819                            found
4820                        } else {
4821                            false
4822                        }
4823                    } else {
4824                        false
4825                    };
4826                    self.set_reg(dst, JSValue::bool(result));
4827                }
4828                Opcode::NewRegExp => {
4829                    let cache_key = self.cached_code_ptr as usize + self.pc;
4830                    let dst = self.read_u16_pc();
4831                    let pattern_idx = self.read_u32() as usize;
4832                    let flags_idx = self.read_u32() as usize;
4833                    let constants_len = self.frames[self.frame_index].constants_len;
4834                    let pattern_val = if pattern_idx < constants_len {
4835                        unsafe { *self.cached_const_ptr.add(pattern_idx) }
4836                    } else {
4837                        JSValue::undefined()
4838                    };
4839                    let flags_val = if flags_idx < constants_len {
4840                        unsafe { *self.cached_const_ptr.add(flags_idx) }
4841                    } else {
4842                        JSValue::undefined()
4843                    };
4844                    let pattern = if pattern_val.is_string() {
4845                        ctx.get_atom_str(pattern_val.get_atom()).to_string()
4846                    } else {
4847                        String::new()
4848                    };
4849                    let flags = if flags_val.is_string() {
4850                        ctx.get_atom_str(flags_val.get_atom()).to_string()
4851                    } else {
4852                        String::new()
4853                    };
4854
4855                    if let Some(cached_re) = self.regex_lit_cache.get(&cache_key) {
4856                        let cloned_re = cached_re.clone();
4857                        let re_val = crate::builtins::regexp::create_regexp_object_precompiled(
4858                            ctx, &pattern, &flags, cloned_re,
4859                        );
4860                        self.set_reg(dst, re_val);
4861                    } else {
4862                        let pattern_atom = ctx.intern(&pattern);
4863                        let flags_atom = ctx.intern(&flags);
4864                        let re_val = crate::builtins::regexp::regexp_constructor(
4865                            ctx,
4866                            &[
4867                                JSValue::new_string(pattern_atom),
4868                                JSValue::new_string(flags_atom),
4869                            ],
4870                        );
4871
4872                        if re_val.is_object() {
4873                            if let Some(compiled) = re_val.as_object().get_compiled_regex() {
4874                                self.regex_lit_cache.insert(cache_key, compiled.clone());
4875                            }
4876                        }
4877                        self.set_reg(dst, re_val);
4878                    }
4879                }
4880                Opcode::GetPrivate => {
4881                    let dst = self.read_u16_pc();
4882                    let obj_reg = self.read_u16_pc();
4883                    let key_reg = self.read_u16_pc();
4884                    let obj_val = self.get_reg(obj_reg);
4885                    let key_val = self.get_reg(key_reg);
4886                    let result = if (obj_val.is_object() || obj_val.is_function())
4887                        && key_val.is_string()
4888                    {
4889                        let js_obj = obj_val.as_object();
4890                        let atom = key_val.get_atom();
4891                        let atom_str = ctx.get_atom_str(atom).to_string();
4892                        let alt_atom = if let Some(stripped) = atom_str.strip_prefix('#') {
4893                            Some(ctx.intern(stripped))
4894                        } else {
4895                            None
4896                        };
4897
4898                        let own_accessor =
4899                            js_obj.get_own_private_accessor_entry(atom).or_else(|| {
4900                                alt_atom.and_then(|a| js_obj.get_own_private_accessor_entry(a))
4901                            });
4902                        if let Some(entry) = own_accessor {
4903                            if let Some(getter) = entry.get {
4904                                self.call_function_with_this(ctx, getter, obj_val, &[])
4905                                    .unwrap_or(JSValue::undefined())
4906                            } else {
4907                                JSValue::undefined()
4908                            }
4909                        } else if let Some(val) = js_obj
4910                            .get_private_field(atom)
4911                            .or_else(|| alt_atom.and_then(|a| js_obj.get_private_field(a)))
4912                        {
4913                            val
4914                        } else {
4915                            let mut getter_fn: Option<JSValue> = None;
4916                            let mut inherited_value: Option<JSValue> = None;
4917                            let mut cur = js_obj.prototype;
4918                            let mut depth = 0u32;
4919                            while let Some(p) = cur {
4920                                if p.is_null() || depth > 100 {
4921                                    break;
4922                                }
4923                                let proto_obj = unsafe { &*p };
4924                                let accessor_entry =
4925                                    proto_obj.get_own_private_accessor_entry(atom).or_else(|| {
4926                                        alt_atom.and_then(|a| {
4927                                            proto_obj.get_own_private_accessor_entry(a)
4928                                        })
4929                                    });
4930                                if let Some(entry) = accessor_entry {
4931                                    getter_fn = entry.get;
4932                                    break;
4933                                }
4934                                if let Some(v) = proto_obj.get_private_field(atom).or_else(|| {
4935                                    alt_atom.and_then(|a| proto_obj.get_private_field(a))
4936                                }) {
4937                                    inherited_value = Some(v);
4938                                    break;
4939                                }
4940                                cur = proto_obj.prototype;
4941                                depth += 1;
4942                            }
4943                            if let Some(getter) = getter_fn {
4944                                self.call_function_with_this(ctx, getter, obj_val, &[])
4945                                    .unwrap_or(JSValue::undefined())
4946                            } else if let Some(v) = inherited_value {
4947                                v
4948                            } else {
4949                                JSValue::undefined()
4950                            }
4951                        }
4952                    } else {
4953                        JSValue::undefined()
4954                    };
4955                    self.set_reg(dst, result);
4956                }
4957                Opcode::SetPrivate => {
4958                    let obj_reg = self.read_u16_pc();
4959                    let key_reg = self.read_u16_pc();
4960                    let val_reg = self.read_u16_pc();
4961                    let obj_val = self.get_reg(obj_reg);
4962                    let key_val = self.get_reg(key_reg);
4963                    let val = self.get_reg(val_reg);
4964                    if (obj_val.is_object() || obj_val.is_function()) && key_val.is_string() {
4965                        let atom = key_val.get_atom();
4966                        let atom_str = ctx.get_atom_str(atom).to_string();
4967                        let alt_atom = if let Some(stripped) = atom_str.strip_prefix('#') {
4968                            Some(ctx.intern(stripped))
4969                        } else {
4970                            None
4971                        };
4972
4973                        let own_accessor_entry = {
4974                            let js_obj = obj_val.as_object();
4975                            js_obj
4976                                .get_own_private_accessor_entry(atom)
4977                                .or_else(|| {
4978                                    alt_atom.and_then(|a| js_obj.get_own_private_accessor_entry(a))
4979                                })
4980                                .cloned()
4981                        };
4982                        if let Some(entry) = own_accessor_entry {
4983                            if let Some(setter) = entry.set {
4984                                let _ = self.call_function_with_this(ctx, setter, obj_val, &[val]);
4985                            } else {
4986                                self.set_pending_type_error(
4987                                    ctx,
4988                                    "Cannot set private property without setter",
4989                                );
4990                                if let Some(exc) = self.pending_throw.take() {
4991                                    match self.dispatch_throw_value(ctx, exc) {
4992                                        ThrowDispatch::Caught => continue,
4993                                        ThrowDispatch::Uncaught(e) => return Err(e),
4994                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
4995                                    }
4996                                }
4997                            }
4998                        } else {
4999                            let mut setter_fn: Option<JSValue> = None;
5000                            let mut accessor_found = false;
5001                            {
5002                                let js_obj = obj_val.as_object();
5003                                let mut cur = js_obj.prototype;
5004                                let mut depth = 0u32;
5005                                while let Some(p) = cur {
5006                                    if p.is_null() || depth > 100 {
5007                                        break;
5008                                    }
5009                                    let proto_obj = unsafe { &*p };
5010                                    let accessor_entry = proto_obj
5011                                        .get_own_private_accessor_entry(atom)
5012                                        .or_else(|| {
5013                                            alt_atom.and_then(|a| {
5014                                                proto_obj.get_own_private_accessor_entry(a)
5015                                            })
5016                                        });
5017                                    if let Some(entry) = accessor_entry {
5018                                        accessor_found = true;
5019                                        setter_fn = entry.set;
5020                                        break;
5021                                    }
5022                                    cur = proto_obj.prototype;
5023                                    depth += 1;
5024                                }
5025                            }
5026                            if let Some(setter) = setter_fn {
5027                                let _ = self.call_function_with_this(ctx, setter, obj_val, &[val]);
5028                            } else if accessor_found {
5029                                self.set_pending_type_error(
5030                                    ctx,
5031                                    "Cannot set private property without setter",
5032                                );
5033                                if let Some(exc) = self.pending_throw.take() {
5034                                    match self.dispatch_throw_value(ctx, exc) {
5035                                        ThrowDispatch::Caught => continue,
5036                                        ThrowDispatch::Uncaught(e) => return Err(e),
5037                                        ThrowDispatch::AsyncComplete(o) => return Ok(o),
5038                                    }
5039                                }
5040                            } else {
5041                                let js_obj = obj_val.as_object_mut();
5042                                js_obj.set_private_field(atom, val);
5043                            }
5044                        }
5045                    }
5046                }
5047                Opcode::HasPrivate => {
5048                    let dst = self.read_u16_pc();
5049                    let obj_reg = self.read_u16_pc();
5050                    let key_reg = self.read_u16_pc();
5051                    let obj_val = self.get_reg(obj_reg);
5052                    let key_val = self.get_reg(key_reg);
5053                    let result =
5054                        if (obj_val.is_object() || obj_val.is_function()) && key_val.is_string() {
5055                            let js_obj = obj_val.as_object();
5056                            let atom = key_val.get_atom();
5057                            js_obj.has_private_field(atom)
5058                        } else {
5059                            false
5060                        };
5061                    self.set_reg(dst, JSValue::bool(result));
5062                }
5063                Opcode::GatherRest => {
5064                    let dst = self.read_u16_pc();
5065                    let base = self.frames[self.frame_index].registers_base;
5066                    let arg_count = self.frames[self.frame_index].arg_count as usize;
5067                    let rest_start = dst as usize;
5068
5069                    let rest_count = if arg_count + 1 > rest_start {
5070                        arg_count + 1 - rest_start
5071                    } else {
5072                        0
5073                    };
5074                    let mut arr =
5075                        crate::object::array_obj::JSArrayObject::with_capacity(rest_count);
5076                    if let Some(proto_ptr) = ctx.get_array_prototype() {
5077                        arr.header.set_prototype_raw(proto_ptr);
5078                    }
5079                    for i in 0..rest_count {
5080                        let val = self.registers[base + rest_start + i];
5081                        arr.push(val);
5082                    }
5083                    let len_atom = ctx.common_atoms.length;
5084                    arr.header
5085                        .set(len_atom, JSValue::new_int(rest_count as i64));
5086                    let ptr = Box::into_raw(Box::new(arr)) as usize;
5087                    ctx.runtime_mut().gc_heap_mut().track_array(ptr);
5088                    self.set_reg(dst, JSValue::new_object(ptr));
5089                }
5090                Opcode::ObjectSpread => {
5091                    let dst = self.read_u16_pc();
5092                    let src = self.read_u16_pc();
5093                    let dst_val = self.get_reg(dst);
5094                    let src_val = self.get_reg(src);
5095                    if (dst_val.is_object() || dst_val.is_function())
5096                        && (src_val.is_object() || src_val.is_function())
5097                    {
5098                        let dst_obj = dst_val.as_object_mut();
5099                        let src_obj = src_val.as_object();
5100
5101                        for (atom, value) in src_obj.own_properties() {
5102                            dst_obj.set_cached(atom, value, ctx.shape_cache_mut());
5103                        }
5104                    }
5105                }
5106                Opcode::GetPropertyNames => {
5107                    let dst = self.read_u16_pc();
5108                    let obj_reg = self.read_u16_pc();
5109                    let obj_val = self.get_reg(obj_reg);
5110                    let mut arr = crate::object::array_obj::JSArrayObject::new();
5111                    if let Some(proto_ptr) = ctx.get_array_prototype() {
5112                        arr.header.set_prototype_raw(proto_ptr);
5113                    }
5114                    if obj_val.is_object() || obj_val.is_function() {
5115                        let js_obj = obj_val.as_object();
5116                        if js_obj.is_dense_array() {
5117                            let arr_obj = unsafe {
5118                                &*(obj_val.get_ptr()
5119                                    as *mut crate::object::array_obj::JSArrayObject)
5120                            };
5121                            for i in 0..arr_obj.elements.len() {
5122                                let idx_atom = ctx.intern(i.to_string().as_str());
5123                                arr.push(JSValue::new_string(idx_atom));
5124                            }
5125                        }
5126                        for (atom, _value) in js_obj.own_properties() {
5127                            arr.push(JSValue::new_string(atom));
5128                        }
5129                    }
5130                    let len_atom = ctx.common_atoms.length;
5131                    arr.header
5132                        .set(len_atom, JSValue::new_int(arr.elements.len() as i64));
5133                    let ptr = Box::into_raw(Box::new(arr)) as usize;
5134                    ctx.runtime_mut().gc_heap_mut().track_array(ptr);
5135                    self.allocation_count += 1;
5136                    self.set_reg(dst, JSValue::new_object(ptr));
5137                    self.maybe_gc(ctx);
5138                }
5139                Opcode::ArrayExtend => {
5140                    let dst = self.read_u16_pc();
5141                    let src = self.read_u16_pc();
5142                    let dst_val = self.get_reg(dst);
5143                    let src_val = self.get_reg(src);
5144                    if dst_val.is_object() && src_val.is_object() {
5145                        let dst_ptr = dst_val.get_ptr();
5146                        let src_ptr = src_val.get_ptr();
5147                        let is_src_array = src_val.as_object().is_dense_array();
5148                        let is_dst_array = dst_val.as_object().is_dense_array();
5149                        if is_src_array && is_dst_array {
5150                            let dst_arr = unsafe {
5151                                &mut *(dst_ptr as *mut crate::object::array_obj::JSArrayObject)
5152                            };
5153                            let src_arr = unsafe {
5154                                &*(src_ptr as *const crate::object::array_obj::JSArrayObject)
5155                            };
5156                            for val in src_arr.elements.iter() {
5157                                dst_arr.push(*val);
5158                            }
5159                            let len_atom = ctx.common_atoms.length;
5160                            dst_arr
5161                                .header
5162                                .set(len_atom, JSValue::new_int(dst_arr.elements.len() as i64));
5163                        } else if is_dst_array {
5164                            let dst_arr = unsafe {
5165                                &mut *(dst_ptr as *mut crate::object::array_obj::JSArrayObject)
5166                            };
5167                            let src_obj =
5168                                unsafe { &*(src_ptr as *const crate::object::object::JSObject) };
5169                            let len_atom = ctx.common_atoms.length;
5170                            let arr_len = src_obj
5171                                .get(len_atom)
5172                                .map(|v| v.get_int() as usize)
5173                                .unwrap_or(0);
5174                            if let Some(elems) = src_obj.get_array_elements() {
5175                                for val in elems.iter() {
5176                                    dst_arr.push(*val);
5177                                }
5178                            } else {
5179                                for i in 0..arr_len {
5180                                    let key = ctx.intern(&i.to_string());
5181                                    if let Some(val) = src_obj.get(key) {
5182                                        dst_arr.push(val);
5183                                    }
5184                                }
5185                            }
5186                            dst_arr
5187                                .header
5188                                .set(len_atom, JSValue::new_int(dst_arr.elements.len() as i64));
5189                        }
5190                    }
5191                }
5192                Opcode::ArrayPush => {
5193                    let arr_reg = self.read_u16_pc();
5194                    let val_reg = self.read_u16_pc();
5195                    let arr_val = self.get_reg(arr_reg);
5196                    let val = self.get_reg(val_reg);
5197                    if arr_val.is_object() {
5198                        let arr_ptr = arr_val.get_ptr();
5199                        let arr = unsafe {
5200                            &mut *(arr_ptr as *mut crate::object::array_obj::JSArrayObject)
5201                        };
5202                        arr.push(val);
5203                        let len_atom = ctx.common_atoms.length;
5204                        let new_len = arr.elements.len() as i64;
5205                        if !arr.header.has_own(len_atom) {
5206                            arr.header.define_property(
5207                                len_atom,
5208                                crate::object::object::PropertyDescriptor {
5209                                    value: Some(JSValue::new_int(new_len)),
5210                                    writable: true,
5211                                    enumerable: false,
5212                                    configurable: false,
5213                                    get: None,
5214                                    set: None,
5215                                },
5216                            );
5217                        } else {
5218                            arr.header.set(len_atom, JSValue::new_int(new_len));
5219                        }
5220                    }
5221                }
5222                Opcode::SetProto => {
5223                    let obj_reg = self.read_u16_pc();
5224                    let proto_reg = self.read_u16_pc();
5225                    let obj_val = self.get_reg(obj_reg);
5226                    let proto_val = self.get_reg(proto_reg);
5227                    if obj_val.is_object() && proto_val.is_object() {
5228                        let obj_ref = obj_val.as_object_mut();
5229                        obj_ref.prototype =
5230                            Some(proto_val.get_ptr() as *mut crate::object::object::JSObject);
5231                    }
5232                }
5233                Opcode::GetSuper => {
5234                    let dst = self.read_u16_pc();
5235                    self.set_reg(dst, self.frames[self.frame_index].super_ctor);
5236                }
5237
5238                Opcode::DefineAccessor => {
5239                    let obj_reg = self.read_u16_pc();
5240                    let key_reg = self.read_u16_pc();
5241                    let getter_reg = self.read_u16_pc();
5242                    let setter_reg = self.read_u16_pc();
5243                    let obj_val = self.get_reg(obj_reg);
5244                    let key_val = self.get_reg(key_reg);
5245                    if obj_val.is_object_like() {
5246                        let prop_key = if key_val.is_string() {
5247                            key_val
5248                        } else {
5249                            let prim = self.ordinary_to_primitive(&key_val, "string", ctx);
5250                            if self.pending_throw.is_some() {
5251                                if let Some(exc) = self.pending_throw.take() {
5252                                    let disp = self.dispatch_throw_value(ctx, exc);
5253                                    match disp {
5254                                        ThrowDispatch::Caught => {}
5255                                        ThrowDispatch::Uncaught(e) => return Err(e),
5256                                        ThrowDispatch::AsyncComplete(o) => match o {
5257                                            ExecutionOutcome::Complete(v) => {
5258                                                self.set_reg(obj_reg, v);
5259                                            }
5260                                            _ => {}
5261                                        },
5262                                    }
5263                                }
5264                                continue;
5265                            }
5266                            if prim.is_string() {
5267                                prim
5268                            } else {
5269                                VM::js_to_string(&prim, ctx)
5270                            }
5271                        };
5272                        if prop_key.is_string() {
5273                            let atom = prop_key.get_atom();
5274                            let obj = obj_val.as_object_mut();
5275                            let getter = if getter_reg != u16::MAX {
5276                                Some(self.get_reg(getter_reg))
5277                            } else {
5278                                None
5279                            };
5280                            let setter = if setter_reg != u16::MAX {
5281                                Some(self.get_reg(setter_reg))
5282                            } else {
5283                                None
5284                            };
5285                            let existing = obj.get_own_accessor_entry(atom);
5286                            let final_getter = getter.or_else(|| existing.and_then(|e| e.get));
5287                            let final_setter = setter.or_else(|| existing.and_then(|e| e.set));
5288                            obj.define_accessor(atom, final_getter, final_setter);
5289                        } else if self.pending_throw.is_none() {
5290                            self.set_pending_type_error(ctx, "Invalid property key");
5291                        }
5292                    }
5293                }
5294
5295                Opcode::DefinePrivateAccessor => {
5296                    let obj_reg = self.read_u16_pc();
5297                    let key_reg = self.read_u16_pc();
5298                    let getter_reg = self.read_u16_pc();
5299                    let setter_reg = self.read_u16_pc();
5300                    let obj_val = self.get_reg(obj_reg);
5301                    let key_val = self.get_reg(key_reg);
5302                    if (obj_val.is_object() || obj_val.is_function()) && key_val.is_string() {
5303                        let atom = key_val.get_atom();
5304                        let obj = obj_val.as_object_mut();
5305                        let getter = if getter_reg != u16::MAX {
5306                            Some(self.get_reg(getter_reg))
5307                        } else {
5308                            None
5309                        };
5310                        let setter = if setter_reg != u16::MAX {
5311                            Some(self.get_reg(setter_reg))
5312                        } else {
5313                            None
5314                        };
5315                        obj.define_private_accessor(atom, getter, setter);
5316                    }
5317                }
5318
5319                Opcode::SetMethodProp => {
5320                    let obj_reg = self.read_u16_pc();
5321                    let key_reg = self.read_u16_pc();
5322                    let val_reg = self.read_u16_pc();
5323                    let obj_val = self.get_reg(obj_reg);
5324                    let key_val = self.get_reg(key_reg);
5325                    let value = self.get_reg(val_reg);
5326                    if obj_val.is_object_like() {
5327                        let js_obj = unsafe { JSValue::object_from_ptr_mut(obj_val.get_ptr()) };
5328                        let atom = if key_val.is_string() {
5329                            key_val.get_atom()
5330                        } else if key_val.is_symbol() {
5331                            crate::runtime::atom::Atom(0x40000000 | key_val.get_symbol_id())
5332                        } else if key_val.is_float() {
5333                            let s = VM::js_to_string(&key_val, ctx);
5334                            s.get_atom()
5335                        } else {
5336                            self.int_atom(key_val.get_int() as usize, ctx)
5337                        };
5338                        js_obj.set_cached_non_enumerable(atom, value, ctx.shape_cache_mut());
5339                    }
5340                    self.set_reg(obj_reg, obj_val);
5341                }
5342
5343                Opcode::CallCurrent1 => {
5344                    unsafe {
5345                        (*self.frames.as_mut_ptr().add(self.frame_index)).current_pc = instr_pc;
5346                    }
5347                    let dst = self.read_u16_pc();
5348                    let arg_reg = self.read_u16_pc();
5349                    let caller = &self.frames[self.frame_index];
5350                    let function_ptr = caller.function_ptr;
5351                    if function_ptr.is_none() {
5352                        self.set_pending_type_error(ctx, "undefined is not a function");
5353                        if let Some(exc) = self.pending_throw.take() {
5354                            match self.dispatch_throw_value(ctx, exc) {
5355                                ThrowDispatch::Caught => continue,
5356                                ThrowDispatch::Uncaught(e) => return Err(e),
5357                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
5358                            }
5359                        }
5360                        continue;
5361                    }
5362                    let one_arg = [arg_reg];
5363                    self.push_frame_from_arg_regs_raw(
5364                        ctx,
5365                        caller.locals_count,
5366                        caller.bytecode_ptr,
5367                        caller.bytecode_len,
5368                        caller.constants_ptr,
5369                        caller.constants_len,
5370                        self.pc,
5371                        function_ptr,
5372                        caller.ic_table_ptr,
5373                        JSValue::undefined(),
5374                        dst,
5375                        1,
5376                        false,
5377                        caller.is_async,
5378                        self.cached_registers_base,
5379                        &one_arg,
5380                        caller.uses_arguments,
5381                    );
5382                    continue;
5383                }
5384
5385                Opcode::Call
5386                | Opcode::Call0
5387                | Opcode::Call1
5388                | Opcode::Call2
5389                | Opcode::Call3
5390                | Opcode::CallMethod
5391                | Opcode::CallNew
5392                | Opcode::CallCurrent => {
5393                    unsafe {
5394                        (*self.frames.as_mut_ptr().add(self.frame_index)).current_pc = instr_pc;
5395                    }
5396                    let is_call_current = op == Opcode::CallCurrent;
5397                    let is_call0 = op == Opcode::Call0;
5398                    let is_call1 = op == Opcode::Call1;
5399                    let is_call2 = op == Opcode::Call2;
5400                    let is_call3 = op == Opcode::Call3;
5401                    let is_call_method = op == Opcode::CallMethod;
5402                    let is_call_new = op == Opcode::CallNew;
5403                    let dst = self.read_u16_pc();
5404                    let obj_reg = if is_call_method {
5405                        self.read_u16_pc()
5406                    } else {
5407                        0
5408                    };
5409                    let func_reg = if is_call_current {
5410                        0
5411                    } else {
5412                        self.read_u16_pc()
5413                    };
5414                    let mut one_arg = [0u16; 1];
5415                    let mut two_args = [0u16; 2];
5416                    let mut three_args = [0u16; 3];
5417                    let argc = if is_call0 {
5418                        0
5419                    } else if is_call1 {
5420                        one_arg[0] = self.read_u16_pc();
5421                        1
5422                    } else if is_call2 {
5423                        two_args[0] = self.read_u16_pc();
5424                        two_args[1] = self.read_u16_pc();
5425                        2
5426                    } else if is_call3 {
5427                        three_args[0] = self.read_u16_pc();
5428                        three_args[1] = self.read_u16_pc();
5429                        three_args[2] = self.read_u16_pc();
5430                        3
5431                    } else {
5432                        self.read_u16_pc()
5433                    };
5434
5435                    let mut arg_regs_buf = [0u16; 16];
5436                    let mut arg_regs_vec = Vec::new();
5437                    let arg_regs: &[u16] = if is_call1 {
5438                        &one_arg
5439                    } else if is_call2 {
5440                        &two_args
5441                    } else if is_call3 {
5442                        &three_args
5443                    } else if argc as usize <= 16 {
5444                        for i in 0..argc {
5445                            arg_regs_buf[i as usize] = self.read_u16_pc();
5446                        }
5447                        &arg_regs_buf[..argc as usize]
5448                    } else {
5449                        arg_regs_vec.reserve(argc as usize);
5450                        for _ in 0..argc {
5451                            arg_regs_vec.push(self.read_u16_pc());
5452                        }
5453                        &arg_regs_vec
5454                    };
5455
5456                    if is_call_current {
5457                        let caller = &self.frames[self.frame_index];
5458                        let function_ptr = caller.function_ptr;
5459                        if function_ptr.is_none() {
5460                            self.set_pending_type_error(ctx, "undefined is not a function");
5461                            if let Some(exc) = self.pending_throw.take() {
5462                                match self.dispatch_throw_value(ctx, exc) {
5463                                    ThrowDispatch::Caught => continue,
5464                                    ThrowDispatch::Uncaught(e) => return Err(e),
5465                                    ThrowDispatch::AsyncComplete(o) => return Ok(o),
5466                                }
5467                            }
5468                            continue;
5469                        }
5470
5471                        self.push_frame_from_arg_regs_raw(
5472                            ctx,
5473                            caller.locals_count,
5474                            caller.bytecode_ptr,
5475                            caller.bytecode_len,
5476                            caller.constants_ptr,
5477                            caller.constants_len,
5478                            self.pc,
5479                            function_ptr,
5480                            caller.ic_table_ptr,
5481                            JSValue::undefined(),
5482                            dst,
5483                            argc,
5484                            false,
5485                            caller.is_async,
5486                            self.cached_registers_base,
5487                            arg_regs,
5488                            caller.uses_arguments,
5489                        );
5490                        continue;
5491                    }
5492
5493                    let func_val = self.get_reg(func_reg);
5494                    let this_val = if is_call_method {
5495                        self.get_reg(obj_reg)
5496                    } else if is_call_new {
5497                        let props = ctx.runtime_mut().gc_heap_mut().take_prop_vec();
5498                        let mut obj = crate::object::object::JSObject::new_typed_from_pool(
5499                            crate::object::object::ObjectType::Ordinary,
5500                            props,
5501                        );
5502                        obj.ensure_shape(ctx.shape_cache_mut());
5503                        if let Some(proto_ptr) = ctx.get_object_prototype() {
5504                            obj.prototype = Some(proto_ptr);
5505                        }
5506                        let func_val = if is_call_current {
5507                            if let Some(ptr) = self.frames[self.frame_index].function_ptr {
5508                                JSValue::new_function(ptr)
5509                            } else {
5510                                JSValue::undefined()
5511                            }
5512                        } else {
5513                            self.get_reg(func_reg)
5514                        };
5515                        if func_val.is_function() {
5516                            let js_func = func_val.as_function();
5517
5518                            if !js_func.cached_prototype_ptr.is_null() {
5519                                obj.prototype = Some(js_func.cached_prototype_ptr);
5520                            } else {
5521                                let proto_key = ctx.common_atoms.prototype;
5522                                if let Some(proto_val) = js_func.base.get(proto_key) {
5523                                    if proto_val.is_object() {
5524                                        let cptr = proto_val.get_ptr()
5525                                            as *mut crate::object::object::JSObject;
5526                                        func_val.as_function_mut().cached_prototype_ptr = cptr;
5527                                        obj.prototype = Some(cptr);
5528                                    }
5529                                } else if !js_func.is_builtin() {
5530                                    let mut pobj = crate::object::object::JSObject::new();
5531                                    pobj.set(ctx.common_atoms.constructor, func_val);
5532                                    if let Some(opp) = ctx.get_object_prototype() {
5533                                        pobj.prototype = Some(opp);
5534                                    }
5535                                    let pp = Box::into_raw(Box::new(pobj)) as usize;
5536                                    ctx.runtime_mut().gc_heap_mut().track(pp);
5537                                    let func_mut = func_val.as_function_mut();
5538                                    func_mut.base.set(proto_key, JSValue::new_object(pp));
5539                                    let cptr = pp as *mut crate::object::object::JSObject;
5540                                    func_mut.cached_prototype_ptr = cptr;
5541                                    obj.prototype = Some(cptr);
5542                                }
5543                            }
5544                        }
5545                        let ptr = if ctx.runtime().gc_heap().nursery_enabled_and_can_fit_object() {
5546                            ctx.runtime_mut()
5547                                .gc_heap_mut()
5548                                .alloc_object_with_value(obj)
5549                                .map(|p| p as usize)
5550                                .unwrap_or_else(|| {
5551                                    let hp = Box::into_raw(Box::new(
5552                                        crate::object::object::JSObject::new(),
5553                                    )) as usize;
5554                                    ctx.runtime_mut().gc_heap_mut().track(hp);
5555                                    hp
5556                                })
5557                        } else {
5558                            let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
5559                            ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
5560                            heap_ptr
5561                        };
5562                        self.allocation_count += 1;
5563                        JSValue::new_object(ptr)
5564                    } else {
5565                        let frame = &self.frames[self.frame_index];
5566                        if frame.is_constructor
5567                            && !frame.super_ctor.is_undefined()
5568                            && func_val.is_function()
5569                            && frame.super_ctor.is_function()
5570                        {
5571                            let sctor_ptr = frame.super_ctor.get_ptr();
5572                            let fptr = func_val.get_ptr();
5573                            if fptr == sctor_ptr {
5574                                frame.this_value
5575                            } else {
5576                                JSValue::undefined()
5577                            }
5578                        } else {
5579                            JSValue::undefined()
5580                        }
5581                    };
5582
5583                    if self.execute_call(
5584                        ctx,
5585                        func_val,
5586                        this_val,
5587                        dst,
5588                        argc,
5589                        arg_regs,
5590                        obj_reg,
5591                        is_call_new,
5592                        is_call_method,
5593                    )? {
5594                        continue;
5595                    }
5596                    if let Some(exc) = self.pending_throw.take() {
5597                        match self.dispatch_throw_value(ctx, exc) {
5598                            ThrowDispatch::Caught => continue,
5599                            ThrowDispatch::Uncaught(e) => return Err(e),
5600                            ThrowDispatch::AsyncComplete(o) => return Ok(o),
5601                        }
5602                    }
5603                }
5604
5605                Opcode::CallNamedMethod => {
5606                    unsafe {
5607                        (*self.frames.as_mut_ptr().add(self.frame_index)).current_pc = instr_pc;
5608                    }
5609                    let dst = self.read_u16_pc();
5610                    let obj_reg = self.read_u16_pc();
5611                    let atom = crate::runtime::atom::Atom(self.read_u32_pc());
5612                    let argc = self.read_u16_pc();
5613
5614                    let mut arg_regs_buf = [0u16; 16];
5615                    let mut arg_regs_vec = Vec::new();
5616                    let arg_regs: &[u16] = if argc as usize <= 16 {
5617                        for i in 0..argc {
5618                            arg_regs_buf[i as usize] = self.read_u16_pc();
5619                        }
5620                        &arg_regs_buf[..argc as usize]
5621                    } else {
5622                        arg_regs_vec.reserve(argc as usize);
5623                        for _ in 0..argc {
5624                            arg_regs_vec.push(self.read_u16_pc());
5625                        }
5626                        &arg_regs_vec
5627                    };
5628
5629                    let obj_val = self.get_reg(obj_reg);
5630                    if !self.get_named_prop_fast(ctx, obj_reg, obj_reg, atom, instr_pc) {}
5631                    let func_val = self.get_reg(obj_reg);
5632
5633                    self.set_reg(obj_reg, obj_val);
5634                    let this_val = obj_val;
5635
5636                    if self.execute_call(
5637                        ctx, func_val, this_val, dst, argc, arg_regs, obj_reg, false, true,
5638                    )? {
5639                        continue;
5640                    }
5641                    if let Some(exc) = self.pending_throw.take() {
5642                        match self.dispatch_throw_value(ctx, exc) {
5643                            ThrowDispatch::Caught => continue,
5644                            ThrowDispatch::Uncaught(e) => return Err(e),
5645                            ThrowDispatch::AsyncComplete(o) => return Ok(o),
5646                        }
5647                    }
5648                }
5649
5650                Opcode::CallSpread | Opcode::CallMethodSpread | Opcode::CallNewSpread => {
5651                    let is_call_method = op == Opcode::CallMethodSpread;
5652                    let is_call_new = op == Opcode::CallNewSpread;
5653                    let dst = self.read_u16_pc();
5654                    let obj_reg = if is_call_method {
5655                        self.read_u16_pc()
5656                    } else {
5657                        0
5658                    };
5659                    let func_reg = self.read_u16_pc();
5660                    let arr_reg = self.read_u16_pc();
5661
5662                    let func_val = self.get_reg(func_reg);
5663                    let arr_val = self.get_reg(arr_reg);
5664                    let mut args = Vec::new();
5665                    if arr_val.is_object() {
5666                        let arr_ptr = arr_val.get_ptr();
5667                        let is_jsarray = arr_val.as_object().is_dense_array();
5668                        if is_jsarray {
5669                            let arr = unsafe {
5670                                &*(arr_ptr as *const crate::object::array_obj::JSArrayObject)
5671                            };
5672                            for val in arr.elements.iter() {
5673                                args.push(*val);
5674                            }
5675                        } else {
5676                            let arr = arr_val.as_object();
5677                            let len_atom = ctx.common_atoms.length;
5678                            let arr_len =
5679                                arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
5680                            if let Some(elems) = arr.get_array_elements() {
5681                                for val in elems.iter() {
5682                                    args.push(*val);
5683                                }
5684                            } else {
5685                                for i in 0..arr_len {
5686                                    let key = ctx.intern(&i.to_string());
5687                                    if let Some(val) = arr.get(key) {
5688                                        args.push(val);
5689                                    }
5690                                }
5691                            }
5692                        }
5693                    }
5694                    let argc = args.len() as u16;
5695
5696                    let this_val = if is_call_method {
5697                        self.get_reg(obj_reg)
5698                    } else if is_call_new {
5699                        let props = ctx.runtime_mut().gc_heap_mut().take_prop_vec();
5700                        let mut obj = crate::object::object::JSObject::new_typed_from_pool(
5701                            crate::object::object::ObjectType::Ordinary,
5702                            props,
5703                        );
5704                        obj.ensure_shape(ctx.shape_cache_mut());
5705                        if let Some(proto_ptr) = ctx.get_object_prototype() {
5706                            obj.prototype = Some(proto_ptr);
5707                        }
5708                        if func_val.is_function() {
5709                            let js_func = func_val.as_function();
5710
5711                            if !js_func.cached_prototype_ptr.is_null() {
5712                                obj.prototype = Some(js_func.cached_prototype_ptr);
5713                            } else {
5714                                let proto_key = ctx.common_atoms.prototype;
5715                                if let Some(proto_val) = js_func.base.get(proto_key) {
5716                                    if proto_val.is_object() {
5717                                        let cptr = proto_val.get_ptr()
5718                                            as *mut crate::object::object::JSObject;
5719                                        func_val.as_function_mut().cached_prototype_ptr = cptr;
5720                                        obj.prototype = Some(cptr);
5721                                    }
5722                                } else if !js_func.is_builtin() {
5723                                    let mut pobj = crate::object::object::JSObject::new();
5724                                    pobj.set(ctx.common_atoms.constructor, func_val);
5725                                    if let Some(opp) = ctx.get_object_prototype() {
5726                                        pobj.prototype = Some(opp);
5727                                    }
5728                                    let pp = Box::into_raw(Box::new(pobj)) as usize;
5729                                    ctx.runtime_mut().gc_heap_mut().track(pp);
5730                                    let func_mut = func_val.as_function_mut();
5731                                    func_mut.base.set(proto_key, JSValue::new_object(pp));
5732                                    let cptr = pp as *mut crate::object::object::JSObject;
5733                                    func_mut.cached_prototype_ptr = cptr;
5734                                    obj.prototype = Some(cptr);
5735                                }
5736                            }
5737                        }
5738                        let ptr = if ctx.runtime().gc_heap().nursery_enabled_and_can_fit_object() {
5739                            ctx.runtime_mut()
5740                                .gc_heap_mut()
5741                                .alloc_object_with_value(obj)
5742                                .map(|p| p as usize)
5743                                .unwrap_or_else(|| {
5744                                    let hp = Box::into_raw(Box::new(
5745                                        crate::object::object::JSObject::new(),
5746                                    )) as usize;
5747                                    ctx.runtime_mut().gc_heap_mut().track(hp);
5748                                    hp
5749                                })
5750                        } else {
5751                            let heap_ptr = Box::into_raw(Box::new(obj)) as usize;
5752                            ctx.runtime_mut().gc_heap_mut().track(heap_ptr);
5753                            heap_ptr
5754                        };
5755                        self.allocation_count += 1;
5756                        JSValue::new_object(ptr)
5757                    } else {
5758                        let frame = &self.frames[self.frame_index];
5759                        if frame.is_constructor
5760                            && !frame.super_ctor.is_undefined()
5761                            && func_val.is_function()
5762                            && frame.super_ctor.is_function()
5763                        {
5764                            let sctor_ptr = frame.super_ctor.get_ptr();
5765                            let fptr = func_val.get_ptr();
5766                            if fptr == sctor_ptr {
5767                                frame.this_value
5768                            } else {
5769                                JSValue::undefined()
5770                            }
5771                        } else {
5772                            JSValue::undefined()
5773                        }
5774                    };
5775
5776                    if func_val.is_function() {
5777                        let ptr = func_val.get_ptr();
5778                        let js_func = func_val.as_function();
5779                        if let Some(ref rb) = js_func.bytecode {
5780                            if js_func.is_generator() {
5781                                let mut snapshot =
5782                                    vec![JSValue::undefined(); rb.locals_count as usize];
5783                                if !snapshot.is_empty() {
5784                                    snapshot[0] = this_val;
5785                                    for (i, arg) in args.iter().enumerate() {
5786                                        if i + 1 < snapshot.len() {
5787                                            snapshot[i + 1] = *arg;
5788                                        }
5789                                    }
5790                                }
5791                                let mut gen_obj = crate::object::object::JSObject::new();
5792                                gen_obj.set_is_generator(true);
5793                                if let Some(proto_ptr) = ctx.get_generator_prototype() {
5794                                    gen_obj.prototype = Some(proto_ptr);
5795                                }
5796                                gen_obj.set_generator_state(
5797                                    crate::object::object::GeneratorState {
5798                                        bytecode: Box::new((**rb).clone()),
5799                                        snapshot,
5800                                        pc: 0,
5801                                        done: false,
5802                                    },
5803                                );
5804                                let gen_ptr = Box::into_raw(Box::new(gen_obj)) as usize;
5805                                ctx.runtime_mut().gc_heap_mut().track(gen_ptr);
5806                                self.set_reg(dst, JSValue::new_object(gen_ptr));
5807                                continue;
5808                            }
5809                            let return_pc = self.pc;
5810                            self.push_frame(
5811                                rb,
5812                                return_pc,
5813                                Some(ptr),
5814                                this_val,
5815                                dst,
5816                                argc,
5817                                is_call_new,
5818                                js_func.is_async(),
5819                                &args,
5820                                js_func.uses_arguments(),
5821                            );
5822                            if is_call_new {
5823                                let super_key = ctx.common_atoms.__super__;
5824                                if let Some(super_val) = js_func.base.get(super_key) {
5825                                    self.frames[self.frame_index].super_ctor = super_val;
5826                                }
5827                            }
5828                            continue;
5829                        } else if js_func.is_builtin() {
5830                            let caller_base = self.cached_registers_base;
5831                            let mut builtin_args = Vec::with_capacity(args.len() + 1);
5832                            if is_call_method {
5833                                builtin_args.push(self.registers[caller_base + obj_reg as usize]);
5834                            }
5835                            builtin_args.extend(args);
5836                            let result = if let Some(bf) = js_func.builtin_func {
5837                                ctx.call_builtin_direct(bf, &builtin_args)
5838                            } else if let Some(ba) = js_func.builtin_atom {
5839                                let name = ctx.get_atom_str(ba).to_string();
5840                                ctx.call_builtin(&name, &builtin_args)
5841                            } else {
5842                                JSValue::undefined()
5843                            };
5844                            self.set_reg(dst, result);
5845                        }
5846                    } else if func_val.is_object() && !is_call_new {
5847                        let result =
5848                            self.call_function_with_this(ctx, func_val, this_val, &args)?;
5849                        self.set_reg(dst, result);
5850                    } else {
5851                        let msg = format!(
5852                            "{} is not a function",
5853                            self.format_thrown_value(&func_val, ctx)
5854                        );
5855                        self.set_pending_type_error(ctx, &msg);
5856                        if let Some(exc) = self.pending_throw.take() {
5857                            match self.dispatch_throw_value(ctx, exc) {
5858                                ThrowDispatch::Caught => continue,
5859                                ThrowDispatch::Uncaught(e) => return Err(e),
5860                                ThrowDispatch::AsyncComplete(o) => return Ok(o),
5861                            }
5862                        }
5863                    }
5864                }
5865
5866                Opcode::NewFunction
5867                | Opcode::NewGeneratorFunction
5868                | Opcode::NewAsyncFunction
5869                | Opcode::NewAsyncGeneratorFunction => {
5870                    let is_generator = op == Opcode::NewGeneratorFunction
5871                        || op == Opcode::NewAsyncGeneratorFunction;
5872                    let is_async =
5873                        op == Opcode::NewAsyncFunction || op == Opcode::NewAsyncGeneratorFunction;
5874
5875                    let newfunc_start_pc = self.pc;
5876
5877                    let cached: Option<std::sync::Arc<crate::compiler::opcode::NestedBytecode>> =
5878                        if let Some(parent_ptr) = self.frames[self.frame_index].function_ptr {
5879                            let parent_func = unsafe {
5880                                &*(parent_ptr as *const crate::object::function::JSFunction)
5881                            };
5882                            if let Some(bc) = parent_func.bytecode.as_ref() {
5883                                bc.nested_bytecodes.get(&(newfunc_start_pc as u32)).cloned()
5884                            } else {
5885                                None
5886                            }
5887                        } else {
5888                            None
5889                        };
5890
5891                    let mut nb_for_ic: Option<
5892                        std::sync::Arc<crate::compiler::opcode::NestedBytecode>,
5893                    > = None;
5894
5895                    let (
5896                        fb_code,
5897                        fb_constants,
5898                        locals_count,
5899                        param_count,
5900                        uses_arguments,
5901                        is_strict,
5902                        func_var_name_to_slot,
5903                        upvalue_descs,
5904                        line_number_table_opt,
5905                        func_name_atom,
5906                    ) = if let Some(ref nb) = cached {
5907                        self.pc = newfunc_start_pc + nb.parent_bytecode_span as usize;
5908                        let uvdescs: Vec<(u32, u32)> = nb.upvalue_descs.clone();
5909
5910                        (
5911                            Vec::new(),
5912                            Vec::new(),
5913                            nb.locals_count,
5914                            nb.param_count,
5915                            nb.uses_arguments,
5916                            nb.is_strict,
5917                            nb.var_name_to_slot.clone(),
5918                            uvdescs,
5919                            nb.line_number_table.clone(),
5920                            nb.func_name_atom,
5921                        )
5922                    } else {
5923                        let param_count = self.read_u16_pc();
5924                        let uses_arguments = self.read_u8() != 0;
5925                        let is_strict = self.read_u8() != 0;
5926                        let locals_count = self.read_u16_pc() as u32;
5927                        let bytecode_len = self.read_u32();
5928                        let mut fb_code = Vec::with_capacity(bytecode_len as usize);
5929
5930                        unsafe {
5931                            let src = self.cached_code_ptr.add(self.pc);
5932                            fb_code.set_len(bytecode_len as usize);
5933                            std::ptr::copy_nonoverlapping(
5934                                src,
5935                                fb_code.as_mut_ptr(),
5936                                bytecode_len as usize,
5937                            );
5938                        }
5939                        self.pc += bytecode_len as usize;
5940                        let constants_len = self.read_u32();
5941                        let mut fb_constants = Vec::with_capacity(constants_len as usize);
5942                        for _ in 0..constants_len {
5943                            let const_type = self.read_u8();
5944                            match const_type {
5945                                0 => fb_constants.push(JSValue::undefined()),
5946                                1 => fb_constants.push(JSValue::null()),
5947                                2 => fb_constants.push(JSValue::bool(true)),
5948                                3 => fb_constants.push(JSValue::bool(false)),
5949                                4 => {
5950                                    let v = self.read_i64();
5951                                    fb_constants.push(JSValue::new_int(v));
5952                                }
5953                                5 => {
5954                                    let bytes = [
5955                                        self.read_u8(),
5956                                        self.read_u8(),
5957                                        self.read_u8(),
5958                                        self.read_u8(),
5959                                        self.read_u8(),
5960                                        self.read_u8(),
5961                                        self.read_u8(),
5962                                        self.read_u8(),
5963                                    ];
5964                                    let v = f64::from_le_bytes(bytes);
5965                                    fb_constants.push(JSValue::new_float(v));
5966                                }
5967                                6 => {
5968                                    let atom_id = self.read_u32() as u32;
5969                                    fb_constants.push(JSValue::new_string(
5970                                        crate::runtime::atom::Atom(atom_id),
5971                                    ));
5972                                }
5973                                _ => fb_constants.push(JSValue::undefined()),
5974                            }
5975                        }
5976                        let upvalue_count = self.read_u32();
5977                        let mut upvalue_descs = Vec::with_capacity(upvalue_count as usize);
5978                        for _ in 0..upvalue_count {
5979                            let atom_id = self.read_u32();
5980                            let local_idx = self.read_u32();
5981                            upvalue_descs.push((atom_id, local_idx));
5982                        }
5983
5984                        let line_table_entry_count = self.read_u32();
5985                        let mut line_table = crate::compiler::location::LineNumberTable::new();
5986                        for _ in 0..line_table_entry_count {
5987                            let off = self.read_u32();
5988                            let line = self.read_u32();
5989                            line_table.add_entry(off, line);
5990                        }
5991                        let line_number_table_opt = if line_table_entry_count > 0 {
5992                            Some(line_table)
5993                        } else {
5994                            None
5995                        };
5996                        let func_name_atom = self.read_u32() as u32;
5997                        let var_count = self.read_u32();
5998                        let func_var_name_to_slot = std::rc::Rc::new({
5999                            let mut v = Vec::new();
6000                            for _ in 0..var_count {
6001                                let atom_id = self.read_u32();
6002                                let slot = self.read_u16_pc();
6003                                v.push((atom_id, slot));
6004                            }
6005                            v
6006                        });
6007
6008                        let parent_bytecode_span = (self.pc - newfunc_start_pc) as u32;
6009
6010                        if let Some(parent_ptr) = self.frames[self.frame_index].function_ptr {
6011                            let parent_func = unsafe {
6012                                &mut *(parent_ptr as *mut crate::object::function::JSFunction)
6013                            };
6014                            if let Some(bc) = parent_func.bytecode.as_mut() {
6015                                let nb = if let Some(existing) =
6016                                    bc.nested_bytecodes.get(&(newfunc_start_pc as u32))
6017                                {
6018                                    let arc = existing.clone();
6019                                    nb_for_ic = Some(arc.clone());
6020                                    arc
6021                                } else {
6022                                    let nb = std::sync::Arc::new(
6023                                        crate::compiler::opcode::NestedBytecode {
6024                                            code: fb_code.clone(),
6025                                            constants: fb_constants.clone(),
6026                                            locals_count,
6027                                            param_count,
6028                                            uses_arguments,
6029                                            is_strict,
6030                                            var_name_to_slot: func_var_name_to_slot.clone(),
6031                                            line_number_table: line_number_table_opt.clone(),
6032                                            parent_bytecode_span,
6033                                            upvalue_count: upvalue_descs.len() as u32,
6034                                            upvalue_descs: upvalue_descs.clone(),
6035                                            func_name_atom,
6036                                            ic_table: std::cell::UnsafeCell::new(
6037                                                crate::compiler::InlineCacheTable::new(),
6038                                            ),
6039                                        },
6040                                    );
6041                                    nb_for_ic = Some(nb.clone());
6042                                    bc.nested_bytecodes
6043                                        .insert(newfunc_start_pc as u32, nb.clone());
6044                                    nb
6045                                };
6046                                let _ = nb;
6047                            }
6048                        }
6049                        (
6050                            fb_code,
6051                            fb_constants,
6052                            locals_count,
6053                            param_count,
6054                            uses_arguments,
6055                            is_strict,
6056                            func_var_name_to_slot,
6057                            upvalue_descs,
6058                            line_number_table_opt,
6059                            func_name_atom,
6060                        )
6061                    };
6062
6063                    let mut func = crate::object::function::JSFunction::new();
6064
6065                    if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
6066                        func.base.prototype = Some(fn_proto_ptr);
6067                    }
6068                    func.param_count = param_count as u32;
6069                    func.arity = param_count as u32;
6070                    func.locals_count = locals_count;
6071                    func.set_is_generator(is_generator);
6072                    func.set_is_async(is_async);
6073                    func.set_uses_arguments(uses_arguments);
6074                    func.set_is_strict(is_strict);
6075                    func.line_number_table = line_number_table_opt.clone();
6076                    func.bytecode = Some(Box::new(Bytecode {
6077                        code: fb_code,
6078                        constants: fb_constants,
6079                        locals_count,
6080                        param_count,
6081                        line_number_table: line_number_table_opt,
6082                        ic_table: crate::compiler::InlineCacheTable::new(),
6083                        shared_ic_table_ptr: std::ptr::null_mut(),
6084                        shared_code_ptr: std::ptr::null(),
6085                        shared_code_len: 0,
6086                        shared_const_ptr: std::ptr::null(),
6087                        shared_const_len: 0,
6088                        uses_arguments,
6089                        is_strict,
6090                        var_name_to_slot: func_var_name_to_slot,
6091                        nested_bytecodes: std::collections::HashMap::new(),
6092                        is_simple_constructor: false,
6093                        simple_constructor_props: Vec::new(),
6094                        cached_constructor_final_shape: None,
6095                        cached_constructor_atoms: Vec::new(),
6096                    }));
6097
6098                    let effective_nb = nb_for_ic.or_else(|| cached.clone());
6099                    if let Some(ref nb_arc) = effective_nb {
6100                        if let Some(bc) = func.bytecode.as_mut() {
6101                            bc.shared_ic_table_ptr = nb_arc.ic_table.get();
6102
6103                            if bc.code.is_empty() {
6104                                bc.shared_code_ptr = nb_arc.code.as_ptr();
6105                                bc.shared_code_len = nb_arc.code.len();
6106                            }
6107                            if bc.constants.is_empty() {
6108                                bc.shared_const_ptr = nb_arc.constants.as_ptr();
6109                                bc.shared_const_len = nb_arc.constants.len();
6110                            }
6111                        }
6112                    }
6113                    func.shared_nb_for_ic = effective_nb;
6114
6115                    let inherited_sentinel = u16::MAX as usize;
6116                    let current_frame_base = self.frames[self.frame_index].registers_base;
6117                    for (atom_id, local_idx_raw) in &upvalue_descs {
6118                        let atom = crate::runtime::atom::Atom(*atom_id);
6119                        let local_idx = *local_idx_raw as usize;
6120                        func.upvalues_mut().upvalue_slot_atoms.push(atom);
6121
6122                        let cell = if local_idx != inherited_sentinel {
6123                            func.upvalues_mut()
6124                                .upvalue_local_indices
6125                                .insert(atom, local_idx);
6126                            let initial_value =
6127                                if local_idx < self.frames[self.frame_index].registers_count {
6128                                    self.registers[current_frame_base + local_idx]
6129                                } else {
6130                                    JSValue::undefined()
6131                                };
6132
6133                            let current_frame = &mut self.frames[self.frame_index];
6134                            let local_idx_u16 = local_idx as u16;
6135                            if let Some(existing) = current_frame
6136                                .upvalue_sync_map
6137                                .as_ref()
6138                                .and_then(|m| m.get(&local_idx_u16))
6139                            {
6140                                existing.clone()
6141                            } else {
6142                                let new_cell =
6143                                    std::rc::Rc::new(std::cell::Cell::new(initial_value));
6144                                current_frame
6145                                    .upvalue_sync_map
6146                                    .get_or_insert_with(|| Box::new(FxHashMap::default()))
6147                                    .insert(local_idx_u16, new_cell.clone());
6148                                if local_idx_u16 < 64 {
6149                                    current_frame.upvalue_sync_bitset |= 1u64 << local_idx_u16;
6150                                    self.cached_upvalue_sync_bitset |= 1u64 << local_idx_u16;
6151                                }
6152                                self.cached_has_upvalue_sync = true;
6153                                new_cell
6154                            }
6155                        } else if let Some(parent_ptr) = self.frames[self.frame_index].function_ptr
6156                        {
6157                            let parent_func = unsafe {
6158                                &*(parent_ptr as *const crate::object::function::JSFunction)
6159                            };
6160
6161                            if let Some(parent_cell) = parent_func
6162                                .upvalues_ref()
6163                                .and_then(|u| u.upvalue_cells.get(&atom))
6164                            {
6165                                parent_cell.clone()
6166                            } else {
6167                                std::rc::Rc::new(std::cell::Cell::new(JSValue::undefined()))
6168                            }
6169                        } else {
6170                            std::rc::Rc::new(std::cell::Cell::new(JSValue::undefined()))
6171                        };
6172                        func.upvalues_mut().upvalue_slots.push(cell.clone());
6173                        func.upvalues_mut().upvalue_cells.insert(atom, cell);
6174                    }
6175
6176                    if func_name_atom != 0 {
6177                        func.name = crate::runtime::atom::Atom(func_name_atom);
6178                    }
6179
6180                    let is_closure = !upvalue_descs.is_empty();
6181
6182                    let proto_atom = ctx.common_atoms.prototype;
6183                    let func_ptr = Box::into_raw(Box::new(func)) as usize;
6184                    ctx.runtime_mut().gc_heap_mut().track_function(func_ptr);
6185                    let unboxed_func =
6186                        unsafe { &mut *(func_ptr as *mut crate::object::function::JSFunction) };
6187                    if !is_closure {
6188                        let proto_obj = crate::object::object::JSObject::new();
6189                        let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
6190                        ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
6191                        let func_value = JSValue::new_function(func_ptr);
6192                        unsafe {
6193                            let proto_obj_mut =
6194                                &mut *(proto_ptr as *mut crate::object::object::JSObject);
6195                            proto_obj_mut.set(ctx.common_atoms.constructor, func_value);
6196                        }
6197                        unboxed_func
6198                            .base
6199                            .set(proto_atom, JSValue::new_object(proto_ptr));
6200                        unboxed_func.cached_prototype_ptr =
6201                            proto_ptr as *mut crate::object::object::JSObject;
6202                    }
6203                    if let Some(fn_pp) = ctx.get_function_prototype() {
6204                        unboxed_func.base.prototype = Some(fn_pp);
6205                    } else if let Some(obj_pp) = ctx.get_object_prototype() {
6206                        unboxed_func.base.prototype = Some(obj_pp);
6207                    }
6208                    self.allocation_count += 1;
6209                    let dst_reg = self.read_u16_pc();
6210                    self.set_reg(dst_reg, JSValue::new_function(func_ptr));
6211                    self.maybe_gc(ctx);
6212                }
6213                Opcode::LoadTdz => {
6214                    let dst = self.read_u16_pc();
6215                    self.set_reg(dst, JSValue::new_tdz());
6216                }
6217                Opcode::CheckTdz => {
6218                    let reg = self.read_u16_pc();
6219                    if self.get_reg(reg).is_tdz() {
6220                        return Err(
6221                            "ReferenceError: Cannot access variable before initialization"
6222                                .to_string(),
6223                        );
6224                    }
6225                }
6226                Opcode::CheckRef => {
6227                    let reg = self.read_u16_pc();
6228                    let idx = self.read_u32() as usize;
6229
6230                    let reg_val = self.get_reg(reg);
6231                    if !reg_val.is_undefined()
6232                        && !reg_val.is_tdz()
6233                        && self.eval_binding_frames == 0
6234                        && self.caller_vm.is_none()
6235                    {
6236                        continue;
6237                    }
6238                    let name = if idx < self.frames[self.frame_index].constants_len {
6239                        unsafe { *self.cached_const_ptr.add(idx) }
6240                    } else {
6241                        JSValue::undefined()
6242                    };
6243                    let atom = name.get_atom();
6244                    let mut exists = false;
6245                    let has_eval = self.eval_binding_frames > 0 || self.caller_vm.is_some();
6246                    if has_eval {
6247                        for fi in (0..=self.frame_index).rev() {
6248                            if let Some(ref eb) = self.frames[fi].eval_bindings {
6249                                if eb.contains_key(&atom.0) {
6250                                    exists = true;
6251                                    break;
6252                                }
6253                            }
6254                        }
6255                        if !exists && self.get_var_in_caller_vm(atom.0).is_some() {
6256                            exists = true;
6257                        }
6258                    }
6259                    if !exists {
6260                        let global = ctx.global();
6261                        if global.is_object() {
6262                            exists = global.as_object().get_own(atom).is_some();
6263                        }
6264                    }
6265                    if !exists {
6266                        let ref_err_atom = ctx.intern("ReferenceError");
6267                        let name_str = ctx.get_atom_str(atom).to_string();
6268                        let err_msg = format!("{} is not defined", name_str);
6269                        let msg_atom = ctx.intern(&err_msg);
6270                        let mut err = crate::object::object::JSObject::new();
6271                        err.set(ctx.intern("name"), JSValue::new_string(ref_err_atom));
6272                        err.set(ctx.intern("message"), JSValue::new_string(msg_atom));
6273                        if let Some(proto) = ctx.get_reference_error_prototype() {
6274                            err.prototype = Some(proto as *mut _);
6275                        } else if let Some(proto) = ctx.get_error_prototype() {
6276                            err.prototype = Some(proto as *mut _);
6277                        }
6278                        let ptr = Box::into_raw(Box::new(err)) as usize;
6279                        ctx.runtime_mut().gc_heap_mut().track(ptr);
6280                        self.pending_throw = Some(JSValue::new_object(ptr));
6281                        if let Some(exc) = self.pending_throw.take() {
6282                            match self.dispatch_throw_value(ctx, exc) {
6283                                ThrowDispatch::Caught => continue,
6284                                ThrowDispatch::Uncaught(e) => return Err(e),
6285                                ThrowDispatch::AsyncComplete(_) => continue,
6286                            }
6287                        }
6288                    }
6289                }
6290                Opcode::CheckObjectCoercible => {
6291                    let reg = self.read_u16_pc();
6292                    let val = self.get_reg(reg);
6293                    if val.is_null() || val.is_undefined() {
6294                        let type_err_atom = ctx.intern("TypeError");
6295                        let msg = "Cannot destructure 'undefined' or 'null'.";
6296                        let msg_atom = ctx.intern(msg);
6297                        let mut err = crate::object::object::JSObject::new();
6298                        err.set(ctx.intern("name"), JSValue::new_string(type_err_atom));
6299                        err.set(ctx.intern("message"), JSValue::new_string(msg_atom));
6300                        if let Some(proto) = ctx.get_type_error_prototype() {
6301                            err.prototype = Some(proto as *mut _);
6302                        } else if let Some(proto) = ctx.get_error_prototype() {
6303                            err.prototype = Some(proto as *mut _);
6304                        }
6305                        let ptr = Box::into_raw(Box::new(err)) as usize;
6306                        ctx.runtime_mut().gc_heap_mut().track(ptr);
6307                        let exc = JSValue::new_object(ptr);
6308                        self.pending_throw = Some(exc);
6309                        if let Some(thrown) = self.pending_throw.take() {
6310                            match self.dispatch_throw_value(ctx, thrown) {
6311                                ThrowDispatch::Caught => continue,
6312                                ThrowDispatch::Uncaught(e) => {
6313                                    self.pending_throw = Some(exc);
6314                                    return Err(e);
6315                                }
6316                                ThrowDispatch::AsyncComplete(_) => continue,
6317                            }
6318                        }
6319                    }
6320                }
6321                Opcode::GetIterator => {
6322                    let dst = self.read_u16_pc();
6323                    let src = self.read_u16_pc();
6324                    let iterable = self.get_reg(src);
6325                    let result = if iterable.is_object() || iterable.is_function() {
6326                        let arr_ptr = iterable.get_ptr();
6327                        let is_array = iterable.as_object().is_dense_array();
6328                        if is_array {
6329                            self.create_iter_object(ctx, iterable)
6330                        } else if iterable.is_string() {
6331                            self.create_iter_object(ctx, iterable)
6332                        } else {
6333                            let obj: &crate::object::object::JSObject = if iterable.is_function() {
6334                                let func = iterable.as_function();
6335                                &func.base
6336                            } else {
6337                                iterable.as_object()
6338                            };
6339
6340                            let mut sym_iter_atom = None;
6341                            let global = ctx.global();
6342                            if global.is_object() {
6343                                if let Some(sym_val) =
6344                                    global.as_object().get(ctx.intern("Symbol.iterator"))
6345                                {
6346                                    if sym_val.is_symbol() {
6347                                        sym_iter_atom = Some(crate::runtime::atom::Atom(
6348                                            0x40000000 | sym_val.get_symbol_id(),
6349                                        ));
6350                                    }
6351                                }
6352                            }
6353                            let iter_fn = sym_iter_atom.and_then(|a| obj.get(a)).or_else(|| {
6354                                let mut current = obj.prototype;
6355                                while let Some(p) = current {
6356                                    let pobj = unsafe { &*p };
6357                                    if let Some(v) = sym_iter_atom.and_then(|a| pobj.get(a)) {
6358                                        return Some(v);
6359                                    }
6360                                    current = pobj.prototype;
6361                                }
6362                                None
6363                            });
6364                            if let Some(iter_fn) = iter_fn.or_else(|| {
6365                                let str_atom = ctx.intern("Symbol.iterator");
6366                                obj.get(str_atom).or_else(|| {
6367                                    let mut proto = obj.prototype;
6368                                    while let Some(p) = proto {
6369                                        let pobj = unsafe { &*p };
6370                                        if let Some(v) = pobj.get(str_atom) {
6371                                            return Some(v);
6372                                        }
6373                                        proto = pobj.prototype;
6374                                    }
6375                                    None
6376                                })
6377                            }) {
6378                                if iter_fn.is_function() {
6379                                    let func_ptr = iter_fn.get_ptr();
6380                                    let js_func = unsafe {
6381                                        &*(func_ptr as *const crate::object::function::JSFunction)
6382                                    };
6383                                    if js_func.is_builtin() {
6384                                        if let Some(builtin_fn) = js_func.builtin_func {
6385                                            ctx.call_builtin_direct(builtin_fn, &[iterable])
6386                                        } else {
6387                                            JSValue::undefined()
6388                                        }
6389                                    } else {
6390                                        let obj2 = iterable.as_object();
6391                                        let len_atom = ctx.common_atoms.length;
6392                                        if obj2.get(len_atom).is_some() {
6393                                            let mut iter_obj =
6394                                                crate::object::object::JSObject::new();
6395                                            let arr_atom = ctx.common_atoms.__iter_arr__;
6396                                            let idx_atom = ctx.common_atoms.__iter_idx__;
6397                                            iter_obj.set_cached(
6398                                                arr_atom,
6399                                                iterable,
6400                                                ctx.shape_cache_mut(),
6401                                            );
6402                                            iter_obj.set_cached(
6403                                                idx_atom,
6404                                                JSValue::new_int(0),
6405                                                ctx.shape_cache_mut(),
6406                                            );
6407                                            let iter_ptr =
6408                                                Box::into_raw(Box::new(iter_obj)) as usize;
6409                                            ctx.runtime_mut().gc_heap_mut().track(iter_ptr);
6410                                            self.allocation_count += 1;
6411
6412                                            JSValue::new_object(iter_ptr)
6413                                        } else {
6414                                            JSValue::undefined()
6415                                        }
6416                                    }
6417                                } else {
6418                                    let obj2: &crate::object::object::JSObject = unsafe {
6419                                        &*(arr_ptr as *const crate::object::object::JSObject)
6420                                    };
6421                                    let len_atom = ctx.common_atoms.length;
6422                                    if obj2.get(len_atom).is_some() {
6423                                        let mut iter_obj = crate::object::object::JSObject::new();
6424                                        let arr_atom = ctx.common_atoms.__iter_arr__;
6425                                        let idx_atom = ctx.common_atoms.__iter_idx__;
6426                                        iter_obj.set_cached(
6427                                            arr_atom,
6428                                            iterable,
6429                                            ctx.shape_cache_mut(),
6430                                        );
6431                                        iter_obj.set_cached(
6432                                            idx_atom,
6433                                            JSValue::new_int(0),
6434                                            ctx.shape_cache_mut(),
6435                                        );
6436                                        let iter_ptr = Box::into_raw(Box::new(iter_obj)) as usize;
6437                                        ctx.runtime_mut().gc_heap_mut().track(iter_ptr);
6438                                        self.allocation_count += 1;
6439
6440                                        JSValue::new_object(iter_ptr)
6441                                    } else {
6442                                        JSValue::undefined()
6443                                    }
6444                                }
6445                            } else {
6446                                let obj2: &crate::object::object::JSObject = unsafe {
6447                                    &*(arr_ptr as *const crate::object::object::JSObject)
6448                                };
6449                                let len_atom = ctx.common_atoms.length;
6450                                if obj2.get(len_atom).is_some() {
6451                                    let mut iter_obj = crate::object::object::JSObject::new();
6452                                    let arr_atom = ctx.common_atoms.__iter_arr__;
6453                                    let idx_atom = ctx.common_atoms.__iter_idx__;
6454                                    iter_obj.set_cached(arr_atom, iterable, ctx.shape_cache_mut());
6455                                    iter_obj.set_cached(
6456                                        idx_atom,
6457                                        JSValue::new_int(0),
6458                                        ctx.shape_cache_mut(),
6459                                    );
6460                                    let iter_ptr = Box::into_raw(Box::new(iter_obj)) as usize;
6461                                    ctx.runtime_mut().gc_heap_mut().track(iter_ptr);
6462                                    self.allocation_count += 1;
6463
6464                                    JSValue::new_object(iter_ptr)
6465                                } else {
6466                                    JSValue::undefined()
6467                                }
6468                            }
6469                        }
6470                    } else if iterable.is_string() {
6471                        self.create_iter_object(ctx, iterable)
6472                    } else {
6473                        JSValue::undefined()
6474                    };
6475                    self.set_reg(dst, result);
6476                }
6477                Opcode::IteratorNext => {
6478                    let dst_val = self.read_u16_pc();
6479                    let dst_done = self.read_u16_pc();
6480                    let iter_reg = self.read_u16_pc();
6481                    let iter_val = self.get_reg(iter_reg);
6482
6483                    if !iter_val.is_object() {
6484                        self.set_reg(dst_val, JSValue::undefined());
6485                        self.set_reg(dst_done, JSValue::bool(true));
6486                        continue;
6487                    }
6488
6489                    let iter_ptr = iter_val.get_ptr();
6490                    let iter_obj =
6491                        unsafe { &mut *(iter_ptr as *mut crate::object::object::JSObject) };
6492                    let arr_atom = ctx.common_atoms.__iter_arr__;
6493
6494                    if let Some(arr_val) = iter_obj.get(arr_atom) {
6495                        let idx_atom = ctx.common_atoms.__iter_idx__;
6496                        let idx = iter_obj.get(idx_atom).map(|v| v.get_int()).unwrap_or(0) as usize;
6497
6498                        if arr_val.is_string() {
6499                            let atom = arr_val.get_atom();
6500                            let s = ctx.atom_table().get(atom);
6501                            let chars: Vec<char> = s.chars().collect();
6502                            if idx < chars.len() {
6503                                let ch = chars[idx].to_string();
6504                                let ch_atom = ctx.atom_table_mut().intern(&ch);
6505                                let ch_val = JSValue::new_string(ch_atom);
6506                                iter_obj.set_cached(
6507                                    idx_atom,
6508                                    JSValue::new_int((idx + 1) as i64),
6509                                    ctx.shape_cache_mut(),
6510                                );
6511                                self.set_reg(dst_val, ch_val);
6512                                self.set_reg(dst_done, JSValue::bool(false));
6513                            } else {
6514                                self.set_reg(dst_val, JSValue::undefined());
6515                                self.set_reg(dst_done, JSValue::bool(true));
6516                            }
6517                        } else if arr_val.is_object() {
6518                            let arr_ptr = arr_val.get_ptr();
6519                            let is_jsarray =
6520                                unsafe { &*(arr_ptr as *const crate::object::object::JSObject) }
6521                                    .is_dense_array();
6522                            let length = if is_jsarray {
6523                                let arr = unsafe {
6524                                    &*(arr_ptr as *const crate::object::array_obj::JSArrayObject)
6525                                };
6526                                arr.len()
6527                            } else {
6528                                let obj = unsafe {
6529                                    &*(arr_ptr as *const crate::object::object::JSObject)
6530                                };
6531                                let len_atom = ctx.common_atoms.length;
6532                                obj.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
6533                            };
6534
6535                            if idx < length {
6536                                let value = if is_jsarray {
6537                                    let arr = unsafe {
6538                                        &*(arr_ptr
6539                                            as *const crate::object::array_obj::JSArrayObject)
6540                                    };
6541                                    arr.get(idx).unwrap_or(JSValue::undefined())
6542                                } else {
6543                                    let obj = unsafe {
6544                                        &*(arr_ptr as *const crate::object::object::JSObject)
6545                                    };
6546                                    let key = self.int_atom(idx, ctx);
6547                                    obj.get(key).unwrap_or(JSValue::undefined())
6548                                };
6549                                iter_obj.set_cached(
6550                                    idx_atom,
6551                                    JSValue::new_int((idx + 1) as i64),
6552                                    ctx.shape_cache_mut(),
6553                                );
6554                                self.set_reg(dst_val, value);
6555                                self.set_reg(dst_done, JSValue::bool(false));
6556                            } else {
6557                                self.set_reg(dst_val, JSValue::undefined());
6558                                self.set_reg(dst_done, JSValue::bool(true));
6559                            }
6560                        } else {
6561                            self.set_reg(dst_val, JSValue::undefined());
6562                            self.set_reg(dst_done, JSValue::bool(true));
6563                        }
6564                    } else {
6565                        self.set_reg(dst_val, JSValue::undefined());
6566                        self.set_reg(dst_done, JSValue::bool(true));
6567                    }
6568                }
6569                Opcode::GetArguments => {
6570                    let dst = self.read_u16_pc();
6571                    let fi = self.frame_index;
6572                    if let Some(cached_ptr) = self.frames[fi].cached_arguments {
6573                        self.set_reg(dst, JSValue::new_object(cached_ptr));
6574                    } else {
6575                        let frame = &self.frames[fi];
6576                        let func_ptr = frame.function_ptr;
6577                        let saved_args = &frame.saved_args;
6578                        let arg_count = saved_args.len();
6579                        let is_strict = frame.is_strict_frame;
6580                        let use_mapped = !is_strict && func_ptr.is_some();
6581                        use crate::object::object::{
6582                            ATTR_CONFIGURABLE, ATTR_WRITABLE, PropertyDescriptor,
6583                        };
6584                        let mut args_obj = crate::object::object::JSObject::new();
6585                        if let Some(obj_proto) = ctx.get_object_prototype() {
6586                            args_obj.prototype = Some(obj_proto);
6587                        }
6588
6589                        let args_shape = ctx.get_or_create_args_length_shape();
6590                        args_obj.set_first_prop_with_shape(
6591                            ctx.common_atoms.length,
6592                            JSValue::new_int(arg_count as i64),
6593                            ATTR_WRITABLE | ATTR_CONFIGURABLE,
6594                            args_shape,
6595                        );
6596                        let declared_param_count = if use_mapped {
6597                            if let Some(ptr) = func_ptr {
6598                                let js_func = unsafe { JSValue::function_from_ptr(ptr) };
6599                                js_func.param_count
6600                            } else {
6601                                0
6602                            }
6603                        } else {
6604                            0
6605                        };
6606                        if use_mapped {
6607                            args_obj
6608                                .set_obj_type(crate::object::object::ObjectType::MappedArguments);
6609                            {
6610                                let extra = args_obj.ensure_extra();
6611                                extra.mapped_args_frame_index = fi;
6612                                extra.mapped_args_param_count = declared_param_count;
6613                            }
6614                            let start = declared_param_count as usize;
6615                            if start < arg_count {
6616                                args_obj
6617                                    .ensure_elements()
6618                                    .resize(arg_count, JSValue::undefined());
6619                                for i in start..arg_count {
6620                                    args_obj.set_indexed(i, saved_args[i]);
6621                                }
6622                            }
6623                        } else {
6624                            if arg_count > 0 {
6625                                args_obj
6626                                    .ensure_elements()
6627                                    .resize(arg_count, JSValue::undefined());
6628                                for (i, val) in saved_args.iter().enumerate() {
6629                                    args_obj.set_indexed(i, *val);
6630                                }
6631                            }
6632                        }
6633                        let callee_atom = ctx.common_atoms.callee;
6634                        if let Some(ptr) = func_ptr {
6635                            if is_strict {
6636                                let mut thrower = crate::object::function::JSFunction::new_builtin(
6637                                    ctx.intern("callee"),
6638                                    0,
6639                                );
6640                                thrower.builtin_atom = Some(ctx.intern("throw_type_error_callee"));
6641                                thrower.builtin_func =
6642                                    ctx.get_builtin_func("throw_type_error_callee");
6643                                let thrower_ptr = Box::into_raw(Box::new(thrower)) as usize;
6644                                ctx.runtime_mut().gc_heap_mut().track_function(thrower_ptr);
6645                                args_obj.define_accessor(
6646                                    callee_atom,
6647                                    Some(JSValue::new_function(thrower_ptr)),
6648                                    None,
6649                                );
6650                            } else {
6651                                args_obj.define_property(
6652                                    callee_atom,
6653                                    PropertyDescriptor {
6654                                        value: Some(JSValue::new_function(ptr)),
6655                                        writable: true,
6656                                        enumerable: false,
6657                                        configurable: true,
6658                                        get: None,
6659                                        set: None,
6660                                    },
6661                                );
6662                            }
6663                        }
6664
6665                        let ptr = if ctx
6666                            .runtime_mut()
6667                            .gc_heap_mut()
6668                            .nursery_enabled_and_can_fit_object()
6669                        {
6670                            ctx.runtime_mut()
6671                                .gc_heap_mut()
6672                                .alloc_object_with_value(args_obj)
6673                                .expect("nursery alloc failed despite can_fit")
6674                                as usize
6675                        } else {
6676                            let hp = Box::into_raw(Box::new(args_obj)) as usize;
6677                            ctx.runtime_mut().gc_heap_mut().track(hp);
6678                            hp
6679                        };
6680                        self.frames[fi].cached_arguments = Some(ptr);
6681                        self.set_reg(dst, JSValue::new_object(ptr));
6682                        self.allocation_count += 1;
6683                        self.maybe_gc(ctx);
6684                    }
6685                }
6686                Opcode::GetCurrentFunction => {
6687                    let dst = self.read_u16_pc();
6688                    let value = if let Some(ptr) = self.frames[self.frame_index].function_ptr {
6689                        JSValue::new_function(ptr)
6690                    } else {
6691                        JSValue::undefined()
6692                    };
6693                    self.set_reg(dst, value);
6694                }
6695            }
6696        }
6697    }
6698
6699    pub fn execute_generator_step(
6700        &mut self,
6701        ctx: &mut JSContext,
6702        bytecode: &Bytecode,
6703        registers_snapshot: &[JSValue],
6704        start_pc: usize,
6705    ) -> Result<(JSValue, Vec<JSValue>, usize, bool), String> {
6706        self.ctx_ptr = ctx;
6707
6708        let needed = bytecode.locals_count as usize;
6709        if needed > self.registers.len() {
6710            self.registers.resize(needed, JSValue::undefined());
6711        }
6712        for (i, val) in registers_snapshot.iter().enumerate() {
6713            if i < self.registers.len() {
6714                self.registers[i] = *val;
6715            }
6716        }
6717        for i in registers_snapshot.len()..needed {
6718            if i < self.registers.len() {
6719                self.registers[i] = JSValue::undefined();
6720            }
6721        }
6722
6723        let result = self.execute_inner(ctx, bytecode, true, start_pc, true)?;
6724        match result {
6725            ExecutionOutcome::Complete(val) => Ok((val, self.registers.clone(), self.pc, true)),
6726            ExecutionOutcome::Yield(val) => Ok((val, self.registers.clone(), self.pc, false)),
6727        }
6728    }
6729
6730    #[inline(always)]
6731    fn read_u16_pc(&mut self) -> u16 {
6732        let val =
6733            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const u16) };
6734        self.pc += 2;
6735        val
6736    }
6737
6738    #[inline(always)]
6739    fn read_u32_pc(&mut self) -> u32 {
6740        let val =
6741            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const u32) };
6742        self.pc += 4;
6743        val
6744    }
6745
6746    #[inline(always)]
6747    fn read_i32_pc(&mut self) -> i32 {
6748        let val =
6749            unsafe { std::ptr::read_unaligned(self.cached_code_ptr.add(self.pc) as *const i32) };
6750        self.pc += 4;
6751        val
6752    }
6753
6754    #[inline(always)]
6755    fn get_named_prop_result(
6756        &mut self,
6757        ctx: &mut JSContext,
6758        dst: u16,
6759        obj_val: JSValue,
6760        atom: crate::runtime::atom::Atom,
6761        ic_pc: usize,
6762    ) -> Option<JSValue> {
6763        if !obj_val.is_object_like()
6764            && !obj_val.is_function()
6765            && (obj_val.is_int() || obj_val.is_float() || obj_val.is_string() || obj_val.is_bool())
6766        {
6767            if obj_val.is_string() && atom == ctx.common_atoms.length {
6768                let len = ctx.string_char_count(obj_val.get_atom()) as i64;
6769                let v = JSValue::new_int(len);
6770                self.set_reg(dst, v);
6771                return Some(v);
6772            }
6773            let start_proto = if obj_val.is_string() {
6774                ctx.get_string_prototype()
6775            } else if obj_val.is_int() || obj_val.is_float() {
6776                ctx.get_number_prototype()
6777            } else {
6778                ctx.get_object_prototype()
6779            };
6780            let mut current = start_proto;
6781            while let Some(ptr) = current {
6782                let pobj = unsafe { &*ptr };
6783                if let Some(getter) = pobj.get_own_accessor_value(atom) {
6784                    if getter.is_function() {
6785                        let js_func = getter.as_function();
6786                        let fn_this = if !js_func.is_strict()
6787                            && (obj_val.is_int()
6788                                || obj_val.is_float()
6789                                || obj_val.is_string()
6790                                || obj_val.is_bool())
6791                        {
6792                            let mut wrapper = crate::object::object::JSObject::new();
6793                            if let Some(opp) = ctx.get_object_prototype() {
6794                                wrapper.set_prototype_raw(opp);
6795                            }
6796                            if obj_val.is_string() || obj_val.is_int() || obj_val.is_float() {
6797                                wrapper.set_cached(
6798                                    crate::runtime::atom::Atom(0),
6799                                    obj_val,
6800                                    ctx.shape_cache_mut(),
6801                                );
6802                            }
6803                            JSValue::new_object(Box::into_raw(Box::new(wrapper)) as usize)
6804                        } else {
6805                            obj_val
6806                        };
6807                        if let Ok(v) = self.call_function_with_this(ctx, getter, fn_this, &[]) {
6808                            self.set_reg(dst, v);
6809                            return Some(v);
6810                        }
6811                    }
6812                    self.set_reg(dst, JSValue::undefined());
6813                    return Some(JSValue::undefined());
6814                }
6815                if let Some(val) = pobj.get_own(atom) {
6816                    self.set_reg(dst, val);
6817
6818                    let ic_table_ptr = self.cached_ic_table_ptr;
6819                    if !ic_table_ptr.is_null() {
6820                        let pc = ic_pc;
6821                        let pseudo_id = if obj_val.is_string() {
6822                            PRIM_STRING_SHAPE_ID
6823                        } else {
6824                            PRIM_NUMBER_SHAPE_ID
6825                        };
6826                        if let Some(offset) = pobj.find_offset(atom) {
6827                            unsafe {
6828                                (*ic_table_ptr).ensure_capacity(pc + 1);
6829                                if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
6830                                    ic.insert(
6831                                        crate::object::shape::ShapeId(pseudo_id),
6832                                        offset as u32,
6833                                        Some(ptr as usize),
6834                                    );
6835                                }
6836                            }
6837                        }
6838                    }
6839                    return Some(val);
6840                }
6841                current = pobj.prototype;
6842            }
6843            self.set_reg(dst, JSValue::undefined());
6844            return Some(JSValue::undefined());
6845        }
6846        if obj_val.is_object_like() {
6847            let ptr = obj_val.get_ptr();
6848            let js_obj = unsafe { JSValue::object_from_ptr(ptr) };
6849            let ic_table_ptr = self.cached_ic_table_ptr;
6850
6851            let pc = ic_pc;
6852            let mut ic_hit = false;
6853            if let Some(shape_id) = js_obj.get_shape_id() {
6854                if !ic_table_ptr.is_null() {
6855                    let ic_table = unsafe { &*ic_table_ptr };
6856                    if let Some(ic) = ic_table.get(pc) {
6857                        if let Some((offset, proto_ptr)) = ic.get(shape_id) {
6858                            if offset == u32::MAX && proto_ptr.is_none() {
6859                                self.set_reg(dst, JSValue::undefined());
6860                                ic_hit = true;
6861                            } else {
6862                                let val = if let Some(proto) = proto_ptr {
6863                                    let mut proto_matches = js_obj
6864                                        .prototype
6865                                        .map(|p| !p.is_null() && p as usize == proto)
6866                                        .unwrap_or(false);
6867                                    if !proto_matches {
6868                                        let mut cur = js_obj.prototype;
6869                                        let mut depth = 0u32;
6870                                        while let Some(p) = cur {
6871                                            if p.is_null() || depth > 1000 {
6872                                                break;
6873                                            }
6874                                            if p as usize == proto {
6875                                                proto_matches = true;
6876                                                break;
6877                                            }
6878                                            depth += 1;
6879                                            unsafe {
6880                                                cur = (*p).prototype;
6881                                            }
6882                                        }
6883                                    }
6884                                    if proto_matches {
6885                                        let proto_obj = unsafe {
6886                                            &*(proto as *const crate::object::object::JSObject)
6887                                        };
6888                                        proto_obj.get_by_offset(offset as usize)
6889                                    } else {
6890                                        None
6891                                    }
6892                                } else {
6893                                    js_obj.get_by_offset(offset as usize)
6894                                };
6895                                if let Some(v) = val {
6896                                    self.set_reg(dst, v);
6897                                    ic_hit = true;
6898                                }
6899                            }
6900                        }
6901                    }
6902                }
6903            }
6904
6905            if !ic_hit {
6906                if let Some(shape_id) = js_obj.get_shape_id() {
6907                    if let Some(offset) = js_obj.find_offset(atom) {
6908                        let v = js_obj.get_by_offset(offset).unwrap_or(JSValue::undefined());
6909                        self.set_reg(dst, v);
6910                        if !ic_table_ptr.is_null() {
6911                            unsafe {
6912                                (*ic_table_ptr).ensure_capacity(pc + 1);
6913                                if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
6914                                    ic.insert(shape_id, offset as u32, None);
6915                                }
6916                            }
6917                        }
6918                    } else {
6919                        if let Some(getter) = js_obj.get_own_accessor_value(atom) {
6920                            if getter.is_function() {
6921                                match self.call_function_with_this(ctx, getter, obj_val, &[]) {
6922                                    Ok(ret) => {
6923                                        self.set_reg(dst, ret);
6924                                    }
6925                                    Err(msg) => {
6926                                        return self.throw_reference_error(ctx, &msg);
6927                                    }
6928                                }
6929                            } else {
6930                                self.set_pending_type_error(
6931                                    ctx,
6932                                    "Property getter is not a function",
6933                                );
6934                            }
6935                        } else {
6936                            let mut current = js_obj.prototype;
6937                            let mut depth = 0u32;
6938                            let mut found = false;
6939                            while let Some(proto_ptr) = current {
6940                                if proto_ptr.is_null() || depth > 1000 {
6941                                    break;
6942                                }
6943                                depth += 1;
6944                                unsafe {
6945                                    let proto = &*proto_ptr;
6946                                    if let Some(offset) = proto.find_offset(atom) {
6947                                        let v = proto
6948                                            .get_by_offset(offset)
6949                                            .unwrap_or(JSValue::undefined());
6950                                        self.set_reg(dst, v);
6951                                        if !ic_table_ptr.is_null() {
6952                                            (*ic_table_ptr).ensure_capacity(pc + 1);
6953                                            if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
6954                                                ic.insert(
6955                                                    shape_id,
6956                                                    offset as u32,
6957                                                    Some(proto_ptr as usize),
6958                                                );
6959                                            }
6960                                        }
6961                                        found = true;
6962                                        break;
6963                                    }
6964                                    if let Some(getter) = proto.get_own_accessor_value(atom) {
6965                                        if getter.is_function() {
6966                                            match self.call_function_with_this(
6967                                                ctx,
6968                                                getter,
6969                                                obj_val,
6970                                                &[],
6971                                            ) {
6972                                                Ok(ret) => {
6973                                                    self.set_reg(dst, ret);
6974                                                }
6975                                                Err(msg) => {
6976                                                    let mut err =
6977                                                        crate::object::object::JSObject::new();
6978                                                    err.set(
6979                                                        ctx.intern("name"),
6980                                                        JSValue::new_string(
6981                                                            ctx.intern("ReferenceError"),
6982                                                        ),
6983                                                    );
6984                                                    err.set(
6985                                                        ctx.intern("message"),
6986                                                        JSValue::new_string(ctx.intern(&msg)),
6987                                                    );
6988                                                    if let Some(proto) =
6989                                                        ctx.get_reference_error_prototype()
6990                                                    {
6991                                                        err.prototype = Some(proto);
6992                                                    }
6993                                                    let ptr = Box::into_raw(Box::new(err)) as usize;
6994                                                    ctx.runtime_mut().gc_heap_mut().track(ptr);
6995                                                    self.pending_throw =
6996                                                        Some(JSValue::new_object(ptr));
6997                                                    return None;
6998                                                }
6999                                            }
7000                                        } else {
7001                                            self.set_pending_type_error(
7002                                                ctx,
7003                                                "Property getter is not a function",
7004                                            );
7005                                        }
7006                                        found = true;
7007                                        break;
7008                                    }
7009                                    current = proto.prototype;
7010                                }
7011                            }
7012                            if !found && obj_val.is_function() && js_obj.prototype.is_none() {
7013                                if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
7014                                    let fn_proto = unsafe { &*fn_proto_ptr };
7015                                    if let Some(offset) = fn_proto.find_offset(atom) {
7016                                        let v = fn_proto
7017                                            .get_by_offset(offset)
7018                                            .unwrap_or(JSValue::undefined());
7019                                        self.set_reg(dst, v);
7020                                        found = true;
7021                                    } else if let Some(getter) =
7022                                        fn_proto.get_own_accessor_value(atom)
7023                                    {
7024                                        if getter.is_function() {
7025                                            match self.call_function_with_this(
7026                                                ctx,
7027                                                getter,
7028                                                obj_val,
7029                                                &[],
7030                                            ) {
7031                                                Ok(ret) => {
7032                                                    self.set_reg(dst, ret);
7033                                                }
7034                                                Err(msg) => {
7035                                                    let mut err =
7036                                                        crate::object::object::JSObject::new();
7037                                                    err.set(
7038                                                        ctx.intern("name"),
7039                                                        JSValue::new_string(
7040                                                            ctx.intern("ReferenceError"),
7041                                                        ),
7042                                                    );
7043                                                    err.set(
7044                                                        ctx.intern("message"),
7045                                                        JSValue::new_string(ctx.intern(&msg)),
7046                                                    );
7047                                                    if let Some(proto) =
7048                                                        ctx.get_reference_error_prototype()
7049                                                    {
7050                                                        err.prototype = Some(proto);
7051                                                    }
7052                                                    let ptr = Box::into_raw(Box::new(err)) as usize;
7053                                                    ctx.runtime_mut().gc_heap_mut().track(ptr);
7054                                                    self.pending_throw =
7055                                                        Some(JSValue::new_object(ptr));
7056                                                    return None;
7057                                                }
7058                                            }
7059                                        } else {
7060                                            self.set_pending_type_error(
7061                                                ctx,
7062                                                "Property getter is not a function",
7063                                            );
7064                                        }
7065                                        found = true;
7066                                    }
7067                                }
7068                            }
7069                            if !found {
7070                                self.set_reg(dst, JSValue::undefined());
7071                                if !ic_table_ptr.is_null() {
7072                                    unsafe {
7073                                        (*ic_table_ptr).ensure_capacity(pc + 1);
7074                                        if let Some(ic) = (*ic_table_ptr).get_mut(pc) {
7075                                            ic.insert(shape_id, u32::MAX, None);
7076                                        }
7077                                    }
7078                                }
7079                            }
7080                        }
7081                    }
7082                } else {
7083                    let mut value = JSValue::undefined();
7084                    let mut found = false;
7085
7086                    if let Some(offset) = js_obj.find_offset(atom) {
7087                        value = js_obj.get_by_offset(offset).unwrap_or(JSValue::undefined());
7088                        found = true;
7089                    } else if let Some(getter) = js_obj.get_own_accessor_value(atom) {
7090                        value = if getter.is_function() {
7091                            match self.call_function_with_this(ctx, getter, obj_val, &[]) {
7092                                Ok(ret) => ret,
7093                                Err(msg) => {
7094                                    self.set_pending_type_error(ctx, &msg);
7095                                    JSValue::undefined()
7096                                }
7097                            }
7098                        } else {
7099                            self.set_pending_type_error(ctx, "Property getter is not a function");
7100                            JSValue::undefined()
7101                        };
7102                        found = true;
7103                    } else {
7104                        let mut current = js_obj.prototype;
7105                        let mut depth = 0u32;
7106                        while let Some(proto_ptr) = current {
7107                            if proto_ptr.is_null() || depth > 1000 {
7108                                break;
7109                            }
7110                            depth += 1;
7111                            unsafe {
7112                                let proto = &*proto_ptr;
7113                                if let Some(offset) = proto.find_offset(atom) {
7114                                    value =
7115                                        proto.get_by_offset(offset).unwrap_or(JSValue::undefined());
7116                                    found = true;
7117                                    break;
7118                                }
7119                                if let Some(getter) = proto.get_own_accessor_value(atom) {
7120                                    value = if getter.is_function() {
7121                                        match self.call_function_with_this(
7122                                            ctx,
7123                                            getter,
7124                                            obj_val,
7125                                            &[],
7126                                        ) {
7127                                            Ok(ret) => ret,
7128                                            Err(msg) => {
7129                                                self.set_pending_type_error(ctx, &msg);
7130                                                JSValue::undefined()
7131                                            }
7132                                        }
7133                                    } else {
7134                                        self.set_pending_type_error(
7135                                            ctx,
7136                                            "Property getter is not a function",
7137                                        );
7138                                        JSValue::undefined()
7139                                    };
7140                                    found = true;
7141                                    break;
7142                                }
7143                                current = proto.prototype;
7144                            }
7145                        }
7146                    }
7147
7148                    if !found && obj_val.is_function() && js_obj.prototype.is_none() {
7149                        if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
7150                            let fn_proto = unsafe { &*fn_proto_ptr };
7151                            if let Some(offset) = fn_proto.find_offset(atom) {
7152                                value = fn_proto
7153                                    .get_by_offset(offset)
7154                                    .unwrap_or(JSValue::undefined());
7155                                found = true;
7156                            } else if let Some(getter) = fn_proto.get_own_accessor_value(atom) {
7157                                value = if getter.is_function() {
7158                                    match self.call_function_with_this(ctx, getter, obj_val, &[]) {
7159                                        Ok(ret) => ret,
7160                                        Err(msg) => {
7161                                            self.set_pending_type_error(ctx, &msg);
7162                                            JSValue::undefined()
7163                                        }
7164                                    }
7165                                } else {
7166                                    self.set_pending_type_error(
7167                                        ctx,
7168                                        "Property getter is not a function",
7169                                    );
7170                                    JSValue::undefined()
7171                                };
7172                                found = true;
7173                            }
7174                        }
7175                    }
7176
7177                    if found {
7178                        self.set_reg(dst, value);
7179                    } else {
7180                        self.set_reg(dst, JSValue::undefined());
7181                    }
7182                }
7183            }
7184
7185            None
7186        } else if obj_val.is_string() {
7187            if atom == ctx.common_atoms.length {
7188                Some(JSValue::new_int(
7189                    ctx.string_char_count(obj_val.get_atom()) as i64
7190                ))
7191            } else if let Some(proto_ptr) = ctx.get_string_prototype() {
7192                let proto_obj = unsafe { &*proto_ptr };
7193                Some(proto_obj.get(atom).unwrap_or(JSValue::undefined()))
7194            } else {
7195                Some(JSValue::undefined())
7196            }
7197        } else if obj_val.is_int() || obj_val.is_float() || obj_val.is_bool() {
7198            if let Some(proto_ptr) = ctx.get_number_prototype() {
7199                let proto_obj = unsafe { &*proto_ptr };
7200                Some(proto_obj.get(atom).unwrap_or(JSValue::undefined()))
7201            } else {
7202                Some(JSValue::undefined())
7203            }
7204        } else if obj_val.is_symbol() {
7205            let prop_str = ctx.get_atom_str(atom);
7206            if prop_str == "description" {
7207                let desc_atom = obj_val.get_atom();
7208                if desc_atom.0 == ctx.common_atoms.empty.0 {
7209                    Some(JSValue::undefined())
7210                } else {
7211                    Some(JSValue::new_string(desc_atom))
7212                }
7213            } else if let Some(proto_ptr) = ctx.get_symbol_prototype() {
7214                let proto = unsafe { &*proto_ptr };
7215                Some(proto.get(atom).unwrap_or(JSValue::undefined()))
7216            } else {
7217                Some(JSValue::undefined())
7218            }
7219        } else {
7220            Some(JSValue::undefined())
7221        }
7222    }
7223
7224    #[inline(always)]
7225    fn get_named_prop_fast(
7226        &mut self,
7227        ctx: &mut JSContext,
7228        dst: u16,
7229        obj_reg: u16,
7230        atom: crate::runtime::atom::Atom,
7231        instr_pc: usize,
7232    ) -> bool {
7233        let obj_val = self.get_reg(obj_reg);
7234
7235        if obj_val.is_object_like() {
7236            let js_obj = unsafe { crate::value::JSValue::object_from_ptr(obj_val.get_ptr()) };
7237
7238            if js_obj.is_dense_array() && atom == ctx.common_atoms.length {
7239                if js_obj.props_len() > 0 {
7240                    self.set_reg(dst, js_obj.get_by_offset_fast(0));
7241                } else {
7242                    self.set_reg(dst, crate::value::JSValue::new_int(0));
7243                }
7244                return true;
7245            }
7246            let shape_id = js_obj.shape_id_cache;
7247            if shape_id != usize::MAX {
7248                let ic_table_ptr = self.cached_ic_table_ptr;
7249                if !ic_table_ptr.is_null() {
7250                    let (ic_hit, r0_offset, r0_proto) =
7251                        unsafe { (*ic_table_ptr).get_reads0_values(instr_pc, shape_id) };
7252                    if ic_hit {
7253                        if r0_proto == 0 {
7254                            let val = if r0_offset == u32::MAX {
7255                                crate::value::JSValue::undefined()
7256                            } else {
7257                                let off = r0_offset as usize;
7258
7259                                if off < crate::object::object::INLINE_PROPS
7260                                    && js_obj.has_no_deleted_props()
7261                                {
7262                                    js_obj.get_by_offset_fast(off)
7263                                } else if let Some(v) = js_obj.get_by_offset(off) {
7264                                    v
7265                                } else {
7266                                    return self
7267                                        .get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc);
7268                                }
7269                            };
7270                            self.set_reg(dst, val);
7271                            return true;
7272                        } else {
7273                            if let Some(proto_raw) = js_obj.prototype {
7274                                if proto_raw as usize == r0_proto {
7275                                    let proto_obj = unsafe { &*proto_raw };
7276                                    let off = r0_offset as usize;
7277                                    let v = if off < crate::object::object::INLINE_PROPS
7278                                        && proto_obj.has_no_deleted_props()
7279                                    {
7280                                        Some(proto_obj.get_by_offset_fast(off))
7281                                    } else {
7282                                        proto_obj.get_by_offset(off)
7283                                    };
7284                                    if let Some(v) = v {
7285                                        self.set_reg(dst, v);
7286                                        return true;
7287                                    }
7288                                    return self
7289                                        .get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc);
7290                                }
7291                            }
7292                            return self.get_inherited_fast(
7293                                ctx, dst, obj_val, atom, instr_pc, r0_offset, r0_proto,
7294                            );
7295                        }
7296                    } else {
7297                        return self
7298                            .get_named_prop_poly_hit(ctx, dst, obj_val, atom, instr_pc, shape_id);
7299                    }
7300                }
7301            }
7302        }
7303        self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc)
7304    }
7305
7306    #[inline(never)]
7307    fn get_named_prop_poly_hit(
7308        &mut self,
7309        ctx: &mut JSContext,
7310        dst: u16,
7311        obj_val: JSValue,
7312        atom: crate::runtime::atom::Atom,
7313        instr_pc: usize,
7314        shape_id: usize,
7315    ) -> bool {
7316        let ic_table_ptr = self.cached_ic_table_ptr;
7317        if !ic_table_ptr.is_null() {
7318            let (ic_hit, r_offset, r_proto) =
7319                unsafe { (*ic_table_ptr).get_reads123_values(instr_pc, shape_id) };
7320            if ic_hit {
7321                let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
7322                if r_proto == 0 {
7323                    let val = if r_offset == u32::MAX {
7324                        JSValue::undefined()
7325                    } else {
7326                        let off = r_offset as usize;
7327                        if off < crate::object::object::INLINE_PROPS
7328                            && js_obj.has_no_deleted_props()
7329                        {
7330                            js_obj.get_by_offset_fast(off)
7331                        } else if let Some(v) = js_obj.get_by_offset(off) {
7332                            v
7333                        } else {
7334                            return self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc);
7335                        }
7336                    };
7337                    self.set_reg(dst, val);
7338                    return true;
7339                } else {
7340                    if let Some(p) = js_obj.prototype {
7341                        if p as usize == r_proto {
7342                            let proto_obj = unsafe { &*p };
7343                            let off = r_offset as usize;
7344                            let v = if off < crate::object::object::INLINE_PROPS
7345                                && proto_obj.has_no_deleted_props()
7346                            {
7347                                Some(proto_obj.get_by_offset_fast(off))
7348                            } else {
7349                                proto_obj.get_by_offset(off)
7350                            };
7351                            if let Some(v) = v {
7352                                self.set_reg(dst, v);
7353                                return true;
7354                            }
7355                        }
7356                    }
7357
7358                    return self
7359                        .get_inherited_fast(ctx, dst, obj_val, atom, instr_pc, r_offset, r_proto);
7360                }
7361            }
7362        }
7363        self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc)
7364    }
7365
7366    #[inline(never)]
7367    fn get_inherited_fast(
7368        &mut self,
7369        ctx: &mut JSContext,
7370        dst: u16,
7371        obj_val: crate::value::JSValue,
7372        atom: crate::runtime::atom::Atom,
7373        instr_pc: usize,
7374        offset: u32,
7375        proto_ptr: usize,
7376    ) -> bool {
7377        let js_obj = unsafe { crate::value::JSValue::object_from_ptr(obj_val.get_ptr()) };
7378        let mut cur = js_obj.prototype;
7379        let mut depth = 0u32;
7380        while let Some(p) = cur {
7381            if depth > 1000 {
7382                break;
7383            }
7384            if p as usize == proto_ptr {
7385                let proto_obj = unsafe { &*p };
7386                if let Some(v) = proto_obj.get_by_offset(offset as usize) {
7387                    self.set_reg(dst, v);
7388                    return true;
7389                }
7390
7391                break;
7392            }
7393            depth += 1;
7394            cur = unsafe { (*p).prototype };
7395        }
7396
7397        self.get_named_prop_slow(ctx, dst, obj_val, atom, instr_pc)
7398    }
7399
7400    #[inline(never)]
7401    fn get_named_prop_slow(
7402        &mut self,
7403        ctx: &mut JSContext,
7404        dst: u16,
7405        obj_val: JSValue,
7406        atom: crate::runtime::atom::Atom,
7407        instr_pc: usize,
7408    ) -> bool {
7409        if (obj_val.is_string() && atom != ctx.common_atoms.length)
7410            || obj_val.is_int()
7411            || obj_val.is_float()
7412        {
7413            let pseudo_id = if obj_val.is_string() {
7414                PRIM_STRING_SHAPE_ID
7415            } else {
7416                PRIM_NUMBER_SHAPE_ID
7417            };
7418            let ic_table_ptr = self.cached_ic_table_ptr;
7419            if !ic_table_ptr.is_null() {
7420                let (ic_hit, r0_offset, r0_proto) =
7421                    unsafe { (*ic_table_ptr).get_reads0_values(instr_pc, pseudo_id) };
7422                if ic_hit {
7423                    if r0_proto != 0 {
7424                        let proto_obj =
7425                            unsafe { &*(r0_proto as *const crate::object::object::JSObject) };
7426                        if let Some(v) = proto_obj.get_by_offset(r0_offset as usize) {
7427                            self.set_reg(dst, v);
7428                            return true;
7429                        }
7430                    } else if r0_offset == u32::MAX {
7431                        self.set_reg(dst, JSValue::undefined());
7432                        return true;
7433                    }
7434                }
7435            }
7436        }
7437        if let Some(result) = self.get_named_prop_result(ctx, dst, obj_val, atom, instr_pc) {
7438            self.set_reg(dst, result);
7439            return true;
7440        }
7441        false
7442    }
7443
7444    #[inline(always)]
7445    fn set_named_prop_fast(
7446        &mut self,
7447        ctx: &mut JSContext,
7448        obj_reg: u16,
7449        val_reg: u16,
7450        atom: crate::runtime::atom::Atom,
7451        instr_pc: usize,
7452    ) {
7453        let obj_val = self.get_reg(obj_reg);
7454        let value = self.get_reg(val_reg);
7455        self.set_named_prop(ctx, obj_val, value, atom, instr_pc);
7456    }
7457
7458    #[inline(always)]
7459    fn proto_chain_has_accessors(
7460        &self,
7461        obj_val: JSValue,
7462        atom: crate::runtime::atom::Atom,
7463    ) -> (bool, Option<JSValue>) {
7464        if obj_val.is_object_like() {
7465            let js_obj = unsafe { JSValue::object_from_ptr(obj_val.get_ptr()) };
7466            if let Some(entry) = js_obj.get_own_accessor_entry(atom) {
7467                return (true, entry.set);
7468            }
7469            let mut proto = js_obj.prototype;
7470            while let Some(proto_ptr) = proto {
7471                if proto_ptr.is_null() {
7472                    break;
7473                }
7474                let proto_obj = unsafe { &*proto_ptr };
7475                if let Some(entry) = proto_obj.get_own_accessor_entry(atom) {
7476                    return (true, entry.set);
7477                }
7478                proto = proto_obj.prototype;
7479            }
7480        }
7481        (false, None)
7482    }
7483
7484    #[inline(always)]
7485    fn set_named_prop(
7486        &mut self,
7487        ctx: &mut JSContext,
7488        obj_val: JSValue,
7489        value: JSValue,
7490        atom: crate::runtime::atom::Atom,
7491        ic_pc: usize,
7492    ) {
7493        if obj_val.is_object_like() {
7494            let ptr = obj_val.get_ptr();
7495            let js_obj = unsafe { JSValue::object_from_ptr_mut(ptr) };
7496            let ic_table_ptr = self.cached_ic_table_ptr;
7497
7498            if let Some(shape_id) = js_obj.get_shape_id() {
7499                if !ic_table_ptr.is_null() {
7500                    let ic_table = unsafe { &*ic_table_ptr };
7501                    if let Some(ic) = ic_table.get(ic_pc) {
7502                        if let Some((offset, new_shape_ptr)) = ic.get_transition(shape_id) {
7503                            if new_shape_ptr.is_null() {
7504                                js_obj.set_by_offset(offset as usize, value);
7505                            } else {
7506                                let new_shape = unsafe {
7507                                    std::ptr::NonNull::new_unchecked(
7508                                        new_shape_ptr as *mut crate::object::shape::Shape,
7509                                    )
7510                                };
7511                                js_obj.push_prop_with_shape(
7512                                    offset as usize,
7513                                    atom,
7514                                    value,
7515                                    new_shape,
7516                                );
7517                            }
7518                            return;
7519                        }
7520                    }
7521                }
7522            }
7523
7524            let pre_offset = js_obj.find_offset(atom);
7525
7526            if !js_obj.is_prop_writable_at(pre_offset) {
7527                let msg = format!(
7528                    "Cannot assign to read only property '{}'",
7529                    ctx.get_atom_str(atom)
7530                );
7531                self.set_pending_type_error(ctx, &msg);
7532                return;
7533            }
7534
7535            let (has_accessor, setter) = self.proto_chain_has_accessors(obj_val, atom);
7536            if has_accessor {
7537                if let Some(setter_fn) = setter {
7538                    let _ = self.call_function_with_this(ctx, setter_fn, obj_val, &[value]);
7539                }
7540                return;
7541            }
7542
7543            if pre_offset.is_none() && !js_obj.extensible() {
7544                let msg = format!(
7545                    "Cannot define property '{}', object is not extensible",
7546                    ctx.get_atom_str(atom)
7547                );
7548                self.set_pending_type_error(ctx, &msg);
7549                return;
7550            }
7551
7552            self.set_named_prop_slow(ctx, obj_val, ptr, js_obj, value, atom, ic_pc);
7553        }
7554    }
7555
7556    #[cold]
7557    fn set_named_prop_slow(
7558        &mut self,
7559        ctx: &mut JSContext,
7560        obj_val: JSValue,
7561        ptr: usize,
7562        js_obj: &mut crate::object::object::JSObject,
7563        value: JSValue,
7564        atom: crate::runtime::atom::Atom,
7565        ic_pc: usize,
7566    ) {
7567        let ic_table_ptr = self.cached_ic_table_ptr;
7568
7569        let pre_shape_id = js_obj.get_shape_id();
7570        let pre_props_len = js_obj.props_len();
7571        let pre_offset = js_obj.find_offset(atom);
7572        js_obj.set_cached_with_offset(atom, value, ctx.shape_cache_mut(), pre_offset);
7573        if let Some(shape_id) = js_obj.get_shape_id() {
7574            let offset = pre_offset.or_else(|| js_obj.find_offset(atom));
7575            if let Some(offset) = offset {
7576                if !ic_table_ptr.is_null() && ic_pc != usize::MAX {
7577                    unsafe {
7578                        (*ic_table_ptr).ensure_capacity((ic_pc) + 1);
7579                        if let Some(ic) = (*ic_table_ptr).get_mut(ic_pc) {
7580                            let was_transition = offset == pre_props_len;
7581                            if was_transition {
7582                                if let Some(pre_id) = pre_shape_id {
7583                                    if let Some(new_shape_ptr) = js_obj.get_shape_ptr() {
7584                                        ic.insert_transition(pre_id, offset as u32, new_shape_ptr);
7585                                    } else {
7586                                        ic.insert(shape_id, offset as u32, None);
7587                                    }
7588                                } else {
7589                                    ic.insert(shape_id, offset as u32, None);
7590                                }
7591                            } else {
7592                                ic.insert_transition_null(shape_id, offset as u32);
7593                            }
7594                        }
7595                    }
7596                }
7597            }
7598        }
7599
7600        if obj_val.is_function() && atom == ctx.common_atoms.prototype {
7601            let js_func = unsafe { JSValue::function_from_ptr_mut(ptr) };
7602            if value.is_object() {
7603                js_func.cached_prototype_ptr =
7604                    value.get_ptr() as *mut crate::object::object::JSObject;
7605            } else {
7606                js_func.cached_prototype_ptr = std::ptr::null_mut();
7607            }
7608        }
7609
7610        if obj_val.is_function() && atom.0 & 0x40000000 != 0 {
7611            let js_func = unsafe { JSValue::function_from_ptr_mut(ptr) };
7612            js_func.mark_has_symbol_prop();
7613        }
7614    }
7615
7616    fn int_atom(&self, idx: usize, ctx: &mut JSContext) -> crate::runtime::atom::Atom {
7617        ctx.int_atom_mut(idx)
7618    }
7619
7620    #[cold]
7621    fn add_slow(&mut self, a: &JSValue, b: &JSValue, ctx: &mut JSContext) -> JSValue {
7622        if a.is_int() && b.is_float() {
7623            return JSValue::new_float_raw(a.get_int() as f64 + b.get_float());
7624        }
7625        if a.is_float() && b.is_int() {
7626            return JSValue::new_float_raw(a.get_float() + b.get_int() as f64);
7627        }
7628
7629        let a = self.ordinary_to_primitive(a, "default", ctx);
7630        if self.pending_throw.is_some() {
7631            return JSValue::undefined();
7632        }
7633        let b = self.ordinary_to_primitive(b, "default", ctx);
7634        if self.pending_throw.is_some() {
7635            return JSValue::undefined();
7636        }
7637
7638        if a.is_bigint() && b.is_bigint() {
7639            let a_int = Self::get_bigint_int(&a).unwrap_or(0);
7640            let b_int = Self::get_bigint_int(&b).unwrap_or(0);
7641            Self::create_bigint(a_int + b_int)
7642        } else if a.is_symbol() || b.is_symbol() {
7643            self.set_pending_type_error(ctx, "Cannot convert a Symbol value to a string");
7644            JSValue::undefined()
7645        } else if b.is_string() || a.is_string() {
7646            let a_str = if a.is_object() || a.is_function() {
7647                self.object_to_string(&a, ctx)
7648            } else {
7649                Self::js_to_string(&a, ctx)
7650            };
7651            let b_str = if b.is_object() || b.is_function() {
7652                self.object_to_string(&b, ctx)
7653            } else {
7654                Self::js_to_string(&b, ctx)
7655            };
7656            Self::js_add_string(&a_str, &b_str, ctx)
7657        } else if a.is_bigint() || b.is_bigint() {
7658            self.set_pending_type_error(ctx, "Cannot mix BigInt and other types");
7659            JSValue::undefined()
7660        } else if a.is_float() && b.is_float() {
7661            JSValue::new_float_raw(a.get_float() + b.get_float())
7662        } else if a.is_int() && b.is_float() {
7663            JSValue::new_float_raw(a.get_int() as f64 + b.get_float())
7664        } else if a.is_float() && b.is_int() {
7665            JSValue::new_float_raw(a.get_float() + b.get_int() as f64)
7666        } else {
7667            let fa = Self::js_to_number(&a, ctx);
7668            let fb = Self::js_to_number(&b, ctx);
7669            JSValue::new_float_raw(fa + fb)
7670        }
7671    }
7672
7673    fn get_method_for_primitive(
7674        &self,
7675        obj: &crate::object::JSObject,
7676        method_atom: crate::runtime::atom::Atom,
7677        ctx: &JSContext,
7678    ) -> Option<JSValue> {
7679        if let Some(v) = obj.get(method_atom) {
7680            return Some(v);
7681        }
7682
7683        if obj.obj_type() == crate::object::object::ObjectType::Function {
7684            if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
7685                unsafe {
7686                    if let Some(v) = (*fn_proto_ptr).get(method_atom) {
7687                        return Some(v);
7688                    }
7689                }
7690            }
7691        }
7692        None
7693    }
7694
7695    fn get_symbol_to_primitive_atom(
7696        &self,
7697        ctx: &mut JSContext,
7698    ) -> Option<crate::runtime::atom::Atom> {
7699        let global = ctx.global();
7700        if !global.is_object() {
7701            return None;
7702        }
7703        let sym_val = global.as_object().get(ctx.intern("Symbol.toPrimitive"))?;
7704        if !sym_val.is_symbol() {
7705            return None;
7706        }
7707        let sym_id = sym_val.get_symbol_id();
7708        Some(crate::runtime::atom::Atom(0x40000000 | sym_id))
7709    }
7710
7711    fn call_function_safe(
7712        &mut self,
7713        ctx: &mut JSContext,
7714        func: JSValue,
7715        this_val: JSValue,
7716        args: &[JSValue],
7717    ) -> Result<JSValue, String> {
7718        let saved_handlers = std::mem::take(&mut self.exception_handlers);
7719        let result = self.call_function_with_this(ctx, func, this_val, args);
7720        self.exception_handlers = saved_handlers;
7721        if let Some(exc) = ctx.pending_exception.take() {
7722            self.pending_throw = Some(exc);
7723            return Err("exception propagated".to_string());
7724        }
7725        result
7726    }
7727
7728    fn ordinary_to_primitive(&mut self, v: &JSValue, hint: &str, ctx: &mut JSContext) -> JSValue {
7729        if !v.is_object() && !v.is_function() {
7730            return *v;
7731        }
7732        let obj: &crate::object::JSObject = if v.is_object() {
7733            v.as_object()
7734        } else if v.is_function() {
7735            let func = v.as_function();
7736
7737            if let Some(custom_fn) = func.base.get_own_value(ctx.common_atoms.value_of) {
7738                if custom_fn.is_function() {
7739                    if let Ok(r) = self.call_function_with_this(ctx, custom_fn, *v, &[]) {
7740                        if !r.is_object() {
7741                            return r;
7742                        }
7743                    }
7744                }
7745            }
7746
7747            if let Some(custom_fn) = func.base.get_own_value(ctx.common_atoms.to_string) {
7748                if custom_fn.is_function() {
7749                    if let Ok(r) = self.call_function_with_this(ctx, custom_fn, *v, &[]) {
7750                        if !r.is_object() {
7751                            return r;
7752                        }
7753                    }
7754                }
7755            }
7756            let name = ctx.get_atom_str(func.name);
7757            let params: Vec<String> = (0..func.arity as usize)
7758                .map(|i| format!("a{}", i))
7759                .collect();
7760            let param_str = params.join(", ");
7761            let s = if name.is_empty() {
7762                format!("function({}) {{ [user code] }}", param_str)
7763            } else {
7764                format!("function {}({}) {{ [user code] }}", name, param_str)
7765            };
7766            return JSValue::new_string(ctx.intern(&s));
7767        } else {
7768            return *v;
7769        };
7770
7771        let mut has_tp = false;
7772
7773        let has_own_tp = self.get_symbol_to_primitive_atom(ctx).map_or(false, |a| {
7774            obj.get_own_value(a)
7775                .or_else(|| {
7776                    let mut cur = obj.prototype;
7777                    while let Some(p) = cur {
7778                        unsafe {
7779                            if let Some(v) = (*p).get_own_value(a) {
7780                                return Some(v);
7781                            }
7782                            cur = (*p).prototype;
7783                        }
7784                    }
7785                    None
7786                })
7787                .map_or(false, |v| v.is_function())
7788        });
7789        if has_own_tp {
7790            has_tp = true;
7791        } else if let Some(tp_atom) = self.get_symbol_to_primitive_atom(ctx) {
7792            let tp_val = {
7793                let saved_tp = std::mem::take(&mut self.exception_handlers);
7794                let result = obj
7795                    .get_own_descriptor(tp_atom)
7796                    .and_then(|desc| {
7797                        if let Some(getter) = desc.get {
7798                            self.call_function_with_this(ctx, getter, *v, &[]).ok()
7799                        } else {
7800                            desc.value
7801                        }
7802                    })
7803                    .or_else(|| {
7804                        let mut current = obj.prototype;
7805                        while let Some(proto_ptr) = current {
7806                            unsafe {
7807                                let proto = &*proto_ptr;
7808                                if let Some(desc) = proto.get_own_descriptor(tp_atom) {
7809                                    if let Some(getter) = desc.get {
7810                                        return self
7811                                            .call_function_with_this(ctx, getter, *v, &[])
7812                                            .ok();
7813                                    }
7814                                    return desc.value;
7815                                }
7816                                current = proto.prototype;
7817                            }
7818                        }
7819                        None
7820                    });
7821                self.exception_handlers = saved_tp;
7822                result
7823            };
7824            if self.pending_throw.is_some() {
7825                return JSValue::undefined();
7826            }
7827            has_tp = tp_val.map_or(false, |v| v.is_function());
7828        }
7829        if !has_tp {
7830            if let Some(prim) = obj.get(ctx.common_atoms.__value__) {
7831                if !prim.is_object() {
7832                    return prim;
7833                }
7834            }
7835        }
7836
7837        if let Some(tp_atom) = self.get_symbol_to_primitive_atom(ctx) {
7838            let tp_method = (|| -> Option<JSValue> {
7839                let from_own = obj.get_own_descriptor(tp_atom).and_then(|desc| {
7840                    if let Some(getter) = desc.get {
7841                        let saved = std::mem::take(&mut self.exception_handlers);
7842                        let result = self.call_function_with_this(ctx, getter, *v, &[]).ok();
7843                        self.exception_handlers = saved;
7844                        result
7845                    } else {
7846                        desc.value
7847                    }
7848                });
7849                if from_own.is_some() {
7850                    return from_own;
7851                }
7852
7853                let mut current = obj.prototype;
7854                while let Some(proto_ptr) = current {
7855                    unsafe {
7856                        let proto = &*proto_ptr;
7857                        if let Some(desc) = proto.get_own_descriptor(tp_atom) {
7858                            if let Some(getter) = desc.get {
7859                                let saved = std::mem::take(&mut self.exception_handlers);
7860                                let result =
7861                                    self.call_function_with_this(ctx, getter, *v, &[]).ok();
7862                                self.exception_handlers = saved;
7863                                return result;
7864                            }
7865                            return desc.value;
7866                        }
7867                        current = proto.prototype;
7868                    }
7869                }
7870                None
7871            })();
7872            if let Some(tp_fn) = tp_method {
7873                if tp_fn.is_function() {
7874                    let hint_atom = ctx.intern(hint);
7875                    let result =
7876                        self.call_function_safe(ctx, tp_fn, *v, &[JSValue::new_string(hint_atom)]);
7877                    match result {
7878                        Ok(r) if !r.is_object() => return r,
7879                        Ok(_) => {
7880                            self.set_pending_type_error(
7881                                ctx,
7882                                "Cannot convert object to primitive value",
7883                            );
7884                            return JSValue::undefined();
7885                        }
7886                        Err(_) => {
7887                            return JSValue::undefined();
7888                        }
7889                    }
7890                }
7891            }
7892        }
7893
7894        let hint = if hint == "default" { "number" } else { hint };
7895        let to_string_atom = ctx.common_atoms.to_string;
7896        let value_of_atom = ctx.common_atoms.value_of;
7897        let (first_try, second_try) = if hint == "string" {
7898            (to_string_atom, value_of_atom)
7899        } else {
7900            (value_of_atom, to_string_atom)
7901        };
7902        for &method_atom in &[first_try, second_try] {
7903            if let Some(f) = self.get_method_for_primitive(obj, method_atom, ctx) {
7904                if f.is_function() {
7905                    let result = self.call_function_safe(ctx, f, *v, &[]);
7906                    match result {
7907                        Ok(r) if !r.is_object() => return r,
7908                        Ok(_) => {}
7909                        Err(_) => {
7910                            return JSValue::undefined();
7911                        }
7912                    }
7913                }
7914            }
7915        }
7916
7917        if v.is_function() {
7918            let func = v.as_function();
7919            let name = ctx.get_atom_str(func.name);
7920            if !name.is_empty() {
7921                return JSValue::new_string(
7922                    ctx.intern(&format!("function {}() {{ [native code] }}", name)),
7923                );
7924            }
7925            return JSValue::new_string(ctx.intern(&format!("function() {{ [native code] }}")));
7926        }
7927
7928        let name_atom = ctx.common_atoms.name;
7929        let message_atom = ctx.common_atoms.message;
7930        let mut err = crate::object::object::JSObject::new();
7931        err.set(name_atom, JSValue::new_string(ctx.intern("TypeError")));
7932        err.set(
7933            message_atom,
7934            JSValue::new_string(ctx.intern("Cannot convert object to primitive value")),
7935        );
7936        if let Some(proto) = ctx.get_type_error_prototype() {
7937            err.prototype = Some(proto);
7938        }
7939        let ptr = Box::into_raw(Box::new(err)) as usize;
7940        ctx.runtime_mut().gc_heap_mut().track(ptr);
7941        self.pending_throw = Some(JSValue::new_object(ptr));
7942        JSValue::undefined()
7943    }
7944
7945    fn js_to_string(v: &JSValue, ctx: &mut JSContext) -> JSValue {
7946        if v.is_string() {
7947            return *v;
7948        }
7949        if v.is_int() {
7950            return JSValue::new_string(ctx.intern(&v.get_int().to_string()));
7951        }
7952        if v.is_float() {
7953            return JSValue::new_string(ctx.intern(&v.get_float().to_string()));
7954        }
7955        if v.is_bool() {
7956            return JSValue::new_string(ctx.intern(&v.get_bool().to_string()));
7957        }
7958        if v.is_null() {
7959            return JSValue::new_string(ctx.common_atoms.null);
7960        }
7961        if v.is_undefined() {
7962            return JSValue::new_string(ctx.common_atoms.undefined);
7963        }
7964        if v.is_bigint() {
7965            let val = VM::get_bigint_int(v).unwrap_or(0);
7966            return JSValue::new_string(ctx.intern(&val.to_string()));
7967        }
7968        if v.is_symbol() {
7969            return JSValue::new_string(ctx.intern("Symbol()"));
7970        }
7971        JSValue::new_string(ctx.intern(""))
7972    }
7973
7974    fn object_to_string(&mut self, v: &JSValue, ctx: &mut JSContext) -> JSValue {
7975        if !v.is_object() && !v.is_function() {
7976            return Self::js_to_string(v, ctx);
7977        }
7978        let obj: &crate::object::JSObject = if v.is_object() {
7979            v.as_object()
7980        } else if v.is_function() {
7981            &v.as_function().base
7982        } else {
7983            return Self::js_to_string(v, ctx);
7984        };
7985        let to_str_atom = ctx.common_atoms.to_string;
7986
7987        if v.is_function() {
7988            let fn_builtin = ctx.get_builtin_func("function_toString");
7989            if let Some(f) = fn_builtin {
7990                let result = f(ctx, &[*v]);
7991                if result.is_string() {
7992                    return result;
7993                }
7994            }
7995
7996            let func = v.as_function();
7997            let name = ctx.get_atom_str(func.name);
7998            if name.is_empty() {
7999                return JSValue::new_string(ctx.intern("function () { [native code] }"));
8000            } else {
8001                return JSValue::new_string(
8002                    ctx.intern(&format!("function {}() {{ [native code] }}", name)),
8003                );
8004            }
8005        }
8006        if let Some(to_str_fn) = obj.get(to_str_atom) {
8007            if to_str_fn.is_function() {
8008                match self.call_function_with_this(ctx, to_str_fn, *v, &[]) {
8009                    Ok(r) if r.is_string() => return r,
8010                    Ok(r) => return Self::js_to_string(&r, ctx),
8011                    Err(_) => {}
8012                }
8013            }
8014        }
8015
8016        let obj_proto = if let Some(p) = ctx.get_object_prototype() {
8017            p
8018        } else {
8019            return Self::js_to_string(v, ctx);
8020        };
8021        let to_str = unsafe { (*obj_proto).get(to_str_atom) };
8022        if let Some(f) = to_str {
8023            if f.is_function() {
8024                match self.call_function_with_this(ctx, f, *v, &[]) {
8025                    Ok(r) => return r,
8026                    Err(_) => {}
8027                }
8028            }
8029        }
8030        Self::js_to_string(v, ctx)
8031    }
8032
8033    fn js_to_number(v: &JSValue, ctx: &mut JSContext) -> f64 {
8034        if v.is_int() {
8035            return v.get_int() as f64;
8036        } else if v.is_float() {
8037            return v.get_float();
8038        } else if v.is_bool() {
8039            return if v.get_bool() { 1.0 } else { 0.0 };
8040        } else if v.is_null() {
8041            return 0.0;
8042        } else if v.is_undefined() {
8043            return f64::NAN;
8044        } else if v.is_string() {
8045            let s = ctx.get_atom_str(v.get_atom());
8046            return s.trim().parse::<f64>().unwrap_or(f64::NAN);
8047        } else if v.is_bigint() {
8048            let val = VM::get_bigint_int(v).unwrap_or(0);
8049            return val as f64;
8050        } else if v.is_symbol() {
8051            return f64::NAN;
8052        } else if v.is_object() {
8053            if let Some(prim) = v.as_object().get(ctx.common_atoms.__value__) {
8054                return Self::js_to_number(&prim, ctx);
8055            }
8056        }
8057        f64::NAN
8058    }
8059
8060    fn to_int32(na: f64) -> i32 {
8061        if na.is_nan() || na.is_infinite() || na == 0.0 {
8062            return 0;
8063        }
8064        let pos = na.abs().floor() as u64;
8065        let u32_val = if na > 0.0 {
8066            pos % 0x1_0000_0000
8067        } else {
8068            let rem = pos % 0x1_0000_0000;
8069            if rem == 0 { 0 } else { 0x1_0000_0000 - rem }
8070        };
8071        u32_val as i32
8072    }
8073
8074    fn get_bigint_int(v: &JSValue) -> Option<i128> {
8075        if v.is_bigint() {
8076            Some(v.as_object().get_bigint_value())
8077        } else {
8078            None
8079        }
8080    }
8081
8082    fn create_bigint(value: i128) -> JSValue {
8083        let mut bigint_obj = crate::object::object::JSObject::new_bigint();
8084        bigint_obj.set_bigint_value(value);
8085        let ptr = Box::into_raw(Box::new(bigint_obj)) as usize;
8086        JSValue::new_bigint(ptr)
8087    }
8088
8089    fn js_add_string(b: &JSValue, a: &JSValue, ctx: &mut JSContext) -> JSValue {
8090        if b.is_string() && a.is_string() {
8091            let atom = ctx.intern_concat_atoms(b.get_atom(), a.get_atom());
8092            return JSValue::new_string(atom);
8093        }
8094
8095        fn stringify(v: &JSValue, ctx: &JSContext) -> (Option<[u8; 24]>, usize, Option<String>) {
8096            if v.is_string() {
8097                let s = ctx.get_atom_str(v.get_atom());
8098                let bytes = s.as_bytes();
8099                let len = bytes.len();
8100                if len <= 24 {
8101                    let mut buf = [0u8; 24];
8102                    buf[..len].copy_from_slice(bytes);
8103                    return (Some(buf), len, None);
8104                } else {
8105                    return (None, len, Some(s.to_string()));
8106                }
8107            } else if v.is_int() {
8108                let n = v.get_int();
8109                let mut buf = [0u8; 24];
8110                let mut tmp = n;
8111                let mut len = 0;
8112                let negative = tmp < 0;
8113                if negative {
8114                    tmp = -tmp;
8115                }
8116                if tmp == 0 {
8117                    buf[0] = b'0';
8118                    len = 1;
8119                } else {
8120                    while tmp > 0 {
8121                        buf[len] = (tmp % 10) as u8 + b'0';
8122                        len += 1;
8123                        tmp /= 10;
8124                    }
8125                }
8126                if negative {
8127                    buf[len] = b'-';
8128                    len += 1;
8129                }
8130
8131                for i in 0..len / 2 {
8132                    buf.swap(i, len - 1 - i);
8133                }
8134                return (Some(buf), len, None);
8135            } else if v.is_float() {
8136                let f = v.get_float();
8137                let s = if f.fract() == 0.0 && f.abs() < 1e15 {
8138                    format!("{}", f as i64)
8139                } else {
8140                    format!("{}", f)
8141                };
8142                let bytes = s.as_bytes();
8143                let len = bytes.len();
8144                if len <= 24 {
8145                    let mut buf = [0u8; 24];
8146                    buf[..len].copy_from_slice(bytes);
8147                    return (Some(buf), len, None);
8148                } else {
8149                    return (None, len, Some(s));
8150                }
8151            } else if v.is_bool() {
8152                if v.get_bool() {
8153                    let mut buf = [0u8; 24];
8154                    buf[..4].copy_from_slice(b"true");
8155                    return (Some(buf), 4, None);
8156                } else {
8157                    let mut buf = [0u8; 24];
8158                    buf[..5].copy_from_slice(b"false");
8159                    return (Some(buf), 5, None);
8160                }
8161            } else if v.is_null() {
8162                let mut buf = [0u8; 24];
8163                buf[..4].copy_from_slice(b"null");
8164                return (Some(buf), 4, None);
8165            } else if v.is_object() || v.is_function() {
8166                let mut buf = [0u8; 24];
8167                let s = b"[object Object]";
8168                buf[..s.len()].copy_from_slice(s);
8169                return (Some(buf), s.len(), None);
8170            } else if v.is_bigint() {
8171                let val = crate::runtime::vm::VM::get_bigint_int(v);
8172                let n = val.unwrap_or(0);
8173                let mut buf = [0u8; 24];
8174                let s = n.to_string();
8175                let bytes = s.as_bytes();
8176                let len = bytes.len();
8177                if len <= 24 {
8178                    buf[..len].copy_from_slice(bytes);
8179                    (Some(buf), len, None)
8180                } else {
8181                    (None, len, Some(s))
8182                }
8183            } else {
8184                let mut buf = [0u8; 24];
8185                let s = b"undefined";
8186                buf[..s.len()].copy_from_slice(s);
8187                return (Some(buf), s.len(), None);
8188            }
8189        }
8190
8191        let (buf_b, len_b, str_b) = stringify(b, ctx);
8192        let (buf_a, len_a, str_a) = stringify(a, ctx);
8193
8194        let total = len_b + len_a;
8195        if total <= 128 {
8196            let mut combined = [0u8; 128];
8197            if let Some(buf) = buf_b {
8198                combined[..len_b].copy_from_slice(&buf[..len_b]);
8199            } else if let Some(ref s) = str_b {
8200                combined[..len_b].copy_from_slice(s.as_bytes());
8201            }
8202            if let Some(buf) = buf_a {
8203                combined[len_b..total].copy_from_slice(&buf[..len_a]);
8204            } else if let Some(ref s) = str_a {
8205                combined[len_b..total].copy_from_slice(s.as_bytes());
8206            }
8207            let atom = ctx.intern(unsafe { std::str::from_utf8_unchecked(&combined[..total]) });
8208            JSValue::new_string(atom)
8209        } else {
8210            let mut combined = String::with_capacity(total);
8211            if let Some(buf) = buf_b {
8212                combined.push_str(unsafe { std::str::from_utf8_unchecked(&buf[..len_b]) });
8213            } else if let Some(ref s) = str_b {
8214                combined.push_str(s);
8215            }
8216            if let Some(buf) = buf_a {
8217                combined.push_str(unsafe { std::str::from_utf8_unchecked(&buf[..len_a]) });
8218            } else if let Some(ref s) = str_a {
8219                combined.push_str(s);
8220            }
8221            let atom = ctx.intern(&combined);
8222            JSValue::new_string(atom)
8223        }
8224    }
8225}
8226
8227#[inline(always)]
8228fn loose_equal(ctx: &JSContext, a: JSValue, b: JSValue) -> bool {
8229    if a.raw_bits() == b.raw_bits() {
8230        return if a.is_float() {
8231            !a.get_float().is_nan()
8232        } else {
8233            true
8234        };
8235    }
8236
8237    if JSValue::both_int(&a, &b) {
8238        return false;
8239    }
8240
8241    if JSValue::both_object(&a, &b) {
8242        return false;
8243    }
8244
8245    if (a.is_object() || a.is_function()) && b.is_null_or_undefined() {
8246        return false;
8247    }
8248    if (b.is_object() || b.is_function()) && a.is_null_or_undefined() {
8249        return false;
8250    }
8251
8252    if a.is_null_or_undefined() {
8253        return b.is_null_or_undefined();
8254    }
8255    if b.is_null() || b.is_undefined() {
8256        return false;
8257    }
8258
8259    loose_equal_slow(ctx, a, b)
8260}
8261
8262#[cold]
8263fn loose_equal_slow(ctx: &JSContext, a: JSValue, b: JSValue) -> bool {
8264    if a.is_undefined() && b.is_undefined() {
8265        return true;
8266    }
8267    if a.is_null() && b.is_null() {
8268        return true;
8269    }
8270    if a.is_bool() && b.is_bool() {
8271        return a.get_bool() == b.get_bool();
8272    }
8273    if a.is_string() && b.is_string() {
8274        return a.get_atom().0 == b.get_atom().0;
8275    }
8276    if a.is_bigint() && b.is_bigint() {
8277        return a.strict_eq(&b);
8278    }
8279    if a.is_null() && b.is_undefined() || a.is_undefined() && b.is_null() {
8280        return true;
8281    }
8282
8283    if a.is_object() || a.is_function() {
8284        if (a.is_object() && b.is_object()) || (a.is_function() && b.is_function()) {
8285            return a.strict_eq(&b);
8286        }
8287
8288        return false;
8289    }
8290    if b.is_object() || b.is_function() {
8291        return false;
8292    }
8293
8294    if a.is_float() && b.is_float() {
8295        return a.get_float() == b.get_float();
8296    }
8297    if a.is_int() && b.is_float() {
8298        return (a.get_int() as f64) == b.get_float();
8299    }
8300    if a.is_float() && b.is_int() {
8301        return a.get_float() == (b.get_int() as f64);
8302    }
8303
8304    if a.is_bool() && (b.is_int() || b.is_float()) {
8305        return loose_equal(ctx, JSValue::new_int(if a.get_bool() { 1 } else { 0 }), b);
8306    }
8307    if (a.is_int() || a.is_float()) && b.is_bool() {
8308        return loose_equal(ctx, a, JSValue::new_int(if b.get_bool() { 1 } else { 0 }));
8309    }
8310
8311    if a.is_bool() && b.is_string() {
8312        let n = JSValue::new_int(if a.get_bool() { 1 } else { 0 });
8313        return loose_equal(ctx, n, b);
8314    }
8315    if a.is_string() && b.is_bool() {
8316        return loose_equal(ctx, b, a);
8317    }
8318
8319    if a.is_string() && (b.is_int() || b.is_float()) {
8320        let s = ctx.get_atom_str(a.get_atom());
8321        if let Ok(n) = s.parse::<f64>() {
8322            if n.is_nan() {
8323                return false;
8324            }
8325            if b.is_int() {
8326                return n == (b.get_int() as f64);
8327            }
8328            return n == b.get_float();
8329        }
8330        return false;
8331    }
8332    if (a.is_int() || a.is_float()) && b.is_string() {
8333        return loose_equal(ctx, b, a);
8334    }
8335
8336    false
8337}
8338
8339#[cfg(test)]
8340mod tests {
8341    use super::*;
8342
8343    fn make_bytecode(code: Vec<u8>, constants: Vec<JSValue>, locals_count: u32) -> Bytecode {
8344        Bytecode {
8345            code,
8346            constants,
8347            locals_count,
8348            param_count: 0,
8349            line_number_table: None,
8350            ic_table: crate::compiler::InlineCacheTable::new(),
8351            shared_ic_table_ptr: std::ptr::null_mut(),
8352            uses_arguments: false,
8353            is_strict: false,
8354            var_name_to_slot: std::rc::Rc::new(Vec::new()),
8355            nested_bytecodes: std::collections::HashMap::new(),
8356            is_simple_constructor: false,
8357            simple_constructor_props: Vec::new(),
8358            cached_constructor_final_shape: None,
8359            cached_constructor_atoms: Vec::new(),
8360            shared_code_ptr: std::ptr::null(),
8361            shared_code_len: 0,
8362            shared_const_ptr: std::ptr::null(),
8363            shared_const_len: 0,
8364        }
8365    }
8366
8367    fn emit_u16(code: &mut Vec<u8>, v: u16) {
8368        code.extend_from_slice(&v.to_le_bytes());
8369    }
8370
8371    fn emit_u32(code: &mut Vec<u8>, v: u32) {
8372        code.extend_from_slice(&v.to_le_bytes());
8373    }
8374
8375    fn emit_i32(code: &mut Vec<u8>, v: i32) {
8376        code.extend_from_slice(&v.to_le_bytes());
8377    }
8378
8379    fn run_bytecode(code: Vec<u8>, constants: Vec<JSValue>, locals_count: u32) -> JSValue {
8380        let bc = make_bytecode(code, constants, locals_count);
8381        let mut rt = JSRuntime::new();
8382        let mut ctx = JSContext::new(&mut rt);
8383        let mut vm = VM::new();
8384        match vm.execute(&mut ctx, &bc).unwrap() {
8385            ExecutionOutcome::Complete(v) => v,
8386            ExecutionOutcome::Yield(v) => v,
8387        }
8388    }
8389
8390    #[test]
8391    fn test_vm_new_encoding_add() {
8392        let mut code = Vec::new();
8393
8394        code.push(Opcode::LoadInt as u8);
8395        emit_u16(&mut code, 1);
8396        emit_i32(&mut code, 40);
8397
8398        code.push(Opcode::LoadInt as u8);
8399        emit_u16(&mut code, 2);
8400        emit_i32(&mut code, 2);
8401
8402        code.push(Opcode::Add as u8);
8403        emit_u16(&mut code, 3);
8404        emit_u16(&mut code, 1);
8405        emit_u16(&mut code, 2);
8406
8407        code.push(Opcode::Move as u8);
8408        emit_u16(&mut code, 0);
8409        emit_u16(&mut code, 3);
8410
8411        code.push(Opcode::End as u8);
8412
8413        let result = run_bytecode(code, vec![], 4);
8414        assert_eq!(result.get_int(), 42);
8415    }
8416
8417    #[test]
8418    fn test_vm_new_encoding_set_get_prop() {
8419        let mut rt = JSRuntime::new();
8420        let mut ctx = JSContext::new(&mut rt);
8421        let atom_x = ctx.atom_table_mut().intern("x");
8422
8423        let mut code = Vec::new();
8424
8425        code.push(Opcode::NewObject as u8);
8426        emit_u16(&mut code, 1);
8427
8428        code.push(Opcode::LoadConst as u8);
8429        emit_u16(&mut code, 2);
8430        emit_u32(&mut code, 0);
8431
8432        code.push(Opcode::LoadInt as u8);
8433        emit_u16(&mut code, 3);
8434        emit_i32(&mut code, 42);
8435
8436        code.push(Opcode::SetProp as u8);
8437        emit_u16(&mut code, 1);
8438        emit_u16(&mut code, 2);
8439        emit_u16(&mut code, 3);
8440
8441        code.push(Opcode::GetProp as u8);
8442        emit_u16(&mut code, 4);
8443        emit_u16(&mut code, 1);
8444        emit_u16(&mut code, 2);
8445
8446        code.push(Opcode::Move as u8);
8447        emit_u16(&mut code, 0);
8448        emit_u16(&mut code, 4);
8449
8450        code.push(Opcode::End as u8);
8451
8452        let bc = make_bytecode(code, vec![JSValue::new_string(atom_x)], 5);
8453        let mut vm = VM::new();
8454        let result = match vm.execute(&mut ctx, &bc).unwrap() {
8455            ExecutionOutcome::Complete(v) => v,
8456            ExecutionOutcome::Yield(v) => v,
8457        };
8458        assert_eq!(result.get_int(), 42);
8459    }
8460
8461    fn builtin_const_99(_ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
8462        JSValue::new_int(99)
8463    }
8464
8465    fn builtin_inc(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
8466        let v = args.first().copied().unwrap_or_else(JSValue::undefined);
8467        if v.is_int() {
8468            JSValue::new_int(v.get_int() + 1)
8469        } else {
8470            JSValue::new_int(1)
8471        }
8472    }
8473
8474    #[test]
8475    fn test_vm_call0_new_encoding() {
8476        let mut rt = JSRuntime::new();
8477        let mut ctx = JSContext::new(&mut rt);
8478
8479        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f0"), 0);
8480        func.builtin_func = Some(builtin_const_99);
8481        let func_ptr = Box::into_raw(Box::new(func)) as usize;
8482        ctx.runtime_mut().gc_heap_mut().track_function(func_ptr);
8483
8484        let mut code = Vec::new();
8485        code.push(Opcode::LoadConst as u8);
8486        emit_u16(&mut code, 1);
8487        emit_u32(&mut code, 0);
8488
8489        code.push(Opcode::Call0 as u8);
8490        emit_u16(&mut code, 2);
8491        emit_u16(&mut code, 1);
8492
8493        code.push(Opcode::Move as u8);
8494        emit_u16(&mut code, 0);
8495        emit_u16(&mut code, 2);
8496
8497        code.push(Opcode::End as u8);
8498
8499        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 3);
8500        let mut vm = VM::new();
8501        let result = match vm.execute(&mut ctx, &bc).unwrap() {
8502            ExecutionOutcome::Complete(v) => v,
8503            ExecutionOutcome::Yield(v) => v,
8504        };
8505        assert_eq!(result.get_int(), 99);
8506    }
8507
8508    #[test]
8509    fn test_vm_call1_new_encoding() {
8510        let mut rt = JSRuntime::new();
8511        let mut ctx = JSContext::new(&mut rt);
8512
8513        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f1"), 1);
8514        func.builtin_func = Some(builtin_inc);
8515        let func_ptr = Box::into_raw(Box::new(func)) as usize;
8516
8517        let mut code = Vec::new();
8518        code.push(Opcode::LoadConst as u8);
8519        emit_u16(&mut code, 1);
8520        emit_u32(&mut code, 0);
8521
8522        code.push(Opcode::LoadInt as u8);
8523        emit_u16(&mut code, 2);
8524        emit_i32(&mut code, 41);
8525
8526        code.push(Opcode::Call1 as u8);
8527        emit_u16(&mut code, 3);
8528        emit_u16(&mut code, 1);
8529        emit_u16(&mut code, 2);
8530
8531        code.push(Opcode::Move as u8);
8532        emit_u16(&mut code, 0);
8533        emit_u16(&mut code, 3);
8534
8535        code.push(Opcode::End as u8);
8536
8537        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 4);
8538        let mut vm = VM::new();
8539        let result = match vm.execute(&mut ctx, &bc).unwrap() {
8540            ExecutionOutcome::Complete(v) => v,
8541            ExecutionOutcome::Yield(v) => v,
8542        };
8543        assert_eq!(result.get_int(), 42);
8544    }
8545
8546    #[test]
8547    fn test_vm_call2_new_encoding() {
8548        let mut rt = JSRuntime::new();
8549        let mut ctx = JSContext::new(&mut rt);
8550
8551        fn builtin_add2(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
8552            let a = args.first().copied().unwrap_or_else(JSValue::undefined);
8553            let b = args.get(1).copied().unwrap_or_else(JSValue::undefined);
8554            JSValue::new_int(a.get_int() + b.get_int())
8555        }
8556
8557        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f2"), 2);
8558        func.builtin_func = Some(builtin_add2);
8559        let func_ptr = Box::into_raw(Box::new(func)) as usize;
8560
8561        let mut code = Vec::new();
8562        code.push(Opcode::LoadConst as u8);
8563        emit_u16(&mut code, 1);
8564        emit_u32(&mut code, 0);
8565
8566        code.push(Opcode::LoadInt as u8);
8567        emit_u16(&mut code, 2);
8568        emit_i32(&mut code, 40);
8569
8570        code.push(Opcode::LoadInt as u8);
8571        emit_u16(&mut code, 3);
8572        emit_i32(&mut code, 2);
8573
8574        code.push(Opcode::Call2 as u8);
8575        emit_u16(&mut code, 4);
8576        emit_u16(&mut code, 1);
8577        emit_u16(&mut code, 2);
8578        emit_u16(&mut code, 3);
8579
8580        code.push(Opcode::Move as u8);
8581        emit_u16(&mut code, 0);
8582        emit_u16(&mut code, 4);
8583
8584        code.push(Opcode::End as u8);
8585
8586        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 5);
8587        let mut vm = VM::new();
8588        let result = match vm.execute(&mut ctx, &bc).unwrap() {
8589            ExecutionOutcome::Complete(v) => v,
8590            ExecutionOutcome::Yield(v) => v,
8591        };
8592        assert_eq!(result.get_int(), 42);
8593    }
8594
8595    #[test]
8596    fn test_vm_call3_new_encoding() {
8597        let mut rt = JSRuntime::new();
8598        let mut ctx = JSContext::new(&mut rt);
8599
8600        fn builtin_add3(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
8601            let a = args.first().copied().unwrap_or_else(JSValue::undefined);
8602            let b = args.get(1).copied().unwrap_or_else(JSValue::undefined);
8603            let c = args.get(2).copied().unwrap_or_else(JSValue::undefined);
8604            JSValue::new_int(a.get_int() + b.get_int() + c.get_int())
8605        }
8606
8607        let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern("f3"), 3);
8608        func.builtin_func = Some(builtin_add3);
8609        let func_ptr = Box::into_raw(Box::new(func)) as usize;
8610
8611        let mut code = Vec::new();
8612        code.push(Opcode::LoadConst as u8);
8613        emit_u16(&mut code, 1);
8614        emit_u32(&mut code, 0);
8615
8616        code.push(Opcode::LoadInt as u8);
8617        emit_u16(&mut code, 2);
8618        emit_i32(&mut code, 39);
8619
8620        code.push(Opcode::LoadInt as u8);
8621        emit_u16(&mut code, 3);
8622        emit_i32(&mut code, 2);
8623
8624        code.push(Opcode::LoadInt as u8);
8625        emit_u16(&mut code, 4);
8626        emit_i32(&mut code, 1);
8627
8628        code.push(Opcode::Call3 as u8);
8629        emit_u16(&mut code, 5);
8630        emit_u16(&mut code, 1);
8631        emit_u16(&mut code, 2);
8632        emit_u16(&mut code, 3);
8633        emit_u16(&mut code, 4);
8634
8635        code.push(Opcode::Move as u8);
8636        emit_u16(&mut code, 0);
8637        emit_u16(&mut code, 5);
8638
8639        code.push(Opcode::End as u8);
8640
8641        let bc = make_bytecode(code, vec![JSValue::new_function(func_ptr)], 6);
8642        let mut vm = VM::new();
8643        let result = match vm.execute(&mut ctx, &bc).unwrap() {
8644            ExecutionOutcome::Complete(v) => v,
8645            ExecutionOutcome::Yield(v) => v,
8646        };
8647        assert_eq!(result.get_int(), 42);
8648    }
8649
8650    #[test]
8651    fn test_vm_inc_local_new_encoding() {
8652        let mut code = Vec::new();
8653
8654        code.push(Opcode::LoadInt as u8);
8655        emit_u16(&mut code, 1);
8656        emit_i32(&mut code, 41);
8657
8658        code.push(Opcode::IncLocal as u8);
8659        emit_u16(&mut code, 1);
8660
8661        code.push(Opcode::Move as u8);
8662        emit_u16(&mut code, 0);
8663        emit_u16(&mut code, 1);
8664
8665        code.push(Opcode::End as u8);
8666
8667        let result = run_bytecode(code, vec![], 2);
8668        assert_eq!(result.get_int(), 42);
8669    }
8670
8671    #[test]
8672    fn test_vm_dec_local_new_encoding() {
8673        let mut code = Vec::new();
8674
8675        code.push(Opcode::LoadInt as u8);
8676        emit_u16(&mut code, 1);
8677        emit_i32(&mut code, 41);
8678
8679        code.push(Opcode::DecLocal as u8);
8680        emit_u16(&mut code, 1);
8681
8682        code.push(Opcode::Move as u8);
8683        emit_u16(&mut code, 0);
8684        emit_u16(&mut code, 1);
8685
8686        code.push(Opcode::End as u8);
8687
8688        let result = run_bytecode(code, vec![], 2);
8689        assert_eq!(result.get_int(), 40);
8690    }
8691}