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