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