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}