1use crate::captured_output::out;
5use crate::captured_output::outln;
6use crate::data::Data;
7use crate::data::DataHash;
8use crate::data::FALSE;
9use crate::data::FUNCTION_TEXT;
10use crate::data::NULL;
11use crate::data::TRUE;
12use crate::data::format_float;
13use crate::embed::HostDispatch;
14use crate::embed::Value;
15use crate::errors::ErrType;
16use crate::errors::ErrorCtx;
17use crate::errors::RuntimeError;
18use crate::errors::call_site_name;
19use crate::gc::GcState;
20use crate::gc::LIST_ENTRY_BYTES;
21use crate::gc::MAP_ENTRY_BYTES;
22use crate::gc::MarkBits;
23use crate::gc::alloc_array;
24use crate::gc::alloc_map;
25use crate::gc::rescan_list;
26use crate::gc::rescan_map;
27use crate::gc::shade_overwritten;
28use crate::instr::Instr;
29use crate::instr::LibFunc;
30use crate::instr::LibFuncVoid;
31use crate::instr::type_code;
32#[cfg(not(target_arch = "wasm32"))]
33use crate::native::Entry;
34use crate::native::NativeCode;
35#[cfg(not(target_arch = "wasm32"))]
36use crate::native::NativeCtx;
37#[cfg(not(target_arch = "wasm32"))]
38use crate::native::abi;
39use crate::rt::DataType;
40use crate::rt::DynamicLibFn;
41use crate::rt::EnumType;
42use crate::rt::ErrorCatch;
43use crate::rt::HostFnSig;
44use crate::rt::Pools;
45use crate::rt::Span;
46use crate::rt::Struct;
47use indexmap::IndexMap;
48use lexical_core::FormattedSize;
49use memchr::memmem;
50use smol_strc::ToSmolStr;
51use std::cell::Cell;
52use std::fs;
53use std::hash::BuildHasherDefault;
54use std::hint::cold_path;
55use std::hint::unreachable_unchecked;
56use std::io::Write;
57use std::ops::Index;
58use std::ops::IndexMut;
59
60#[path = "ffi.rs"]
61mod ffi;
62
63#[cfg(target_arch = "wasm32")]
64use crate::errors::wasm_error;
65
66pub const CALL_DEPTH_LIMIT: usize = 1_000_000;
73
74pub type ObjectPool = Pool<Vec<Data>>;
75
76pub type CandelaMap = IndexMap<Data, Data, BuildHasherDefault<DataHash>>;
81pub type MapPool = Pool<CandelaMap>;
82
83#[inline(always)]
87#[must_use]
88pub fn same_key(a: Data, b: Data, strings: &StringPool) -> bool {
89 a == b || (a.is_large_str() && b.is_large_str() && a.as_str(strings) == b.as_str(strings))
90}
91
92pub struct KeyByText<'a> {
96 pub key: Data,
97 pub strings: &'a StringPool,
98}
99
100impl std::hash::Hash for KeyByText<'_> {
101 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
102 self.key.as_map_key(self.strings).hash(state);
103 }
104}
105
106impl indexmap::Equivalent<Data> for KeyByText<'_> {
107 fn equivalent(&self, stored: &Data) -> bool {
108 same_key(self.key, *stored, self.strings)
109 }
110}
111
112pub fn map_insert(
116 map: &mut CandelaMap,
117 key: Data,
118 value: Data,
119 strings: &StringPool,
120) -> Option<Data> {
121 if let Some(slot) = map.get_mut(&KeyByText { key, strings }) {
122 return Some(std::mem::replace(slot, value));
123 }
124 map.insert(key.as_map_key(strings), value);
125 None
126}
127
128const UNCOUNTED: u32 = u32::MAX;
130const NO_STRING: u32 = u32::MAX;
132
133const KEEP_FREED_STRING_CAPACITY: usize = 256;
135
136#[derive(Clone, Copy)]
138struct StrCursor {
139 id: u32,
140 char_idx: u32,
141 byte_off: u32,
142}
143
144const NO_CURSOR: StrCursor = StrCursor {
146 id: NO_STRING,
147 char_idx: 0,
148 byte_off: 0,
149};
150
151struct PooledStr {
154 text: String,
155 char_len: Cell<u32>,
156}
157
158pub struct StringPool {
176 strings: Vec<PooledStr>,
177 cursor: Cell<StrCursor>,
178 marks: MarkBits,
179 mark_limit: u32,
182}
183
184impl Default for StringPool {
185 fn default() -> Self {
186 Self::with_capacity(0)
187 }
188}
189
190impl StringPool {
191 #[must_use]
192 pub fn with_capacity(capacity: usize) -> Self {
193 Self {
194 strings: Vec::with_capacity(capacity),
195 cursor: Cell::new(NO_CURSOR),
196 marks: MarkBits::default(),
197 mark_limit: 0,
198 }
199 }
200 #[must_use]
201 pub fn from_strings(strings: Vec<String>) -> Self {
202 Self {
203 strings: strings
204 .into_iter()
205 .map(|text| PooledStr {
206 text,
207 char_len: Cell::new(UNCOUNTED),
208 })
209 .collect(),
210 cursor: Cell::new(NO_CURSOR),
211 marks: MarkBits::default(),
212 mark_limit: 0,
213 }
214 }
215 #[inline(always)]
216 #[must_use]
217 pub const fn len(&self) -> usize {
218 self.strings.len()
219 }
220 #[inline(always)]
221 #[must_use]
222 pub const fn is_empty(&self) -> bool {
223 self.strings.is_empty()
224 }
225 #[inline(always)]
226 pub fn push(&mut self, value: String) {
227 self.strings.push(PooledStr {
228 text: value,
229 char_len: Cell::new(UNCOUNTED),
230 });
231 }
232 #[inline(always)]
233 pub fn iter(&self) -> impl Iterator<Item = &String> {
234 self.strings.iter().map(|s| &s.text)
235 }
236 #[inline(always)]
239 pub fn get_mut(&mut self, index: usize) -> &mut String {
240 if self.cursor.get().id as usize == index {
241 self.cursor.set(NO_CURSOR);
242 }
243 let slot = unsafe { self.strings.get_unchecked_mut(index) };
244 slot.char_len.set(UNCOUNTED);
245 &mut slot.text
246 }
247 pub(crate) fn begin_marking(&mut self) {
249 self.marks.reset(self.len());
250 self.mark_limit = self.len() as u32;
251 }
252 pub(crate) const fn end_marking(&mut self) {
255 self.mark_limit = 0;
256 }
257 #[inline(always)]
259 pub(crate) fn shade(&mut self, id: usize) {
260 if id < self.mark_limit as usize {
261 self.marks.insert(id);
262 }
263 }
264 #[inline(always)]
268 pub(crate) fn shade_counted(&mut self, id: usize) -> usize {
269 if id < self.mark_limit as usize && !self.marks.insert(id) {
270 unsafe { self.strings.get_unchecked(id).text.capacity() }
271 } else {
272 0
273 }
274 }
275 pub(crate) const fn mark_limit(&self) -> usize {
277 self.mark_limit as usize
278 }
279 pub(crate) const fn marks(&self) -> &MarkBits {
281 &self.marks
282 }
283 pub fn release(&mut self, index: usize) {
287 let text = self.get_mut(index);
288 if text.capacity() > KEEP_FREED_STRING_CAPACITY {
289 *text = String::new();
290 } else {
291 text.clear();
292 }
293 }
294 #[inline(always)]
297 #[must_use]
298 pub fn char_len(&self, id: usize) -> usize {
299 let slot = unsafe { self.strings.get_unchecked(id) };
300 let remembered = slot.char_len.get();
301 if remembered == UNCOUNTED {
302 count_and_remember(slot)
303 } else {
304 remembered as usize
305 }
306 }
307 #[inline(always)]
310 #[must_use]
311 pub fn is_ascii(&self, id: usize) -> bool {
312 self.char_len(id) == self[id].len()
313 }
314 #[must_use]
318 pub fn byte_offset(&self, id: usize, char_idx: usize) -> usize {
319 let bytes = self[id].as_bytes();
320 let cursor = self.cursor.get();
321 let (mut at_char, mut at_byte) =
322 if cursor.id as usize == id && cursor.char_idx as usize <= char_idx {
323 (cursor.char_idx as usize, cursor.byte_off as usize)
324 } else {
325 (0, 0)
326 };
327 while at_char < char_idx {
328 at_byte += utf8_width(bytes[at_byte]);
329 at_char += 1;
330 }
331 self.cursor.set(StrCursor {
332 id: id as u32,
333 char_idx: at_char as u32,
334 byte_off: at_byte as u32,
335 });
336 at_byte
337 }
338}
339
340impl Index<usize> for StringPool {
341 type Output = String;
342 #[inline(always)]
343 fn index(&self, index: usize) -> &Self::Output {
344 unsafe { &self.strings.get_unchecked(index).text }
345 }
346}
347
348#[inline(always)]
352pub(crate) const fn utf8_width(first: u8) -> usize {
353 let leading = first.leading_ones() as usize;
354 if leading == 0 { 1 } else { leading }
355}
356
357#[cold]
360#[inline(never)]
361fn count_and_remember(slot: &PooledStr) -> usize {
362 let counted = count_chars(&slot.text);
363 if counted < UNCOUNTED as usize {
364 slot.char_len.set(counted as u32);
365 }
366 counted
367}
368
369#[must_use]
373pub(crate) fn count_chars(s: &str) -> usize {
374 s.as_bytes().iter().filter(|&&b| (b as i8) >= -0x40).count()
375}
376
377#[inline(always)]
380#[must_use]
381pub(crate) fn char_byte_offset(s: &str, char_idx: usize) -> usize {
382 let bytes = s.as_bytes();
383 let mut at_byte = 0;
384 for _ in 0..char_idx {
385 at_byte += utf8_width(bytes[at_byte]);
386 }
387 at_byte
388}
389
390#[inline(always)]
399fn str_eq(x: Data, y: Data, string_pool: &StringPool) -> bool {
400 if x.is_string() && y.is_string() {
401 x.as_str(string_pool) == y.as_str(string_pool)
402 } else {
403 x == y
404 }
405}
406
407#[inline(always)]
413#[must_use]
414pub const fn shift_count_in_range(count: i64) -> bool {
415 count >= 0 && count < i64::BITS as i64
416}
417
418fn has_type_code(v: Data, code: Data, obj_pool: &ObjectPool, map_pool: &MapPool) -> bool {
420 if code.is_array() {
422 let entry = &obj_pool[code.as_array()];
423 let held = |codes: Data, item: &Data| {
424 let codes = &obj_pool[codes.as_array()];
425 codes.is_empty()
426 || codes
427 .iter()
428 .any(|code| has_type_code(*item, *code, obj_pool, map_pool))
429 };
430 return match entry[0].as_int() {
431 type_code::LIST => {
432 v.is_array()
433 && !v.is_function()
434 && obj_pool[v.as_array()]
435 .iter()
436 .all(|item| held(entry[1], item))
437 }
438 type_code::MAP => {
439 v.is_map()
440 && map_pool[v.as_map()]
441 .iter()
442 .all(|(key, value)| held(entry[1], key) && held(entry[2], value))
443 }
444 _ => false,
445 };
446 }
447 let code = code.as_int();
448 let id = (code >> type_code::KIND_BITS) as u16;
449 match code & ((1 << type_code::KIND_BITS) - 1) {
450 type_code::INT => v.is_int(),
451 type_code::FLOAT => v.is_float(),
452 type_code::STRING => v.is_string(),
453 type_code::BOOL => v.is_bool(),
454 type_code::NULL => v.is_null(),
455 type_code::LIST => v.is_array() && !v.is_function(),
456 type_code::MAP => v.is_map(),
457 type_code::FUNCTION => v.is_function(),
458 type_code::STRUCT => v.is_struct() && v.struct_type_id() == id,
459 type_code::ENUM => v.is_enum() && v.enum_type_id() == id,
460 _ => false,
461 }
462}
463
464#[cold]
466fn type_codes_text(
467 codes: &[Data],
468 obj_pool: &ObjectPool,
469 structs: &[Struct],
470 enums: &[EnumType],
471) -> String {
472 let names: Vec<String> = codes
473 .iter()
474 .map(|code| {
475 let held = |codes: Data| {
478 let codes = &obj_pool[codes.as_array()];
479 if codes.is_empty() {
480 "any".to_owned()
481 } else {
482 type_codes_text(codes, obj_pool, structs, enums)
483 }
484 };
485 if code.is_array() {
486 let entry = &obj_pool[code.as_array()];
487 return if entry[0].as_int() == type_code::MAP {
488 format!("{{{}: {}}}", held(entry[1]), held(entry[2]))
489 } else {
490 let elements = held(entry[1]);
491 if elements.contains('|') {
492 format!("({elements})[]")
493 } else {
494 format!("{elements}[]")
495 }
496 };
497 }
498 let code = code.as_int();
499 let id = (code >> type_code::KIND_BITS) as usize;
500 match code & ((1 << type_code::KIND_BITS) - 1) {
501 type_code::INT => "int",
502 type_code::FLOAT => "float",
503 type_code::STRING => "string",
504 type_code::BOOL => "bool",
505 type_code::NULL => "null",
506 type_code::LIST => "list",
507 type_code::MAP => "map",
508 type_code::FUNCTION => "function",
509 type_code::STRUCT => structs.get(id).map_or("struct", |s| s.name.as_str()),
510 type_code::ENUM => enums.get(id).map_or("enum", |e| e.name.as_str()),
511 _ => "value",
512 }
513 .to_owned()
514 })
515 .collect();
516 names.join("|")
517}
518
519fn obj_eq(
520 x: Data,
521 y: Data,
522 obj_pool: &ObjectPool,
523 map_pool: &MapPool,
524 string_pool: &StringPool,
525) -> bool {
526 if x == y {
527 return true;
528 }
529 if x.tag() != y.tag() {
530 return false;
531 }
532 if x.is_string() && y.is_string() {
533 return x.as_str(string_pool) == y.as_str(string_pool);
534 }
535 if (x.is_array() || x.is_struct() || x.is_enum())
539 && (y.is_array() || y.is_struct() || y.is_enum())
540 {
541 let x_obj = &obj_pool[x.as_array()];
542 let y_obj = &obj_pool[y.as_array()];
543 if x_obj.len() != y_obj.len() {
544 return false;
545 }
546 if x_obj == y_obj {
547 return true;
548 }
549 for (x, y) in x_obj.iter().zip(y_obj) {
550 if !obj_eq(*x, *y, obj_pool, map_pool, string_pool) {
551 return false;
552 }
553 }
554 return true;
555 }
556 if x.is_map() && y.is_map() {
557 let x_map = &map_pool[x.as_map()];
558 let y_map = &map_pool[y.as_map()];
559 if x_map.len() != y_map.len() {
560 return false;
561 }
562 if x_map == y_map {
563 return true;
564 }
565 for (k, v) in x_map {
566 if !y_map
567 .get(&KeyByText {
568 key: *k,
569 strings: string_pool,
570 })
571 .is_some_and(|y_v| obj_eq(*v, *y_v, obj_pool, map_pool, string_pool))
572 {
573 return false;
574 }
575 }
576 return true;
577 }
578 false
579}
580
581struct CallFrame {
582 return_addr: u16,
583 return_reg: u16,
584 saved_callsite: Option<u16>,
589}
590
591#[cold]
601#[inline(never)]
602fn unwind_to_catch(
603 handle: &ErrorCatch,
604 call_frames: &[CallFrame],
605 callsite_registers: &[Vec<u16>],
606 recursion_stack: &mut RegisterFile,
607 regs: Regs,
608) {
609 let Some(aborted) = call_frames.get(handle.call_frames_len as usize) else {
610 return;
611 };
612 let base = handle.recursion_len as usize;
613 let saved_regs: &[u16] = match aborted.saved_callsite {
617 Some(callsite_id) => unsafe { callsite_registers.get_unchecked(callsite_id as usize) },
618 None => &[],
619 };
620 restore_registers(base, saved_regs, recursion_stack, regs);
621}
622
623#[cold]
633#[inline(never)]
634fn frame_room(m: &mut Machine<'_>, i: usize, regs: Regs) -> Option<usize> {
635 let depth = m.call_frames.len();
636 let room = CALL_DEPTH_LIMIT - m.native_below;
637 if depth >= room {
638 let instr = m.instr_at(i);
639 return Some(catch_error(
640 ErrType::CallDepthExceeded(call_site_name(m.err_ctx, instr), CALL_DEPTH_LIMIT),
641 instr,
642 &mut m.failure,
643 &mut m.error_handles,
644 m.handles_floor,
645 &mut m.call_frames,
646 &mut m.args,
647 m.callsite_registers,
648 &mut m.recursion_stack,
649 regs,
650 m.r,
651 m.obj_pool,
652 m.map_pool,
653 m.str_pool,
654 m.gc,
655 ));
656 }
657 m.call_frames.reserve_exact(depth.max(64).min(room - depth));
658 None
659}
660
661#[cold]
665#[inline(never)]
666fn grow_list(list: &mut Vec<Data>, gc: &mut GcState) {
667 let before = list.capacity();
668 list.reserve(1);
669 gc.note_bytes((list.capacity() - before) * LIST_ENTRY_BYTES);
670}
671
672#[inline(always)]
675fn map_insert_counted(
676 map: &mut CandelaMap,
677 key: Data,
678 value: Data,
679 strings: &StringPool,
680 gc: &mut GcState,
681) -> Option<Data> {
682 let before = map.capacity();
683 let old = map_insert(map, key, value, strings);
684 if map.capacity() != before {
685 gc.note_bytes(map.capacity().saturating_sub(before) * MAP_ENTRY_BYTES);
686 }
687 old
688}
689
690#[cold]
692#[inline(never)]
693fn grow_recursion_stack(stack: &mut Vec<Data>, count: usize) {
694 stack.reserve(count);
695}
696
697#[cold]
710#[inline(never)]
711fn catch_error(
712 err: ErrType,
713 instr: Instr,
714 failure: &mut Option<RuntimeError>,
715 error_handles: &mut Vec<ErrorCatch>,
716 floor: usize,
717 call_frames: &mut Vec<CallFrame>,
718 args: &mut Vec<u16>,
719 callsite_registers: &[Vec<u16>],
720 recursion_stack: &mut RegisterFile,
721 regs: Regs,
722 r: &RegisterFile,
723 obj_pool: &mut ObjectPool,
724 map_pool: &mut MapPool,
725 str_pool: &mut StringPool,
726 gc: &mut GcState,
727) -> usize {
728 if error_handles.len() <= floor {
729 *failure = Some(RuntimeError::new(instr, err));
730 return FAILED;
731 }
732 let caught = Data::string(
733 err.kind(),
734 obj_pool,
735 map_pool,
736 str_pool,
737 r,
738 recursion_stack,
739 gc,
740 );
741 resume_at_catch(
742 caught,
743 error_handles,
744 call_frames,
745 args,
746 callsite_registers,
747 recursion_stack,
748 regs,
749 )
750}
751
752#[cold]
756#[inline(never)]
757#[cfg(not(target_arch = "wasm32"))]
758fn catch_failure(m: &mut Machine<'_>, error: RuntimeError) -> usize {
759 if m.error_handles.len() <= m.handles_floor {
760 m.failure = Some(error);
761 return FAILED;
762 }
763 let caught = Data::string(
764 error.code.as_str(),
765 m.obj_pool,
766 m.map_pool,
767 m.str_pool,
768 m.r,
769 &m.recursion_stack,
770 m.gc,
771 );
772 resume_at_catch(
773 caught,
774 &mut m.error_handles,
775 &mut m.call_frames,
776 &mut m.args,
777 m.callsite_registers,
778 &mut m.recursion_stack,
779 m.regs,
780 )
781}
782
783fn resume_at_catch(
786 caught: Data,
787 error_handles: &mut Vec<ErrorCatch>,
788 call_frames: &mut Vec<CallFrame>,
789 args: &mut Vec<u16>,
790 callsite_registers: &[Vec<u16>],
791 recursion_stack: &mut RegisterFile,
792 mut regs: Regs,
793) -> usize {
794 let err_handle = error_handles.pop_unchecked();
795 unwind_to_catch(
796 &err_handle,
797 call_frames,
798 callsite_registers,
799 recursion_stack,
800 regs,
801 );
802 unsafe {
803 args.set_len(err_handle.args_len as usize);
804 call_frames.set_len(err_handle.call_frames_len as usize);
805 }
806 regs[err_handle.error_reg] = caught;
807 err_handle.catch_loc as usize
808}
809
810#[inline(always)]
815fn restore_saved_registers(
816 callsite: u16,
817 callsite_registers: &[Vec<u16>],
818 recursion_stack: &mut RegisterFile,
819 regs: Regs,
820) {
821 let saved = unsafe { callsite_registers.get_unchecked(callsite as usize) };
822 restore_registers(
823 recursion_stack.len() - saved.len(),
824 saved,
825 recursion_stack,
826 regs,
827 );
828}
829
830#[inline(always)]
833fn restore_registers(
834 base: usize,
835 saved_regs: &[u16],
836 recursion_stack: &mut RegisterFile,
837 mut regs: Regs,
838) {
839 let saved = unsafe { recursion_stack.0.as_ptr().add(base) };
840 if let [reg] = saved_regs {
843 regs[*reg] = unsafe { Data::load(saved) };
844 } else {
845 for (k, reg) in saved_regs.iter().enumerate() {
846 regs[*reg] = unsafe { Data::load(saved.add(k)) };
847 }
848 }
849 unsafe {
850 recursion_stack.0.set_len(base);
851 }
852}
853
854pub trait UncheckedVecOps<T> {
855 fn pop_unchecked(&mut self) -> T;
856 fn push_unchecked(&mut self, value: T);
858}
859pub trait UncheckedSliceOps<T: Copy> {
860 unsafe fn copy_from_slice_unchecked(&mut self, src: &[T]);
865}
866
867impl<T> UncheckedVecOps<T> for Vec<T> {
868 #[inline(always)]
869 fn pop_unchecked(&mut self) -> T {
870 debug_assert!(!self.is_empty());
871 let new_len = self.len() - 1;
872 unsafe {
873 self.set_len(new_len);
874 self.as_mut_ptr().add(new_len).read()
875 }
876 }
877 #[inline(always)]
878 fn push_unchecked(&mut self, value: T) {
879 debug_assert!(self.len() < self.capacity());
880 unsafe {
881 self.as_mut_ptr().add(self.len()).write(value);
882 self.set_len(self.len() + 1);
883 }
884 }
885}
886
887impl<T: Copy> UncheckedSliceOps<T> for [T] {
888 #[inline(always)]
889 unsafe fn copy_from_slice_unchecked(&mut self, src: &[T]) {
890 debug_assert_eq!(self.len(), src.len());
891 unsafe { core::ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len()) };
892 }
893}
894
895#[repr(transparent)]
896pub struct Pool<T>(pub Vec<T>);
897
898impl<T> Pool<T> {
899 #[inline(always)]
900 pub const fn len(&self) -> usize {
901 self.0.len()
902 }
903 #[inline(always)]
904 pub fn push(&mut self, value: T) {
905 self.0.push(value);
906 }
907 #[inline(always)]
908 pub fn with_capacity(capacity: usize) -> Self {
909 Self(Vec::with_capacity(capacity))
910 }
911 #[inline(always)]
912 pub fn iter(&self) -> std::slice::Iter<'_, T> {
913 self.0.iter()
914 }
915 #[inline(always)]
916 pub fn get_mut(&mut self, index: usize) -> &mut T {
917 unsafe { self.0.get_unchecked_mut(index) }
918 }
919 #[inline(always)]
920 pub const fn as_mut_ptr(&mut self) -> *mut T {
921 self.0.as_mut_ptr()
922 }
923 #[inline(always)]
924 pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
925 unsafe { self.0.split_at_mut_unchecked(mid) }
926 }
927 #[inline(always)]
928 pub const fn is_empty(&self) -> bool {
929 self.0.is_empty()
930 }
931}
932
933impl<T> Index<usize> for Pool<T> {
934 type Output = T;
935 #[inline(always)]
936 fn index(&self, index: usize) -> &Self::Output {
937 unsafe { self.0.get_unchecked(index) }
938 }
939}
940impl<T> IndexMut<usize> for Pool<T> {
941 #[inline(always)]
942 fn index_mut(&mut self, index: usize) -> &mut T {
943 unsafe { self.0.get_unchecked_mut(index) }
944 }
945}
946
947#[repr(transparent)]
948pub struct RegisterFile(pub Vec<Data>);
949
950impl RegisterFile {
951 #[inline(always)]
952 pub const fn len(&self) -> usize {
953 self.0.len()
954 }
955 #[inline(always)]
956 pub const fn is_empty(&self) -> bool {
957 self.0.is_empty()
958 }
959}
960
961impl Index<u16> for RegisterFile {
962 type Output = Data;
963 #[inline(always)]
964 fn index(&self, index: u16) -> &Self::Output {
965 unsafe { self.0.get_unchecked(index as usize) }
966 }
967}
968impl IndexMut<u16> for RegisterFile {
969 #[inline(always)]
970 fn index_mut(&mut self, index: u16) -> &mut Data {
971 unsafe { self.0.get_unchecked_mut(index as usize) }
972 }
973}
974impl Index<usize> for RegisterFile {
975 type Output = Data;
976 #[inline(always)]
977 fn index(&self, index: usize) -> &Self::Output {
978 unsafe { self.0.get_unchecked(index) }
979 }
980}
981impl IndexMut<usize> for RegisterFile {
982 #[inline(always)]
983 fn index_mut(&mut self, index: usize) -> &mut Data {
984 unsafe { self.0.get_unchecked_mut(index) }
985 }
986}
987
988#[derive(Clone, Copy)]
993pub struct Regs(*mut Data);
994
995impl Regs {
996 #[inline(always)]
997 fn new(file: &mut RegisterFile) -> Self {
998 Self(file.0.as_mut_ptr())
999 }
1000}
1001
1002impl Regs {
1003 #[inline(always)]
1007 fn get(self, index: u16) -> Data {
1008 unsafe { Data::load(self.0.add(index as usize)) }
1009 }
1010}
1011
1012impl Index<u16> for Regs {
1013 type Output = Data;
1014 #[inline(always)]
1015 fn index(&self, index: u16) -> &Data {
1016 unsafe { &*self.0.add(index as usize) }
1017 }
1018}
1019impl IndexMut<u16> for Regs {
1020 #[inline(always)]
1021 fn index_mut(&mut self, index: u16) -> &mut Data {
1022 unsafe { &mut *self.0.add(index as usize) }
1023 }
1024}
1025
1026const HALTED: usize = usize::MAX;
1028const FAILED: usize = usize::MAX - 1;
1032const RETURNED: usize = usize::MAX - 2;
1035const PANICKED: usize = usize::MAX - 3;
1039
1040#[cfg(not(target_arch = "wasm32"))]
1045const NATIVE_STACK_BYTES: usize = 256 << 10;
1046
1047#[cfg(not(target_arch = "wasm32"))]
1050const REENTRY_BYTES: usize = 4 << 10;
1051
1052struct Machine<'a> {
1061 r: &'a RegisterFile,
1064 regs: Regs,
1065 instructions: &'a [Instr],
1066 obj_pool: &'a mut ObjectPool,
1067 map_pool: &'a mut MapPool,
1068 str_pool: &'a mut StringPool,
1069 gc: &'a mut GcState,
1070 err_ctx: &'a ErrorCtx<'a>,
1071 callsite_registers: &'a [Vec<u16>],
1072 dyn_libs: &'a [DynamicLibFn],
1073 structs: &'a [Struct],
1074 enums: &'a [EnumType],
1075 host_sigs: &'a [HostFnSig],
1076 host_dispatch: &'a [HostDispatch],
1077 args: Vec<u16>,
1078 call_frames: Vec<CallFrame>,
1079 recursion_stack: RegisterFile,
1080 handle: crate::captured_output::OutputHandle,
1081 dyn_lib_args: Vec<u64>,
1082 host_call_args: Vec<Value>,
1083 keep_alive: Vec<Box<[u8]>>,
1084 error_handles: Vec<ErrorCatch>,
1085 failure: Option<RuntimeError>,
1087 native: Option<&'a NativeCode>,
1089 #[cfg(not(target_arch = "wasm32"))]
1091 ctx: NativeCtx,
1092 native_below: usize,
1095 #[cfg(not(target_arch = "wasm32"))]
1097 native_frames_left: usize,
1098 handles_floor: usize,
1101 panic: Option<Box<dyn std::any::Any + Send>>,
1103}
1104
1105impl Machine<'_> {
1106 #[inline(always)]
1110 fn instr_at(&self, i: usize) -> Instr {
1111 let current = unsafe { *self.instructions.get_unchecked(i) };
1112 match self.native {
1113 Some(native) => native.original(i, current),
1114 None => current,
1115 }
1116 }
1117}
1118
1119#[allow(unused_unsafe)]
1127pub fn execute(
1128 instructions: &[Instr],
1129 r: &mut RegisterFile,
1130 Pools {
1131 objs: obj_pool,
1132 maps: map_pool,
1133 strings: str_pool,
1134 gc,
1135 }: &mut Pools,
1136 err_ctx: &ErrorCtx<'_>,
1137 callsite_registers: &[Vec<u16>],
1138 dyn_libs: &[DynamicLibFn],
1139 structs: &[Struct],
1140 enums: &[EnumType],
1141 allocated_arg_count: usize,
1142 allocated_call_depth: usize,
1143 host_sigs: &[HostFnSig],
1149 host_dispatch: &[HostDispatch],
1150 start: usize,
1153 native: Option<&NativeCode>,
1155) -> Result<(), RuntimeError> {
1156 let regs = Regs::new(r);
1161 let recursion_capacity = allocated_call_depth * r.len();
1162 #[cfg(not(target_arch = "wasm32"))]
1163 let dylibs = native.map_or(std::ptr::null(), |n| n.dylibs.as_ptr());
1164 #[cfg(not(target_arch = "wasm32"))]
1165 let native_frames_left =
1166 native.map_or(0, |n| NATIVE_STACK_BYTES / (n.frame_bytes as usize).max(16));
1167 let mut m = Machine {
1168 r,
1169 regs,
1170 instructions,
1171 obj_pool,
1172 map_pool,
1173 str_pool,
1174 gc,
1175 err_ctx,
1176 callsite_registers,
1177 dyn_libs,
1178 structs,
1179 enums,
1180 host_sigs,
1181 host_dispatch,
1182 args: Vec::with_capacity(allocated_arg_count),
1183 call_frames: Vec::with_capacity(allocated_call_depth.clamp(1, CALL_DEPTH_LIMIT)),
1184 recursion_stack: RegisterFile(Vec::with_capacity(recursion_capacity)),
1185 handle: crate::captured_output::stdout(),
1186 dyn_lib_args: Vec::new(),
1187 host_call_args: Vec::new(),
1188 keep_alive: Vec::new(),
1189 error_handles: Vec::new(),
1190 failure: None,
1191 native,
1192 #[cfg(not(target_arch = "wasm32"))]
1193 ctx: NativeCtx {
1194 regs: regs.0,
1195 objs: std::ptr::null(),
1196 status: abi::STATUS_OK,
1197 err_at: 0,
1198 err_a: 0,
1199 err_b: 0,
1200 marking: 0,
1201 ret: [0; 2],
1202 ceiling: 0,
1203 segment_base: 0,
1204 dylibs,
1205 machine: std::ptr::null_mut(),
1206 shade: native_shade,
1207 print: native_print,
1208 call_vm: native_call_vm,
1209 push_roots: native_push_roots,
1210 pop_roots: native_pop_roots,
1211 },
1212 native_below: 0,
1213 #[cfg(not(target_arch = "wasm32"))]
1214 native_frames_left,
1215 handles_floor: 0,
1216 panic: None,
1217 };
1218
1219 #[cfg(not(target_arch = "wasm32"))]
1220 let stop = match native.and_then(|n| n.main) {
1221 Some(main) if start == 0 => run_native_main(&mut m, main),
1222 _ => run(&mut m, start),
1223 };
1224 #[cfg(target_arch = "wasm32")]
1227 let stop = run(&mut m, start);
1228 if stop == PANICKED
1229 && let Some(payload) = m.panic.take()
1230 {
1231 std::panic::resume_unwind(payload);
1232 }
1233 m.failure.map_or(Ok(()), Err)
1234}
1235
1236#[inline(never)]
1239#[cfg(not(target_arch = "wasm32"))]
1240fn run_nested(m: &mut Machine<'_>, start: usize) -> usize {
1241 run(m, start)
1242}
1243
1244#[allow(unused_unsafe)]
1248#[inline(always)]
1249fn run(m: &mut Machine<'_>, start: usize) -> usize {
1250 let instructions = m.instructions;
1251 let base = instructions.as_ptr();
1254 let mut ip = unsafe { base.add(start) };
1255 let mut regs = m.regs;
1256
1257 macro_rules! error_with_catch {
1258 ($err:expr) => {
1259 let resume = catch_error(
1260 $err,
1261 unsafe { *ip },
1262 &mut m.failure,
1263 &mut m.error_handles,
1264 m.handles_floor,
1265 &mut m.call_frames,
1266 &mut m.args,
1267 m.callsite_registers,
1268 &mut m.recursion_stack,
1269 regs,
1270 m.r,
1271 m.obj_pool,
1272 m.map_pool,
1273 m.str_pool,
1274 m.gc,
1275 );
1276 if resume == FAILED {
1277 return FAILED;
1278 }
1279 ip = unsafe { base.add(resume) };
1280 continue;
1281 };
1282 }
1283
1284 loop {
1285 match unsafe { *ip } {
1286 Instr::Jmp(size) => {
1287 ip = unsafe { ip.add(size as usize) };
1288 continue;
1289 }
1290 Instr::JmpBack(size) => {
1291 ip = unsafe { ip.sub(size as usize) };
1292 continue;
1293 }
1294 Instr::Mov(tgt, dest) => regs[dest] = regs.get(tgt),
1295 Instr::MovChain(dest, mid, src) => {
1296 regs[dest] = regs.get(mid);
1297 regs[mid] = regs.get(src);
1298 }
1299 Instr::SetInt(dest, n) => regs[dest] = Data::int(i64::from(n)),
1300 Instr::SetBool(b, dest) => regs[dest] = b.into(),
1301 Instr::CallFunc(new_loc, return_id) => {
1302 if m.call_frames.len() == m.call_frames.capacity()
1303 && let Some(catch) = frame_room(m, index_of(base, ip), regs)
1304 {
1305 if catch == FAILED {
1306 return FAILED;
1307 }
1308 ip = unsafe { base.add(catch) };
1309 continue;
1310 }
1311 m.call_frames.push_unchecked(CallFrame {
1312 return_addr: index_of(base, ip) as u16,
1313 return_reg: return_id,
1314 saved_callsite: None,
1315 });
1316 ip = unsafe { base.add(new_loc as usize) };
1317 continue;
1318 }
1319 Instr::CallFuncRecursive(new_loc, _) => {
1320 ip = unsafe { base.add(new_loc as usize) };
1321 continue;
1322 }
1323 Instr::CallIndirect(callee) => {
1324 let value_id = regs[callee].as_array();
1328 ip = unsafe { base.add(m.obj_pool[value_id].get_unchecked(0).as_int() as usize) };
1329 continue;
1330 }
1331 Instr::VoidReturn => {
1332 let call_frame = m.call_frames.pop_unchecked();
1335 if let Some(callsite_id) = call_frame.saved_callsite {
1336 restore_saved_registers(
1337 callsite_id,
1338 m.callsite_registers,
1339 &mut m.recursion_stack,
1340 regs,
1341 );
1342 }
1343 ip = unsafe { base.add(call_frame.return_addr as usize) };
1344 }
1345 Instr::SaveFrame(relative_func_loc, return_register, callsite_id) => {
1346 if m.call_frames.len() == m.call_frames.capacity()
1347 && let Some(catch) = frame_room(m, index_of(base, ip), regs)
1348 {
1349 if catch == FAILED {
1350 return FAILED;
1351 }
1352 ip = unsafe { base.add(catch) };
1353 continue;
1354 }
1355 m.call_frames.push_unchecked(CallFrame {
1356 return_addr: (index_of(base, ip) as u16) + relative_func_loc,
1357 return_reg: return_register,
1358 saved_callsite: Some(callsite_id),
1359 });
1360 let saved = unsafe { m.callsite_registers.get_unchecked(callsite_id as usize) };
1361 let stack = &mut m.recursion_stack.0;
1362 if stack.capacity() - stack.len() < saved.len() {
1363 grow_recursion_stack(stack, saved.len());
1364 }
1365 unsafe {
1366 let top = stack.as_mut_ptr().add(stack.len());
1367 if let [reg] = saved[..] {
1370 top.write(regs.get(reg));
1371 } else {
1372 for (k, ®) in saved.iter().enumerate() {
1373 top.add(k).write(regs.get(reg));
1374 }
1375 }
1376 stack.set_len(stack.len() + saved.len());
1377 }
1378 }
1379 Instr::Return(tgt) => {
1380 let call_frame = m.call_frames.pop_unchecked();
1382 ip = unsafe { base.add(call_frame.return_addr as usize) };
1383 regs[call_frame.return_reg] = regs.get(tgt);
1384 }
1385 Instr::RecursiveReturn(tgt) => {
1386 let call_frame = m.call_frames.pop_unchecked();
1387 let temp = regs.get(tgt);
1388 if let Some(callsite_id) = call_frame.saved_callsite {
1389 restore_saved_registers(
1390 callsite_id,
1391 m.callsite_registers,
1392 &mut m.recursion_stack,
1393 regs,
1394 );
1395 }
1396 ip = unsafe { base.add(call_frame.return_addr as usize) };
1397 regs[call_frame.return_reg] = temp;
1398 }
1399 Instr::IsFalseJmp(cond_id, size) => {
1400 if regs[cond_id].is_false() {
1401 ip = unsafe { ip.add(size as usize) };
1402 continue;
1403 }
1404 }
1405 Instr::IsTrueJmp(cond_id, size) => {
1406 if regs[cond_id].is_true() {
1407 ip = unsafe { ip.add(size as usize) };
1408 continue;
1409 }
1410 }
1411 Instr::AddFloat(o1, o2, dest) => {
1412 regs[dest] = (regs[o1].as_float() + regs[o2].as_float()).into();
1413 }
1414 Instr::AddInt(o1, o2, dest) => {
1415 regs[dest] = (regs[o1].as_int() + regs[o2].as_int()).into();
1416 }
1417 Instr::MulFloat(o1, o2, dest) => {
1418 regs[dest] = (regs[o1].as_float() * regs[o2].as_float()).into();
1419 }
1420 Instr::MulInt(o1, o2, dest) => {
1421 regs[dest] = (regs[o1].as_int() * regs[o2].as_int()).into();
1422 }
1423 Instr::DivFloat(o1, o2, dest) => {
1424 regs[dest] = (regs[o1].as_float() / regs[o2].as_float()).into();
1425 }
1426 Instr::SubFloat(o1, o2, dest) => {
1427 regs[dest] = (regs[o1].as_float() - regs[o2].as_float()).into();
1428 }
1429 Instr::SubInt(o1, o2, dest) => {
1430 regs[dest] = (regs[o1].as_int() - regs[o2].as_int()).into();
1431 }
1432 Instr::IncInt(reg) => regs[reg].inc_int(),
1433 Instr::DecInt(reg) => regs[reg].dec_int(),
1434 Instr::IncIntTo(src, dst) => {
1435 let s = regs[src];
1436 regs[dst].inc_into(s);
1437 }
1438 Instr::DecIntTo(src, dst) => {
1439 let s = regs[src];
1440 regs[dst].dec_into(s);
1441 }
1442 Instr::Eq(o1, o2, dest) => {
1443 regs[dest] = (regs.get(o1) == regs.get(o2)).into();
1444 }
1445 Instr::NotEqJmp(o1, o2, jump_size) => {
1446 if regs.get(o1) != regs.get(o2) {
1447 ip = unsafe { ip.add(jump_size as usize) };
1448 continue;
1449 }
1450 }
1451 Instr::NotEq(o1, o2, dest) => {
1452 regs[dest] = (regs.get(o1) != regs.get(o2)).into();
1453 }
1454 Instr::EqJmp(o1, o2, jump_size) => {
1455 if regs.get(o1) == regs.get(o2) {
1456 ip = unsafe { ip.add(jump_size as usize) };
1457 continue;
1458 }
1459 }
1460 Instr::SupFloat(o1, o2, dest) => {
1461 regs[dest] = (regs[o1].as_float() > regs[o2].as_float()).into();
1462 }
1463 Instr::SupInt(o1, o2, dest) => {
1464 regs[dest] = (regs[o1].as_int() > regs[o2].as_int()).into();
1465 }
1466 Instr::InfEqFloatJmp(o1, o2, jump_size) => {
1467 if regs[o1].as_float() <= regs[o2].as_float() {
1468 ip = unsafe { ip.add(jump_size as usize) };
1469 continue;
1470 }
1471 }
1472 Instr::InfEqIntJmp(o1, o2, jump_size) => {
1473 if regs[o1].as_int() <= regs[o2].as_int() {
1474 ip = unsafe { ip.add(jump_size as usize) };
1475 continue;
1476 }
1477 }
1478 Instr::SupEqFloat(o1, o2, dest) => {
1479 regs[dest] = (regs[o1].as_float() >= regs[o2].as_float()).into();
1480 }
1481 Instr::SupEqInt(o1, o2, dest) => {
1482 regs[dest] = (regs[o1].as_int() >= regs[o2].as_int()).into();
1483 }
1484 Instr::InfFloatJmp(o1, o2, jump_size) => {
1485 if regs[o1].as_float() < regs[o2].as_float() {
1486 ip = unsafe { ip.add(jump_size as usize) };
1487 continue;
1488 }
1489 }
1490 Instr::InfIntJmp(o1, o2, jump_size) => {
1491 if regs[o1].as_int() < regs[o2].as_int() {
1492 ip = unsafe { ip.add(jump_size as usize) };
1493 continue;
1494 }
1495 }
1496 Instr::StepIntJmpBack(counter, end, jump_size) => {
1497 regs[counter].inc_int();
1498 if regs[counter].as_int() < regs[end].as_int() {
1499 ip = unsafe { ip.sub(jump_size as usize) };
1500 continue;
1501 }
1502 }
1503 Instr::AddIntImm(src, imm, dest) => {
1504 regs[dest] = (regs[src].as_int() + i64::from(imm)).into();
1505 }
1506 Instr::InfIntJmpBack(o1, o2, jump_size) => {
1507 if regs[o1].as_int() < regs[o2].as_int() {
1508 ip = unsafe { ip.sub(jump_size as usize) };
1509 continue;
1510 }
1511 }
1512 Instr::InfFloat(o1, o2, dest) => {
1513 regs[dest] = (regs[o1].as_float() < regs[o2].as_float()).into();
1514 }
1515 Instr::InfInt(o1, o2, dest) => {
1516 regs[dest] = (regs[o1].as_int() < regs[o2].as_int()).into();
1517 }
1518 Instr::SupEqFloatJmp(o1, o2, jump_size) => {
1519 if regs[o1].as_float() >= regs[o2].as_float() {
1520 ip = unsafe { ip.add(jump_size as usize) };
1521 continue;
1522 }
1523 }
1524 Instr::SupEqIntJmp(o1, o2, jump_size) => {
1525 if regs[o1].as_int() >= regs[o2].as_int() {
1526 ip = unsafe { ip.add(jump_size as usize) };
1527 continue;
1528 }
1529 }
1530 Instr::InfEqFloat(o1, o2, dest) => {
1531 regs[dest] = (regs[o1].as_float() <= regs[o2].as_float()).into();
1532 }
1533 Instr::InfEqInt(o1, o2, dest) => {
1534 regs[dest] = (regs[o1].as_int() <= regs[o2].as_int()).into();
1535 }
1536 Instr::SupFloatJmp(o1, o2, jump_size) => {
1537 if regs[o1].as_float() > regs[o2].as_float() {
1538 ip = unsafe { ip.add(jump_size as usize) };
1539 continue;
1540 }
1541 }
1542 Instr::SupIntJmp(o1, o2, jump_size) => {
1543 if regs[o1].as_int() > regs[o2].as_int() {
1544 ip = unsafe { ip.add(jump_size as usize) };
1545 continue;
1546 }
1547 }
1548 Instr::BoolAnd(o1, o2, dest) => {
1549 regs[dest] = (regs[o1].as_bool() && regs[o2].as_bool()).into();
1550 }
1551 Instr::BoolOr(o1, o2, dest) => {
1552 regs[dest] = (regs[o1].as_bool() || regs[o2].as_bool()).into();
1553 }
1554 Instr::NegBool(src, dest) => {
1555 regs[dest] = (!regs[src].as_bool()).into();
1556 }
1557 Instr::NegFloat(tgt, dest) => {
1558 regs[dest] = (-regs[tgt].as_float()).into();
1559 }
1560 Instr::NegInt(tgt, dest) => {
1561 regs[dest] = (-regs[tgt].as_int()).into();
1562 }
1563 Instr::StoreFuncArg(id) => m.args.push(id),
1564 Instr::ObjElemMov(new_elem_reg_id, array_id, idx) => {
1567 let arr = m.obj_pool.get_mut(array_id as usize);
1568 let slot = unsafe { arr.get_unchecked_mut(idx as usize) };
1569 if m.gc.marking() {
1570 shade_overwritten(m.gc, m.str_pool, *slot);
1571 }
1572 *slot = regs.get(new_elem_reg_id);
1573 }
1574 Instr::SetElementObj(array_reg_id, new_elem_reg_id, idx) => {
1575 let array = m.obj_pool.get_mut(regs[array_reg_id].as_array());
1576 let index = regs[idx].as_int();
1577 if index < 0 || (index as u64) >= array.len() as u64 {
1578 error_with_catch!(ErrType::IndexOutOfBounds(array.len(), index));
1579 }
1580 let slot = unsafe { array.get_unchecked_mut(index as usize) };
1581 if m.gc.marking() {
1582 shade_overwritten(m.gc, m.str_pool, *slot);
1583 }
1584 *slot = regs.get(new_elem_reg_id);
1585 }
1586 Instr::SetFieldStruct(struct_reg_id, new_elem_reg_id, idx) => {
1587 let s = m.obj_pool.get_mut(regs[struct_reg_id].as_struct());
1588 let slot = unsafe { s.get_unchecked_mut(idx as usize) };
1589 if m.gc.marking() {
1590 shade_overwritten(m.gc, m.str_pool, *slot);
1591 }
1592 *slot = regs.get(new_elem_reg_id);
1593 }
1594 Instr::AddFieldFloat(struct_reg_id, value_reg_id, idx) => {
1597 let value = regs[value_reg_id].as_float();
1598 let s = m.obj_pool.get_mut(regs[struct_reg_id].as_struct());
1599 let slot = unsafe { s.get_unchecked_mut(idx as usize) };
1600 *slot = (slot.as_float() + value).into();
1601 }
1602 Instr::GetIndexArray(array_reg_id, index, dest) => {
1603 let idx = regs[index].as_int();
1604 let arr_id = regs[array_reg_id].as_array();
1605 let array = &m.obj_pool[arr_id];
1606 if idx < 0 || (idx as u64) >= array.len() as u64 {
1607 error_with_catch!(ErrType::IndexOutOfBounds(array.len(), idx));
1608 }
1609 regs[dest] = unsafe { Data::load(array.as_ptr().add(idx as usize)) };
1610 }
1611 Instr::GetFieldStruct(struct_reg_id, index, dest) => {
1612 let struct_id = regs[struct_reg_id].as_struct();
1613 let s = &m.obj_pool[struct_id];
1614 regs[dest] = unsafe { Data::load(s.as_ptr().add(index as usize)) }
1615 }
1616 Instr::LoadCell(cell, dest) => {
1617 let slot = &m.obj_pool[regs[cell].as_array()];
1618 regs[dest] = unsafe { Data::load(slot.as_ptr()) };
1619 }
1620 Instr::StoreCell(cell, src) => {
1621 let value = regs.get(src);
1622 let cell = m.obj_pool.get_mut(regs[cell].as_array());
1623 let slot = unsafe { cell.get_unchecked_mut(0) };
1624 if m.gc.marking() {
1625 shade_overwritten(m.gc, m.str_pool, *slot);
1626 }
1627 *slot = value;
1628 }
1629 Instr::Push(array, element) => {
1630 let list = m.obj_pool.get_mut(regs[array].as_array());
1631 if list.len() == list.capacity() {
1632 grow_list(list, m.gc);
1633 }
1634 unsafe {
1637 let len = list.len();
1638 list.as_mut_ptr().add(len).write(regs.get(element));
1639 list.set_len(len + 1);
1640 }
1641 }
1642 Instr::CloneStruct(src_reg, dest_reg) => {
1643 let new_id = alloc_array(
1644 m.obj_pool,
1645 m.map_pool,
1646 m.str_pool,
1647 m.r,
1648 &m.recursion_stack,
1649 m.gc,
1650 ) as usize;
1651 let src_reg = regs[src_reg];
1652 let src = &m.obj_pool[src_reg.as_struct()];
1653 let len = src.len();
1654 unsafe {
1655 let src_ptr = src.as_ptr();
1656 let dst = m.obj_pool.get_mut(new_id);
1657 dst.reserve_exact(len);
1658 dst.set_len(len);
1659 std::ptr::copy_nonoverlapping(src_ptr, dst.as_mut_ptr(), len);
1660 }
1661 regs[dest_reg] = Data::struct_instance(src_reg.struct_type_id(), new_id as u32);
1662 }
1663 Instr::Print(..)
1667 | Instr::ObjNotEqJmp(..)
1668 | Instr::ObjEqJmp(..)
1669 | Instr::StrNotEqJmp(..)
1670 | Instr::StrEqJmp(..)
1671 | Instr::AddArray(..)
1672 | Instr::AddStr(..)
1673 | Instr::DivInt(..)
1674 | Instr::ModFloat(..)
1675 | Instr::ModInt(..)
1676 | Instr::PowFloat(..)
1677 | Instr::PowInt(..)
1678 | Instr::BitAndInt(..)
1679 | Instr::BitOrInt(..)
1680 | Instr::BitXorInt(..)
1681 | Instr::BitNotInt(..)
1682 | Instr::ShlInt(..)
1683 | Instr::ShrInt(..)
1684 | Instr::ObjEq(..)
1685 | Instr::ObjNotEq(..)
1686 | Instr::StrEq(..)
1687 | Instr::StrNotEq(..)
1688 | Instr::SupStr(..)
1689 | Instr::SupEqStr(..)
1690 | Instr::InfStr(..)
1691 | Instr::InfEqStr(..)
1692 | Instr::CallDynamicLibFunc(..)
1693 | Instr::CallHostFunc(..)
1694 | Instr::CallLibFunc(..)
1695 | Instr::CallLibFuncVoid(..)
1696 | Instr::StartErrorCatch(..)
1697 | Instr::StopErrorCatch
1698 | Instr::ThrowError(..)
1699 | Instr::EmptyArray(..)
1700 | Instr::EmptyFnValue(..)
1701 | Instr::CloneArray(..)
1702 | Instr::CloneEnum(..)
1703 | Instr::SetElementString(..)
1704 | Instr::GetIndexString(..)
1705 | Instr::GetSliceArray(..)
1706 | Instr::GetSliceString(..)
1707 | Instr::Remove(..)
1708 | Instr::MapGet(..)
1709 | Instr::MapInsert(..)
1710 | Instr::MapInsertReg(..)
1711 | Instr::MapRemove(..)
1712 | Instr::CloneMap(..)
1713 | Instr::NewCell(..)
1714 | Instr::NativeExit
1715 | Instr::Halt(..) => {
1716 let next = run_cold(m, instructions, index_of(base, ip), regs);
1717 if next >= PANICKED {
1718 return next;
1719 }
1720 ip = unsafe { base.add(next) };
1721 continue;
1722 }
1723 #[cfg(not(target_arch = "wasm32"))]
1726 Instr::CallNative(function, ret) => {
1727 let next = call_native(m, index_of(base, ip), function, ret, false);
1728 if next >= PANICKED {
1729 return next;
1730 }
1731 ip = unsafe { base.add(next) };
1732 continue;
1733 }
1734 #[cfg(not(target_arch = "wasm32"))]
1735 Instr::CallNativeRec(function, ret) => {
1736 let next = call_native(m, index_of(base, ip), function, ret, true);
1737 if next >= PANICKED {
1738 return next;
1739 }
1740 ip = unsafe { base.add(next) };
1741 continue;
1742 }
1743 #[cfg(target_arch = "wasm32")]
1744 Instr::CallNative(..) | Instr::CallNativeRec(..) => unsafe { unreachable_unchecked() },
1745 }
1746 ip = unsafe { ip.add(1) };
1747 }
1748}
1749
1750#[inline(never)]
1759#[cfg(not(target_arch = "wasm32"))]
1760fn call_native(m: &mut Machine<'_>, i: usize, function: u16, ret: u16, recursive: bool) -> usize {
1761 let native = unsafe { m.native.unwrap_unchecked() };
1762 let f = unsafe { native.functions.get_unchecked(function as usize) };
1763 let standing = m.call_frames.len() + m.native_below;
1765 if m.native_frames_left == 0 {
1766 if !recursive {
1767 if m.call_frames.len() == m.call_frames.capacity()
1768 && let Some(catch) = frame_room(m, i, m.regs)
1769 {
1770 return catch;
1771 }
1772 m.call_frames.push_unchecked(CallFrame {
1773 return_addr: i as u16,
1774 return_reg: ret,
1775 saved_callsite: None,
1776 });
1777 }
1778 return f.loc as usize;
1779 }
1780 if !recursive && standing >= CALL_DEPTH_LIMIT {
1781 m.ctx.status = abi::STATUS_OK;
1782 return native_failed(m, i, abi::STATUS_DEPTH);
1783 }
1784 let depth = if recursive { standing } else { standing + 1 };
1785 let below = depth - 1;
1786 let ceiling = CALL_DEPTH_LIMIT.min(below + m.native_frames_left);
1787 let status = enter_native(m, f.entry, below, ceiling, (ceiling - depth) as i64);
1788 if status != abi::STATUS_OK {
1789 return native_failed(m, i, status);
1790 }
1791 let value = Data::from_words(m.ctx.ret[0], m.ctx.ret[1] as i64);
1792 let mut regs = m.regs;
1793 if recursive {
1794 let call_frame = m.call_frames.pop_unchecked();
1796 if let Some(callsite_id) = call_frame.saved_callsite {
1797 restore_saved_registers(
1798 callsite_id,
1799 m.callsite_registers,
1800 &mut m.recursion_stack,
1801 regs,
1802 );
1803 }
1804 if f.returns {
1805 regs[call_frame.return_reg] = value;
1806 }
1807 return call_frame.return_addr as usize + 1;
1808 }
1809 if f.returns {
1810 regs[ret] = value;
1811 }
1812 i + 1
1813}
1814
1815#[cfg(not(target_arch = "wasm32"))]
1817fn run_native_main(m: &mut Machine<'_>, main: usize) -> usize {
1818 let native = unsafe { m.native.unwrap_unchecked() };
1819 let entry = native.functions[main].entry;
1820 let ceiling = CALL_DEPTH_LIMIT.min(m.native_frames_left);
1822 let status = enter_native(m, entry, 0, ceiling, ceiling as i64);
1823 if status == abi::STATUS_OK || status == abi::STATUS_HALT {
1824 return HALTED;
1825 }
1826 match native_failed(m, 0, status) {
1827 stop @ (HALTED | FAILED | RETURNED | PANICKED) => stop,
1828 resume => run_nested(m, resume),
1829 }
1830}
1831
1832#[cfg(not(target_arch = "wasm32"))]
1835fn enter_native(m: &mut Machine<'_>, entry: Entry, below: usize, ceiling: usize, rem: i64) -> u32 {
1836 let machine: *mut Machine<'_> = m;
1837 unsafe {
1838 let ctx = &raw mut (*machine).ctx;
1839 (*ctx).objs = (*machine).obj_pool.0.as_ptr().cast();
1840 (*ctx).marking = u64::from((*machine).gc.marking());
1841 (*ctx).machine = machine.cast();
1842 (*ctx).status = abi::STATUS_OK;
1843 let saved = ((*ctx).ceiling, (*ctx).segment_base);
1844 (*ctx).ceiling = ceiling as i64;
1845 (*ctx).segment_base = below as i64;
1846 let status = entry(ctx, rem);
1847 (*ctx).ceiling = saved.0;
1848 (*ctx).segment_base = saved.1;
1849 status
1850 }
1851}
1852
1853#[cold]
1857#[inline(never)]
1858#[cfg(not(target_arch = "wasm32"))]
1859fn native_failed(m: &mut Machine<'_>, i: usize, status: u32) -> usize {
1860 let at = if m.ctx.status == status {
1863 m.ctx.err_at as usize
1864 } else {
1865 i
1866 };
1867 let err = match status {
1868 abi::STATUS_HALT => return HALTED,
1869 abi::STATUS_PANIC => return PANICKED,
1870 abi::STATUS_VM => {
1871 return match m.failure.take() {
1872 Some(error) => catch_failure(m, error),
1873 None => FAILED,
1874 };
1875 }
1876 abi::STATUS_INDEX => ErrType::IndexOutOfBounds(m.ctx.err_a as usize, m.ctx.err_b),
1877 abi::STATUS_SHIFT => ErrType::ShiftCountOutOfRange(m.ctx.err_a),
1878 _ => {
1879 ErrType::CallDepthExceeded(call_site_name(m.err_ctx, m.instr_at(at)), CALL_DEPTH_LIMIT)
1880 }
1881 };
1882 let instr = m.instr_at(at);
1883 catch_error(
1884 err,
1885 instr,
1886 &mut m.failure,
1887 &mut m.error_handles,
1888 m.handles_floor,
1889 &mut m.call_frames,
1890 &mut m.args,
1891 m.callsite_registers,
1892 &mut m.recursion_stack,
1893 m.regs,
1894 m.r,
1895 m.obj_pool,
1896 m.map_pool,
1897 m.str_pool,
1898 m.gc,
1899 )
1900}
1901
1902#[cfg(not(target_arch = "wasm32"))]
1910unsafe fn machine<'a>(ctx: *mut NativeCtx) -> &'a mut Machine<'a> {
1911 unsafe { &mut *(*ctx).machine.cast::<Machine<'a>>() }
1912}
1913
1914#[cfg(not(target_arch = "wasm32"))]
1917extern "C" fn native_shade(ctx: *mut NativeCtx, boxed: u64, int: u64) {
1918 let m = unsafe { machine(ctx) };
1919 shade_overwritten(m.gc, m.str_pool, Data::from_words(boxed, int as i64));
1920}
1921
1922#[cfg(not(target_arch = "wasm32"))]
1924extern "C" fn native_print(ctx: *mut NativeCtx, boxed: u64, int: u64) {
1925 let m = unsafe { machine(ctx) };
1926 print_value(
1927 &mut m.handle,
1928 Data::from_words(boxed, int as i64),
1929 m.obj_pool,
1930 m.map_pool,
1931 m.str_pool,
1932 m.structs,
1933 m.enums,
1934 );
1935}
1936
1937#[cfg(not(target_arch = "wasm32"))]
1940extern "C" fn native_push_roots(ctx: *mut NativeCtx, values: *const u64, count: u64) {
1941 let m = unsafe { machine(ctx) };
1942 push_values(&mut m.recursion_stack, values, count);
1943}
1944
1945#[cfg(not(target_arch = "wasm32"))]
1947extern "C" fn native_pop_roots(ctx: *mut NativeCtx, count: u64) {
1948 let m = unsafe { machine(ctx) };
1949 let len = m.recursion_stack.len() - count as usize;
1950 m.recursion_stack.0.truncate(len);
1951}
1952
1953#[cfg(not(target_arch = "wasm32"))]
1955fn push_values(stack: &mut RegisterFile, values: *const u64, count: u64) {
1956 for k in 0..count as usize {
1957 let (boxed, int) = unsafe { (*values.add(2 * k), *values.add(2 * k + 1)) };
1958 stack.0.push(Data::from_words(boxed, int as i64));
1959 }
1960}
1961
1962#[cfg(not(target_arch = "wasm32"))]
1968extern "C" fn native_call_vm(
1969 ctx: *mut NativeCtx,
1970 entry: u64,
1971 ret_reg: u64,
1972 at: u64,
1973 rem: i64,
1974 roots: *const u64,
1975 count: u64,
1976) -> u32 {
1977 let m = unsafe { machine(ctx) };
1978 if cfg!(panic = "unwind") {
1979 let run =
1983 std::panic::AssertUnwindSafe(|| call_vm(m, entry, ret_reg, at, rem, roots, count));
1984 match std::panic::catch_unwind(run) {
1985 Ok(status) => status,
1986 Err(payload) => {
1987 let m = unsafe { machine(ctx) };
1988 m.panic = Some(payload);
1989 m.ctx.status = abi::STATUS_PANIC;
1990 abi::STATUS_PANIC
1991 }
1992 }
1993 } else {
1994 call_vm(m, entry, ret_reg, at, rem, roots, count)
1995 }
1996}
1997
1998#[cfg(not(target_arch = "wasm32"))]
2000fn call_vm(
2001 m: &mut Machine<'_>,
2002 entry: u64,
2003 ret_reg: u64,
2004 at: u64,
2005 rem: i64,
2006 roots: *const u64,
2007 count: u64,
2008) -> u32 {
2009 let native = unsafe { m.native.unwrap_unchecked() };
2010 let depth = (m.ctx.ceiling - rem) as usize;
2012 if depth >= CALL_DEPTH_LIMIT {
2013 m.ctx.status = abi::STATUS_DEPTH;
2014 m.ctx.err_at = at as u32;
2015 return abi::STATUS_DEPTH;
2016 }
2017 let saved = (
2018 m.native_below,
2019 m.native_frames_left,
2020 m.handles_floor,
2021 m.ctx.ceiling,
2022 m.ctx.segment_base,
2023 );
2024 let frame = (native.frame_bytes as usize).max(16);
2027 let used = depth.saturating_sub(m.ctx.segment_base as usize) + REENTRY_BYTES.div_ceil(frame);
2028 m.native_frames_left = m.native_frames_left.saturating_sub(used);
2029 m.native_below = depth - m.call_frames.len();
2030
2031 let room = CALL_DEPTH_LIMIT - m.native_below;
2034 if m.call_frames.capacity() > room {
2035 m.call_frames.shrink_to(room.max(m.call_frames.len()));
2036 }
2037 if m.call_frames.len() == m.call_frames.capacity() {
2038 let len = m.call_frames.len();
2039 m.call_frames.reserve_exact(len.max(64).min(room - len));
2040 }
2041 m.call_frames.push(CallFrame {
2042 return_addr: (native.exit - 1) as u16,
2043 return_reg: ret_reg as u16,
2044 saved_callsite: None,
2045 });
2046 let roots_at = m.recursion_stack.len();
2047 push_values(&mut m.recursion_stack, roots, count);
2048 m.handles_floor = m.error_handles.len();
2049
2050 let stop = run_nested(m, entry as usize);
2051
2052 (
2053 m.native_below,
2054 m.native_frames_left,
2055 m.handles_floor,
2056 m.ctx.ceiling,
2057 m.ctx.segment_base,
2058 ) = saved;
2059 m.ctx.objs = m.obj_pool.0.as_ptr().cast();
2060 m.ctx.marking = u64::from(m.gc.marking());
2061 let status = match stop {
2062 RETURNED => {
2063 m.recursion_stack.0.truncate(roots_at);
2064 return abi::STATUS_OK;
2065 }
2066 HALTED => abi::STATUS_HALT,
2067 PANICKED => abi::STATUS_PANIC,
2068 _ => abi::STATUS_VM,
2069 };
2070 m.ctx.status = status;
2071 status
2072}
2073
2074#[inline(never)]
2077fn print_value(
2078 handle: &mut crate::captured_output::OutputHandle,
2079 tgt: Data,
2080 obj_pool: &ObjectPool,
2081 map_pool: &MapPool,
2082 str_pool: &StringPool,
2083 structs: &[Struct],
2084 enums: &[EnumType],
2085) {
2086 if tgt.is_string() {
2087 outln!(*handle, "{}", tgt.as_str(str_pool));
2088 } else if tgt.is_int() {
2089 outln!(*handle, "{}", tgt.as_int());
2090 } else if tgt.is_float() {
2091 outln!(*handle, "{}", format_float(tgt.as_float()));
2092 } else if tgt.is_bool() {
2093 outln!(*handle, "{}", tgt.as_bool());
2094 } else if tgt.is_function() {
2095 outln!(*handle, "{FUNCTION_TEXT}");
2096 } else if tgt.is_array() {
2097 let array = &obj_pool[tgt.as_array()];
2098 out!(*handle, "[");
2099 for (idx, item) in array.iter().enumerate() {
2100 if idx != 0 {
2101 out!(*handle, ",");
2102 }
2103 out!(
2104 *handle,
2105 "{}",
2106 item.format(obj_pool, str_pool, map_pool, structs, enums, false)
2107 );
2108 }
2109 outln!(*handle, "]");
2110 } else if tgt.is_struct() {
2111 let s = unsafe { structs.get_unchecked(tgt.struct_type_id() as usize) };
2112 let s_name = &s.name;
2113 let s_fields = &s.fields;
2114 out!(*handle, "{s_name} {{");
2115 for (idx, item) in obj_pool[tgt.as_struct()].iter().enumerate() {
2116 if idx != 0 {
2117 out!(*handle, ",");
2118 }
2119 out!(
2120 *handle,
2121 "{}:{}",
2122 unsafe { &s_fields.get_unchecked(idx).0 },
2123 item.format(obj_pool, str_pool, map_pool, structs, enums, false)
2124 );
2125 }
2126 outln!(*handle, "}}");
2127 } else if tgt.is_enum() {
2128 let e = unsafe { enums.get_unchecked(tgt.enum_type_id() as usize) };
2129 let entry = &obj_pool[tgt.as_enum()];
2130 let tag = entry[0].as_int() as usize;
2131 let variant = unsafe { e.variants.get_unchecked(tag) };
2132 if entry.len() <= 1 {
2133 outln!(*handle, "{}", variant.name);
2134 } else {
2135 out!(*handle, "{}(", variant.name);
2136 for (idx, item) in entry[1..].iter().enumerate() {
2137 if idx != 0 {
2138 out!(*handle, ",");
2139 }
2140 out!(
2141 *handle,
2142 "{}",
2143 item.format(obj_pool, str_pool, map_pool, structs, enums, false)
2144 );
2145 }
2146 outln!(*handle, ")");
2147 }
2148 } else if tgt.is_map() {
2149 let m = &map_pool[tgt.as_map()];
2150 out!(*handle, "{{");
2151 for (i, (key, val)) in m.iter().enumerate() {
2152 if i != 0 {
2153 out!(*handle, ",");
2154 }
2155 out!(
2156 *handle,
2157 "{}:{}",
2158 key.format(obj_pool, str_pool, map_pool, structs, enums, false),
2159 val.format(obj_pool, str_pool, map_pool, structs, enums, false),
2160 );
2161 }
2162 outln!(*handle, "}}");
2163 } else if tgt.is_null() {
2164 outln!(*handle, "null");
2165 }
2166}
2167
2168#[inline(always)]
2170fn index_of(base: *const Instr, ip: *const Instr) -> usize {
2171 unsafe { ip.offset_from_unsigned(base) }
2172}
2173
2174#[allow(unused_unsafe)]
2183#[cold]
2184#[inline(never)]
2185fn run_cold(m: &mut Machine<'_>, instructions: &[Instr], mut i: usize, mut regs: Regs) -> usize {
2186 let Machine {
2187 r,
2188 regs: _,
2189 instructions: _,
2190 obj_pool,
2191 map_pool,
2192 str_pool,
2193 gc,
2194 err_ctx: _,
2195 callsite_registers,
2196 dyn_libs,
2197 structs,
2198 enums,
2199 host_sigs,
2200 host_dispatch,
2201 args,
2202 call_frames,
2203 recursion_stack,
2204 handle,
2205 dyn_lib_args,
2206 host_call_args,
2207 keep_alive,
2208 error_handles,
2209 failure,
2210 native: _,
2211 #[cfg(not(target_arch = "wasm32"))]
2212 ctx: _,
2213 native_below: _,
2214 #[cfg(not(target_arch = "wasm32"))]
2215 native_frames_left: _,
2216 handles_floor,
2217 panic: _,
2218 } = m;
2219 let r: &RegisterFile = r;
2220 let obj_pool: &mut ObjectPool = obj_pool;
2221 let map_pool: &mut MapPool = map_pool;
2222 let str_pool: &mut StringPool = str_pool;
2223 let gc: &mut GcState = gc;
2224 let callsite_registers: &[Vec<u16>] = callsite_registers;
2225 let dyn_libs: &[DynamicLibFn] = dyn_libs;
2226 let structs: &[Struct] = structs;
2227 let enums: &[EnumType] = enums;
2228 let host_sigs: &[HostFnSig] = host_sigs;
2229 let host_dispatch: &[HostDispatch] = host_dispatch;
2230
2231 macro_rules! string {
2232 ($e: expr) => {
2233 Data::string($e, obj_pool, map_pool, str_pool, r, recursion_stack, gc)
2234 };
2235 }
2236
2237 macro_rules! error_with_catch {
2238 ($err:expr) => {
2239 i = caught!($err);
2240 continue;
2241 };
2242 ($err:expr, $label:lifetime) => {
2243 i = caught!($err);
2244 continue $label;
2245 };
2246 }
2247
2248 macro_rules! caught {
2249 ($err:expr) => {
2250 catch_error(
2251 $err,
2252 unsafe { *instructions.get_unchecked(i) },
2253 failure,
2254 error_handles,
2255 *handles_floor,
2256 call_frames,
2257 args,
2258 callsite_registers,
2259 recursion_stack,
2260 regs,
2261 r,
2262 obj_pool,
2263 map_pool,
2264 str_pool,
2265 gc,
2266 )
2267 };
2268 }
2269
2270 macro_rules! str_order {
2279 ($o1:expr, $o2:expr, $dest:expr, $op:tt) => {
2280 let d1 = regs[$o1];
2281 let d2 = regs[$o2];
2282 if !d1.is_string() || !d2.is_string() {
2283 let culprit = if d1.is_string() { d2 } else { d1 };
2284 error_with_catch!(ErrType::NotAString(culprit.type_name()));
2285 }
2286 regs[$dest] = (d1.as_str(str_pool) $op d2.as_str(str_pool)).into();
2287 };
2288 }
2289
2290 #[allow(clippy::never_loop)]
2293 'main: for _ in 0..1 {
2294 match unsafe { *instructions.get_unchecked(i) } {
2295 #[cfg(target_arch = "wasm32")]
2298 Instr::CallDynamicLibFunc(fn_id, dest) => {
2299 use crate::intrinsics::{Arg, Ret};
2300 let func = unsafe { dyn_libs.get_unchecked(fn_id as usize) };
2301 let ret = {
2302 let mut values = [NULL; 2];
2305 for (value, reg) in values.iter_mut().zip(args.iter()) {
2306 *value = regs[*reg];
2307 }
2308 let mut call = [Arg::Int(0); 2];
2309 for (slot, data) in call.iter_mut().zip(values.iter()) {
2310 *slot = if data.is_int() {
2311 Arg::Int(data.as_int())
2312 } else if data.is_float() {
2313 Arg::Float(data.as_float())
2314 } else if data.is_string() {
2315 Arg::Str(data.as_str(str_pool))
2316 } else {
2317 Arg::Int(0)
2318 };
2319 }
2320 (func.intrinsic)(&call[..args.len().min(2)])
2321 };
2322 args.clear();
2323 regs[dest] = match ret {
2324 Ret::Int(v) => v.into(),
2325 Ret::Float(v) => v.into(),
2326 Ret::Str(v) => string!(v),
2327 Ret::Null => NULL,
2328 };
2329 }
2330 #[cfg(not(target_arch = "wasm32"))]
2331 Instr::CallDynamicLibFunc(fn_id, dest) => {
2332 dyn_lib_args.clear();
2333 dyn_lib_args.reserve_exact(args.len());
2334 let args_ptr = dyn_lib_args.as_mut_ptr();
2335 let mut ffi_args: Vec<libffi::middle::Arg> = Vec::with_capacity(args.len());
2338 keep_alive.clear();
2339
2340 for idx in 0..args.len() {
2341 let data = regs[unsafe { *args.get_unchecked(idx) }];
2342 if data.is_int() {
2343 unsafe {
2344 args_ptr.add(idx).write(data.as_int() as u64);
2345 ffi_args.push(libffi::middle::Arg::new(&*args_ptr.add(idx)));
2346 }
2347 } else if data.is_float() {
2348 unsafe {
2349 args_ptr.add(idx).write(data.as_float().to_bits());
2350 ffi_args.push(libffi::middle::Arg::new(&*args_ptr.add(idx)));
2351 }
2352 } else if data.is_string() {
2353 let bytes = if let Ok(b) = std::ffi::CString::new(data.as_str(str_pool)) {
2354 b.into_bytes_with_nul().into_boxed_slice()
2355 } else {
2356 error_with_catch!(ErrType::NullByteInString, 'main);
2357 };
2358 let ptr = bytes.as_ptr() as u64;
2359 keep_alive.push(bytes);
2360 unsafe {
2361 args_ptr.add(idx).write(ptr);
2362 ffi_args.push(libffi::middle::Arg::new(&*args_ptr.add(idx)));
2363 }
2364 } else if data.is_array() {
2365 let ptr = ffi::array_to_c_ptr(data, obj_pool, str_pool, keep_alive) as u64;
2366 unsafe {
2367 args_ptr.add(idx).write(ptr);
2368 ffi_args.push(libffi::middle::Arg::new(&*args_ptr.add(idx)));
2369 }
2370 } else if data.is_struct() {
2371 let b = ffi::candela_struct_to_c_struct(
2372 data.as_struct(),
2373 obj_pool,
2374 str_pool,
2375 keep_alive,
2376 )
2377 .into_boxed_slice();
2378 let ptr = &raw const *b;
2379 keep_alive.push(b);
2380 ffi_args.push(libffi::middle::Arg::new(unsafe { &*ptr }));
2381 } else {
2382 unsafe { unreachable_unchecked() }
2383 }
2384 }
2385 args.clear();
2386
2387 let func = unsafe { dyn_libs.get_unchecked(fn_id as usize) };
2388 regs[dest] = unsafe {
2390 match func.get_return_type() {
2391 DataType::Int => func.cif.call::<i64>(func.ptr, &ffi_args).into(),
2392 DataType::Float => func.cif.call::<f64>(func.ptr, &ffi_args).into(),
2393 DataType::String => {
2394 let ptr = func
2395 .cif
2396 .call::<*const std::ffi::c_char>(func.ptr, &ffi_args);
2397 if ptr.is_null() {
2398 cold_path();
2399 NULL
2400 } else {
2401 string!(
2402 std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned()
2403 )
2404 }
2405 }
2406 DataType::Struct(struct_idx) => {
2407 let struct_fields =
2408 unsafe { &structs.get_unchecked(*struct_idx as usize).fields };
2409 let (struct_size, _, field_offsets) =
2410 ffi::get_struct_size_datatype(struct_fields, structs);
2411 let mut return_buf: Vec<u8> = vec![0; struct_size];
2412 func.cif.call_return_into(
2413 func.ptr,
2414 &ffi_args,
2415 libffi::middle::ret(&mut return_buf[..]),
2416 );
2417
2418 let new_id = ffi::c_struct_to_candela_struct(
2419 &return_buf,
2420 &field_offsets,
2421 obj_pool,
2422 map_pool,
2423 str_pool,
2424 struct_fields,
2425 r,
2426 recursion_stack,
2427 gc,
2428 structs,
2429 );
2430 Data::struct_instance(*struct_idx, new_id)
2431 }
2432 DataType::Null => {
2433 func.cif.call::<()>(func.ptr, &ffi_args);
2434 NULL
2435 }
2436 DataType::Array(_) => {
2437 error_with_catch!(ErrType::CArrayReturnTypeNotSupported);
2438 }
2439 t => {
2440 error_with_catch!(ErrType::InvalidReturnType(t));
2441 }
2442 }
2443 };
2444 }
2445 Instr::CallHostFunc(fn_id, dest) => {
2446 let dispatch = unsafe { host_dispatch.get_unchecked(fn_id as usize) };
2447
2448 host_call_args.clear();
2451 for idx in 0..args.len() {
2452 let data = regs[unsafe { *args.get_unchecked(idx) }];
2453 host_call_args.push(crate::embed::unmarshal_value(
2454 data, obj_pool, map_pool, str_pool, structs, enums,
2455 ));
2456 }
2457 args.clear();
2458
2459 match (**dispatch)(host_call_args) {
2460 Ok(value) => {
2465 regs[dest] = crate::embed::marshal_value(
2466 &value,
2467 r,
2468 recursion_stack,
2469 obj_pool,
2470 map_pool,
2471 str_pool,
2472 gc,
2473 );
2474 }
2475 Err(err) => {
2478 let function = host_sigs.get(fn_id as usize).map_or_else(
2479 || format!("#{fn_id}"),
2480 |sig| crate::embed::qualified_name(&sig.namespace, &sig.name),
2481 );
2482 error_with_catch!(ErrType::HostFn(&function, err.message()));
2483 }
2484 }
2485 }
2486 Instr::AddStr(o1, o2, dest) => {
2487 let d1 = regs[o1];
2488 let d2 = regs[o2];
2489 if !d1.is_string() || !d2.is_string() {
2495 let culprit = if d1.is_string() { d2 } else { d1 };
2496 error_with_catch!(ErrType::NotAString(culprit.type_name()));
2497 }
2498 let left = d1.as_str(str_pool);
2499 let right = d2.as_str(str_pool);
2500 let mut s = String::with_capacity(left.len() + right.len());
2501 s.push_str(left);
2502 s.push_str(right);
2503 regs[dest] = string!(s);
2504 }
2505 Instr::EmptyArray(arr_reg_id) => {
2506 let array_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2507 regs[arr_reg_id] = Data::array(array_id);
2508 }
2509 Instr::EmptyFnValue(value_reg_id) => {
2510 let array_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2511 regs[value_reg_id] = Data::function(array_id);
2512 }
2513 Instr::CloneArray(src_reg, dest_reg, len) => {
2514 gc.note_bytes(len as usize * LIST_ENTRY_BYTES);
2515 let src_id = regs[src_reg].as_array();
2516 let new_id =
2517 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc) as usize;
2518 let src_ptr = obj_pool[src_id].as_ptr();
2519 let dst = obj_pool.get_mut(new_id);
2520 dst.reserve_exact(len as usize);
2521 unsafe {
2522 dst.set_len(len as usize);
2523 std::ptr::copy_nonoverlapping(src_ptr, dst.as_mut_ptr(), len as usize);
2524 }
2525 regs[dest_reg] = Data::array(new_id as u32);
2526 }
2527 Instr::CloneEnum(src_reg, dest_reg) => {
2528 let new_id =
2529 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc) as usize;
2530 let src_reg = regs[src_reg];
2531 let src = &obj_pool[src_reg.as_enum()];
2532 let len = src.len();
2533 unsafe {
2534 let src_ptr = src.as_ptr();
2535 let dst = obj_pool.get_mut(new_id);
2536 dst.reserve_exact(len);
2537 dst.set_len(len);
2538 std::ptr::copy_nonoverlapping(src_ptr, dst.as_mut_ptr(), len);
2539 }
2540 regs[dest_reg] = Data::enum_instance(src_reg.enum_type_id(), new_id as u32);
2541 }
2542 Instr::AddArray(o1, o2, dest) => {
2543 let arr_a_id = regs[o1].as_array();
2544 let arr_b_id = regs[o2].as_array();
2545 let array_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2546 let array_idx = array_id as usize;
2547 unsafe {
2548 let array_pool_ptr = obj_pool.as_mut_ptr();
2549 let arr_a = &*array_pool_ptr.add(arr_a_id);
2550 let arr_b = &*array_pool_ptr.add(arr_b_id);
2551 let combined = &mut *array_pool_ptr.add(array_idx);
2552
2553 combined.reserve(arr_a.len() + arr_b.len());
2554 combined.extend_from_slice(arr_a);
2555 combined.extend_from_slice(arr_b);
2556 gc.note_bytes(combined.capacity() * LIST_ENTRY_BYTES);
2557 }
2558 regs[dest] = Data::array(array_id);
2559 }
2560 Instr::DivInt(o1, o2, dest) => {
2561 let b = regs[o2].as_int();
2562 if b == 0 {
2563 error_with_catch!(ErrType::DivisionByZero);
2564 }
2565 regs[dest] = (regs[o1].as_int() / b).into();
2566 }
2567 Instr::ModFloat(o1, o2, dest) => {
2568 regs[dest] = (regs[o1].as_float() % regs[o2].as_float()).into();
2569 }
2570 Instr::ModInt(o1, o2, dest) => {
2571 let b = regs[o2].as_int();
2572 if b == 0 {
2573 error_with_catch!(ErrType::ModuloByZero);
2574 }
2575 regs[dest] = (regs[o1].as_int() % b).into();
2576 }
2577 Instr::PowFloat(o1, o2, dest) => {
2578 regs[dest] = (regs[o1].as_float().powf(regs[o2].as_float())).into();
2579 }
2580 Instr::PowInt(o1, o2, dest) => {
2581 let exp = regs[o2].as_int();
2582 if exp < 0 {
2583 error_with_catch!(ErrType::NegativeExponent(exp));
2584 }
2585 regs[dest] = (regs[o1].as_int().pow(exp as u32)).into();
2586 }
2587 Instr::BitAndInt(o1, o2, dest) => {
2588 regs[dest] = (regs[o1].as_int() & regs[o2].as_int()).into();
2589 }
2590 Instr::BitOrInt(o1, o2, dest) => {
2591 regs[dest] = (regs[o1].as_int() | regs[o2].as_int()).into();
2592 }
2593 Instr::BitXorInt(o1, o2, dest) => {
2594 regs[dest] = (regs[o1].as_int() ^ regs[o2].as_int()).into();
2595 }
2596 Instr::BitNotInt(o1, dest) => {
2597 regs[dest] = (!regs[o1].as_int()).into();
2598 }
2599 Instr::ShlInt(o1, o2, dest) => {
2600 let count = regs[o2].as_int();
2601 if !shift_count_in_range(count) {
2602 error_with_catch!(ErrType::ShiftCountOutOfRange(count));
2603 }
2604 regs[dest] = (regs[o1].as_int() << count).into();
2605 }
2606 Instr::ShrInt(o1, o2, dest) => {
2607 let count = regs[o2].as_int();
2608 if !shift_count_in_range(count) {
2609 error_with_catch!(ErrType::ShiftCountOutOfRange(count));
2610 }
2611 regs[dest] = (regs[o1].as_int() >> count).into();
2612 }
2613 Instr::ObjEq(o1, o2, dest) => {
2614 regs[dest] = obj_eq(regs[o1], regs[o2], obj_pool, map_pool, str_pool).into();
2615 }
2616 Instr::ObjNotEqJmp(o1, o2, jump_size) => {
2617 if !obj_eq(regs[o1], regs[o2], obj_pool, map_pool, str_pool) {
2618 i += jump_size as usize;
2619 continue;
2620 }
2621 }
2622 Instr::StrNotEqJmp(o1, o2, jump_size) => {
2623 if !str_eq(regs[o1], regs[o2], str_pool) {
2624 i += jump_size as usize;
2625 continue;
2626 }
2627 }
2628 Instr::ObjNotEq(o1, o2, dest) => {
2629 regs[dest] = (!obj_eq(regs[o1], regs[o2], obj_pool, map_pool, str_pool)).into();
2630 }
2631 Instr::StrEq(o1, o2, dest) => {
2632 regs[dest] = str_eq(regs[o1], regs[o2], str_pool).into();
2633 }
2634 Instr::StrNotEq(o1, o2, dest) => {
2635 regs[dest] = (!str_eq(regs[o1], regs[o2], str_pool)).into();
2636 }
2637 Instr::ObjEqJmp(o1, o2, jump_size) => {
2638 if obj_eq(regs[o1], regs[o2], obj_pool, map_pool, str_pool) {
2639 i += jump_size as usize;
2640 continue;
2641 }
2642 }
2643 Instr::StrEqJmp(o1, o2, jump_size) => {
2644 if str_eq(regs[o1], regs[o2], str_pool) {
2645 i += jump_size as usize;
2646 continue;
2647 }
2648 }
2649 Instr::SupStr(o1, o2, dest) => {
2650 str_order!(o1, o2, dest, >);
2651 }
2652 Instr::SupEqStr(o1, o2, dest) => {
2653 str_order!(o1, o2, dest, >=);
2654 }
2655 Instr::InfStr(o1, o2, dest) => {
2656 str_order!(o1, o2, dest, <);
2657 }
2658 Instr::InfEqStr(o1, o2, dest) => {
2659 str_order!(o1, o2, dest, <=);
2660 }
2661 Instr::Print(tgt) => {
2662 print_value(
2663 handle, regs[tgt], obj_pool, map_pool, str_pool, structs, enums,
2664 );
2665 }
2666 Instr::SetElementString(string_reg_id, new_str_reg_id, idx) => {
2667 let index = regs[idx].as_int();
2668 let temp_str_reg_id = regs[string_reg_id];
2669 let source_string = temp_str_reg_id.as_str(str_pool);
2670 if index < 0 || (index as u64) >= source_string.len() as u64 {
2671 error_with_catch!(ErrType::IndexOutOfBounds(source_string.len(), index));
2672 }
2673 let mut temp = source_string.to_owned();
2674 temp.remove(index as usize);
2675 temp.insert_str(index as usize, regs[new_str_reg_id].as_str(str_pool));
2676 regs[string_reg_id] = string!(temp);
2677 }
2678 Instr::GetSliceArray(array_reg_id, idx_start_id, dest_reg_id) => {
2679 let idx_start = regs[idx_start_id].as_int();
2680 let idx_end = regs[args.pop_unchecked()].as_int();
2681 let arr_id = regs[array_reg_id].as_array();
2682 let array = &obj_pool[arr_id];
2683 if (idx_end as u64) > array.len() as u64
2688 || (idx_start as u64) > array.len() as u64
2689 || idx_start > idx_end
2690 {
2691 error_with_catch!(ErrType::SliceOutOfBounds(array.len(), idx_start, idx_end));
2692 }
2693 let new_array_id =
2694 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2695 unsafe {
2696 if arr_id < (new_array_id as usize) {
2697 let (left, right) = obj_pool.split_at_mut(new_array_id as usize);
2698 right.get_unchecked_mut(0).extend_from_slice(
2699 left.get_unchecked(arr_id)
2700 .get_unchecked((idx_start as usize)..(idx_end as usize)),
2701 );
2702 } else {
2703 let (left, right) = obj_pool.split_at_mut(arr_id);
2704 left.get_unchecked_mut(new_array_id as usize)
2705 .extend_from_slice(
2706 right
2707 .get_unchecked(0)
2708 .get_unchecked((idx_start as usize)..(idx_end as usize)),
2709 );
2710 }
2711 }
2712 gc.note_bytes(obj_pool[new_array_id as usize].capacity() * LIST_ENTRY_BYTES);
2713 regs[dest_reg_id] = Data::array(new_array_id);
2714 }
2715 Instr::GetIndexString(tgt, index, dest) => {
2720 let idx = regs[index].as_int();
2721 let tgt_data = regs[tgt];
2722 let char_len = tgt_data.str_char_len(str_pool);
2723 if idx < 0 || (idx as u64) >= char_len as u64 {
2724 error_with_catch!(ErrType::IndexOutOfBounds(char_len, idx));
2725 }
2726 let bytes = tgt_data.as_str(str_pool).as_bytes();
2727 let (start, end) = if char_len == bytes.len() {
2733 (idx as usize, idx as usize + 1)
2734 } else {
2735 let start = tgt_data.str_byte_offset(idx as usize, str_pool);
2736 (
2737 start,
2738 start + utf8_width(unsafe { *bytes.get_unchecked(start) }),
2739 )
2740 };
2741 regs[dest] = Data::small_str(unsafe {
2742 std::str::from_utf8_unchecked(bytes.get_unchecked(start..end))
2743 });
2744 }
2745 Instr::GetSliceString(str_reg_id, idx_start, dest_reg_id) => {
2746 let idx_start = regs[idx_start].as_int();
2747 let idx_end = regs[args.pop_unchecked()].as_int();
2748 let src = regs[str_reg_id];
2749 let char_len = src.str_char_len(str_pool);
2750 if (idx_end as u64) > char_len as u64
2753 || (idx_start as u64) > char_len as u64
2754 || idx_start > idx_end
2755 {
2756 error_with_catch!(ErrType::SliceOutOfBounds(char_len, idx_start, idx_end));
2757 }
2758 let text = src.as_str(str_pool);
2759 let (start, end) = if char_len == text.len() {
2760 (idx_start as usize, idx_end as usize)
2761 } else {
2762 let start = src.str_byte_offset(idx_start as usize, str_pool);
2767 let span = char_byte_offset(&text[start..], (idx_end - idx_start) as usize);
2768 (start, start + span)
2769 };
2770 let part = src.as_str(str_pool)[start..end].to_smolstr();
2773 regs[dest_reg_id] = string!(part.as_str());
2774 }
2775 Instr::NewCell(src, dest) => {
2776 let cell_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2777 obj_pool.get_mut(cell_id as usize).push(regs.get(src));
2778 regs[dest] = Data::array(cell_id);
2779 }
2780 Instr::Remove(array, idx) => {
2781 let id = regs[array].as_array();
2782 let arr = obj_pool.get_mut(id);
2783 let index = regs[idx].as_int();
2784 if index < 0 || (index as u64) >= arr.len() as u64 {
2785 error_with_catch!(ErrType::IndexOutOfBounds(arr.len(), index));
2786 }
2787 let removed = arr.remove(index as usize);
2788 if gc.marking() {
2789 shade_overwritten(gc, str_pool, removed);
2790 rescan_list(gc, id);
2791 }
2792 }
2793 Instr::MapGet(map_reg_id, key_reg_id, dest_reg_id) => {
2794 regs[dest_reg_id] = if let Some(elem) = unsafe {
2795 map_pool[regs[map_reg_id].as_map()].get(&KeyByText {
2796 key: regs[key_reg_id],
2797 strings: str_pool,
2798 })
2799 } {
2800 *elem
2801 } else {
2802 cold_path();
2803 error_with_catch!(ErrType::UnknownMapKey(
2804 regs[key_reg_id]
2805 .format(obj_pool, str_pool, map_pool, structs, enums, false)
2806 .as_str()
2807 ));
2808 };
2809 }
2810 Instr::MapInsert(map_pool_id, key_reg_id, val_reg_id) => {
2811 let old = map_insert_counted(
2812 &mut map_pool[map_pool_id as usize],
2813 regs[key_reg_id],
2814 regs[val_reg_id],
2815 str_pool,
2816 gc,
2817 );
2818 if let Some(old) = old
2819 && gc.marking()
2820 {
2821 shade_overwritten(gc, str_pool, old);
2822 }
2823 }
2824 Instr::MapInsertReg(map_reg_id, key_reg_id, val_reg_id) => {
2825 let old = map_insert_counted(
2826 &mut map_pool[regs[map_reg_id].as_map()],
2827 regs[key_reg_id],
2828 regs[val_reg_id],
2829 str_pool,
2830 gc,
2831 );
2832 if let Some(old) = old
2833 && gc.marking()
2834 {
2835 shade_overwritten(gc, str_pool, old);
2836 }
2837 }
2838 Instr::MapRemove(map_reg_id, key_reg_id) => {
2841 let id = regs[map_reg_id].as_map();
2842 let removed = map_pool[id].shift_remove_entry(&KeyByText {
2846 key: regs[key_reg_id],
2847 strings: str_pool,
2848 });
2849 if let Some((key, value)) = removed
2850 && gc.marking()
2851 {
2852 shade_overwritten(gc, str_pool, key);
2853 shade_overwritten(gc, str_pool, value);
2854 rescan_map(gc, id);
2855 }
2856 }
2857 Instr::CloneMap(src_reg, dest_reg) => {
2858 let new_map = map_pool[regs[src_reg].as_map()].clone();
2859 gc.note_bytes(new_map.capacity() * MAP_ENTRY_BYTES);
2860 let new_id = alloc_map(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2861 unsafe {
2862 map_pool[new_id as usize] = new_map;
2863 }
2864 regs[dest_reg] = Data::map(new_id);
2865 }
2866 Instr::CallLibFunc(LibFunc::Uppercase, source_string_reg_id, dest_reg_id) => {
2867 regs[dest_reg_id] =
2868 string!(regs[source_string_reg_id].as_str(str_pool).to_uppercase());
2869 }
2870 Instr::CallLibFunc(LibFunc::Lowercase, source_string_reg_id, dest_reg_id) => {
2871 regs[dest_reg_id] =
2872 string!(regs[source_string_reg_id].as_str(str_pool).to_lowercase());
2873 }
2874 Instr::CallLibFunc(LibFunc::Contains, tgt, dest) => {
2875 let reg = regs[tgt];
2876 if reg.is_string() {
2877 let str = reg.as_str(str_pool);
2878 let temp_arg = regs[args.pop_unchecked()];
2879 let arg = temp_arg.as_str(str_pool);
2880 regs[dest] = memmem::find(str.as_bytes(), arg.as_bytes())
2882 .is_some()
2883 .into();
2884 } else if reg.is_array() {
2885 let arg = regs[args.pop_unchecked()];
2886 regs[dest] = obj_pool[reg.as_array()]
2887 .iter()
2888 .any(|&x| same_key(x, arg, str_pool))
2889 .into();
2890 } else if reg.is_map() {
2891 let arg = regs[args.pop_unchecked()];
2892 regs[dest] = map_pool[reg.as_map()]
2893 .contains_key(&KeyByText {
2894 key: arg,
2895 strings: str_pool,
2896 })
2897 .into();
2898 }
2899 }
2900 Instr::CallLibFunc(LibFunc::Trim, tgt, dest) => {
2901 regs[dest] = string!(regs[tgt].as_str(str_pool).trim());
2902 }
2903 Instr::CallLibFunc(LibFunc::TrimSequence, tgt, dest) => {
2904 let temp_arg = regs[args.pop_unchecked()];
2905 let arg = temp_arg.as_str(str_pool);
2906 let chars: Vec<char> = arg.chars().collect();
2907 regs[dest] = string!(regs[tgt].as_str(str_pool).trim_matches(&chars[..]));
2908 }
2909 Instr::CallLibFunc(LibFunc::Find, tgt, dest) => {
2910 let reg = regs[tgt];
2911 if reg.is_string() {
2912 let str = reg.as_str(str_pool);
2913 let temp_elem = regs[args.pop_unchecked()];
2914 let element = temp_elem.as_str(str_pool);
2915 regs[dest] =
2916 if let Some(byte_idx) = memmem::find(str.as_bytes(), element.as_bytes()) {
2917 if reg.str_is_ascii(str_pool) {
2922 byte_idx as i64
2923 } else {
2924 count_chars(&str[..byte_idx]) as i64
2925 }
2926 } else {
2927 cold_path();
2928 -1
2929 }
2930 .into();
2931 } else if reg.is_array() {
2932 let arr_id = reg.as_array();
2933 let element = regs[args.pop_unchecked()];
2934 regs[dest] = if let Some(idx) = obj_pool[arr_id]
2935 .iter()
2936 .position(|&x| same_key(x, element, str_pool))
2937 {
2938 idx as i64
2939 } else {
2940 cold_path();
2941 -1
2942 }
2943 .into();
2944 }
2945 }
2946 Instr::CallLibFunc(LibFunc::IsFloat, tgt, dest) => {
2947 let temp_tgt = regs[tgt];
2948 let num = temp_tgt.as_str(str_pool);
2949 regs[dest] = (num.parse::<i64>().is_err() && num.parse::<f64>().is_ok()).into();
2950 }
2951 Instr::CallLibFunc(LibFunc::IsInt, tgt, dest) => {
2952 regs[dest] = regs[tgt].as_str(str_pool).parse::<i64>().is_ok().into();
2953 }
2954 Instr::CallLibFunc(LibFunc::TrimLeft, tgt, dest) => {
2955 regs[dest] = string!(regs[tgt].as_str(str_pool).trim_start());
2956 }
2957 Instr::CallLibFunc(LibFunc::TrimRight, tgt, dest) => {
2958 regs[dest] = string!(regs[tgt].as_str(str_pool).trim_end());
2959 }
2960 Instr::CallLibFunc(LibFunc::TrimSequenceLeft, tgt, dest) => {
2961 let chars: Vec<char> = regs[args.pop_unchecked()]
2962 .as_str(str_pool)
2963 .chars()
2964 .collect();
2965 regs[dest] = string!(regs[tgt].as_str(str_pool).trim_start_matches(&chars[..]));
2966 }
2967 Instr::CallLibFunc(LibFunc::TrimSequenceRight, tgt, dest) => {
2968 let chars: Vec<char> = regs[args.pop_unchecked()]
2969 .as_str(str_pool)
2970 .chars()
2971 .collect();
2972 regs[dest] = string!(regs[tgt].as_str(str_pool).trim_end_matches(&chars[..]));
2973 }
2974 Instr::CallLibFunc(LibFunc::Repeat, tgt, dest) => {
2975 let reg = regs[tgt];
2976 if reg.is_string() {
2977 let str = reg.as_str(str_pool);
2978 let repeat_count = regs[args.pop_unchecked()].as_int();
2979 regs[dest] = string!(str.repeat(repeat_count as usize));
2980 } else if reg.is_array() {
2981 let repeat_count = regs[args.pop_unchecked()].as_int();
2982 let array_id =
2983 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
2984 obj_pool[array_id as usize] =
2985 obj_pool[reg.as_array()].repeat(repeat_count as usize);
2986 gc.note_bytes(obj_pool[array_id as usize].capacity() * LIST_ENTRY_BYTES);
2987 regs[dest] = Data::array(array_id);
2988 }
2989 }
2990 Instr::CallLibFunc(LibFunc::Round, tgt, dest) => {
2991 regs[dest] = regs[tgt].as_float().round().into();
2992 }
2993 Instr::CallLibFunc(LibFunc::Abs, tgt, dest) => {
2994 let tgt = regs[tgt];
2995 regs[dest] = if tgt.is_float() {
2996 tgt.as_float().abs().into()
2997 } else {
2998 tgt.as_int().abs().into()
2999 }
3000 }
3001 Instr::CallLibFunc(LibFunc::Reverse, tgt, dest) => {
3002 regs[dest] = string!(regs[tgt].as_str(str_pool).chars().rev().collect::<String>());
3003 }
3004 Instr::CallLibFuncVoid(LibFuncVoid::Reverse, tgt, _) => {
3005 let id = regs[tgt].as_array();
3006 obj_pool.get_mut(id).reverse();
3007 if gc.marking() {
3008 rescan_list(gc, id);
3009 }
3010 }
3011 Instr::CallLibFunc(LibFunc::SqrtFloat, tgt, dest) => {
3012 regs[dest] = regs[tgt].as_float().sqrt().into();
3013 }
3014 Instr::CallLibFunc(LibFunc::Float, tgt, dest) => {
3015 let reg = regs[tgt];
3016 if reg.is_int() {
3017 regs[dest] = (reg.as_int() as f64).into();
3018 } else if reg.is_string() {
3019 let str = reg.as_str(str_pool);
3020 regs[dest] = if let Ok(f) = lexical_core::parse::<f64>(str.as_bytes()) {
3021 f.into()
3022 } else {
3023 error_with_catch!(ErrType::InvalidFloat);
3024 }
3025 }
3026 }
3027 Instr::CallLibFunc(LibFunc::Int, tgt, dest) => {
3028 let reg = regs[tgt];
3029 if reg.is_float() {
3030 regs[dest] = (reg.as_float() as i64).into();
3031 } else if reg.is_string() {
3032 let str = reg.as_str(str_pool);
3033 regs[dest] = if let Ok(i) = lexical_core::parse::<i64>(str.as_bytes()) {
3034 i.into()
3035 } else {
3036 cold_path();
3037 error_with_catch!(ErrType::InvalidInt);
3038 }
3039 }
3040 }
3041 Instr::CallLibFunc(LibFunc::Str, tgt, dest) => {
3042 let value = regs[tgt];
3043 regs[dest] = if value.is_int() {
3044 let mut buffer = [0u8; i64::FORMATTED_SIZE_DECIMAL];
3045 let digits = lexical_core::write(value.as_int(), &mut buffer);
3046 string!(unsafe { str::from_utf8_unchecked(digits) })
3047 } else if value.is_float() {
3048 string!(format_float(value.as_float()).as_str())
3049 } else if value.is_bool() {
3050 Data::small_str(if value.as_bool() { "true" } else { "false" })
3051 } else if value.is_string() {
3052 value
3053 } else {
3054 string!(
3055 value
3056 .format(obj_pool, str_pool, map_pool, structs, enums, false)
3057 .as_str()
3058 )
3059 };
3060 }
3061 Instr::CallLibFunc(LibFunc::Bool, tgt, dest) => {
3062 let temp_tgt = regs[tgt];
3063 let str = temp_tgt.as_str(str_pool);
3064 regs[dest] = if str == "true" {
3065 TRUE
3066 } else if str == "false" {
3067 FALSE
3068 } else {
3069 cold_path();
3070 error_with_catch!(ErrType::InvalidBool);
3071 }
3072 }
3073 #[cfg(target_arch = "wasm32")]
3074 Instr::CallLibFunc(LibFunc::Input, _, _) => wasm_error("WASM does not support input()"),
3075 #[cfg(not(target_arch = "wasm32"))]
3076 Instr::CallLibFunc(LibFunc::Input, tgt, dest) => {
3077 let temp_tgt = regs[tgt];
3078 let str_msg = temp_tgt.as_str(str_pool);
3079 out!(*handle, "{str_msg}");
3080 let _ = handle.flush();
3084 let mut line = String::new();
3085 std::io::stdin().read_line(&mut line).unwrap();
3086 regs[dest] = string!(line.trim_end_matches(['\n', '\r']));
3087 }
3088 Instr::CallLibFunc(LibFunc::Floor, tgt, dest) => {
3089 regs[dest] = regs[tgt].as_float().floor().into();
3090 }
3091 Instr::CallLibFunc(LibFunc::TheAnswer, _, dest) => {
3092 outln!(
3093 *handle,
3094 "The answer to the Ultimate Question of Life, the Universe, and Everything is 42."
3095 );
3096 regs[dest] = 42.into();
3097 }
3098 Instr::CallLibFunc(LibFunc::Len, tgt, dest) => {
3099 let reg = regs[tgt];
3100 if reg.is_array() {
3101 regs[dest] = (obj_pool[reg.as_array()].len() as i64).into();
3102 } else if reg.is_string() {
3103 regs[dest] = (reg.str_char_len(str_pool) as i64).into();
3104 } else if reg.is_map() {
3105 regs[dest] = (map_pool[reg.as_map()].len() as i64).into();
3106 } else {
3107 unsafe { unreachable_unchecked() }
3108 }
3109 }
3110 Instr::CallLibFunc(LibFunc::Keys, tgt, dest) => {
3111 let map_data = regs[tgt];
3112 let out_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
3113 let keys: Vec<Data> = map_pool[map_data.as_map()].keys().copied().collect();
3114 let out = obj_pool.get_mut(out_id as usize);
3115 out.clear();
3116 out.extend(keys);
3117 gc.note_bytes(out.capacity() * LIST_ENTRY_BYTES);
3118 regs[dest] = Data::array(out_id);
3119 }
3120 Instr::CallLibFunc(LibFunc::Values, tgt, dest) => {
3121 let map_data = regs[tgt];
3122 let out_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
3123 let vals: Vec<Data> = map_pool[map_data.as_map()].values().copied().collect();
3124 let out = obj_pool.get_mut(out_id as usize);
3125 out.clear();
3126 out.extend(vals);
3127 gc.note_bytes(out.capacity() * LIST_ENTRY_BYTES);
3128 regs[dest] = Data::array(out_id);
3129 }
3130 Instr::CallLibFunc(LibFunc::JsonParse, tgt, dest) => {
3131 let input = regs[tgt].as_str(str_pool).to_owned();
3132 match crate::json::json_parse(
3133 &input,
3134 obj_pool,
3135 map_pool,
3136 str_pool,
3137 r,
3138 recursion_stack,
3139 gc,
3140 ) {
3141 Ok(value) => regs[dest] = value,
3142 Err(reason) => {
3143 error_with_catch!(ErrType::JsonParse(reason));
3144 }
3145 }
3146 }
3147 Instr::CallLibFunc(LibFunc::JsonStringify, tgt, dest) => {
3148 let json = crate::json::json_stringify(
3149 regs[tgt], obj_pool, map_pool, str_pool, structs, enums,
3150 );
3151 regs[dest] = string!(json);
3152 }
3153 Instr::CallLibFunc(LibFunc::IsIntVal, tgt, dest) => {
3154 regs[dest] = regs[tgt].is_int().into();
3155 }
3156 Instr::CallLibFunc(LibFunc::IsFloatVal, tgt, dest) => {
3157 regs[dest] = regs[tgt].is_float().into();
3158 }
3159 Instr::CallLibFunc(LibFunc::IsStrVal, tgt, dest) => {
3160 regs[dest] = regs[tgt].is_string().into();
3161 }
3162 Instr::CallLibFunc(LibFunc::IsBoolVal, tgt, dest) => {
3163 regs[dest] = regs[tgt].is_bool().into();
3164 }
3165 Instr::CallLibFunc(LibFunc::IsListVal, tgt, dest) => {
3166 regs[dest] = regs[tgt].is_array().into();
3167 }
3168 Instr::CallLibFunc(LibFunc::IsMapVal, tgt, dest) => {
3169 regs[dest] = regs[tgt].is_map().into();
3170 }
3171 Instr::CallLibFunc(LibFunc::IsNullVal, tgt, dest) => {
3172 regs[dest] = regs[tgt].is_null().into();
3173 }
3174 Instr::CallLibFunc(LibFunc::AsIntVal, tgt, dest) => {
3175 let v = regs[tgt];
3176 if v.is_int() {
3177 regs[dest] = v;
3178 } else {
3179 error_with_catch!(ErrType::BadDowncast("int", v.type_name()));
3180 }
3181 }
3182 Instr::CallLibFunc(LibFunc::AsFloatVal, tgt, dest) => {
3183 let v = regs[tgt];
3184 if v.is_float() {
3185 regs[dest] = v;
3186 } else {
3187 error_with_catch!(ErrType::BadDowncast("float", v.type_name()));
3188 }
3189 }
3190 Instr::CallLibFunc(LibFunc::AsStrVal, tgt, dest) => {
3191 let v = regs[tgt];
3192 if v.is_string() {
3193 regs[dest] = v;
3194 } else {
3195 error_with_catch!(ErrType::BadDowncast("string", v.type_name()));
3196 }
3197 }
3198 Instr::CallLibFunc(LibFunc::AsBoolVal, tgt, dest) => {
3199 let v = regs[tgt];
3200 if v.is_bool() {
3201 regs[dest] = v;
3202 } else {
3203 error_with_catch!(ErrType::BadDowncast("bool", v.type_name()));
3204 }
3205 }
3206 Instr::CallLibFunc(LibFunc::AsListVal, tgt, dest) => {
3207 let v = regs[tgt];
3208 if v.is_array() {
3209 regs[dest] = v;
3210 } else {
3211 error_with_catch!(ErrType::BadDowncast("list", v.type_name()));
3212 }
3213 }
3214 Instr::CallLibFunc(LibFunc::AsMapVal, tgt, dest) => {
3215 let v = regs[tgt];
3216 if v.is_map() {
3217 regs[dest] = v;
3218 } else {
3219 error_with_catch!(ErrType::BadDowncast("map", v.type_name()));
3220 }
3221 }
3222 Instr::CallLibFunc(LibFunc::AsTypeVal, tgt, dest) => {
3223 let codes = &obj_pool[regs[args.pop_unchecked()].as_array()];
3224 let v = regs[tgt];
3225 if codes
3226 .iter()
3227 .any(|code| has_type_code(v, *code, obj_pool, map_pool))
3228 {
3229 if v.is_function() && !obj_pool[v.as_array()][0].is_int() {
3234 error_with_catch!(ErrType::BadDowncast(
3235 "function with annotated parameters",
3236 "function"
3237 ));
3238 }
3239 regs[dest] = v;
3240 } else {
3241 let wanted = type_codes_text(codes, obj_pool, structs, enums);
3242 error_with_catch!(ErrType::BadDowncast(&wanted, v.type_name()));
3243 }
3244 }
3245 Instr::CallLibFunc(LibFunc::IsTypeVal, tgt, dest) => {
3246 let codes = &obj_pool[regs[args.pop_unchecked()].as_array()];
3247 let v = regs[tgt];
3248 regs[dest] = codes
3249 .iter()
3250 .any(|code| has_type_code(v, *code, obj_pool, map_pool))
3251 .into();
3252 }
3253 Instr::CallLibFunc(LibFunc::StartsWith, source_register, dest_register) => {
3254 regs[dest_register] = regs[source_register]
3255 .as_str(str_pool)
3256 .starts_with(regs[args.pop_unchecked()].as_str(str_pool))
3257 .into();
3258 }
3259 Instr::CallLibFunc(LibFunc::EndsWith, source_register, dest_register) => {
3260 regs[dest_register] = regs[source_register]
3261 .as_str(str_pool)
3262 .ends_with(regs[args.pop_unchecked()].as_str(str_pool))
3263 .into();
3264 }
3265 #[allow(clippy::no_effect_replace)]
3266 Instr::CallLibFunc(LibFunc::Replace, source_register, dest_register) => {
3267 regs[dest_register] = string!(regs[source_register].as_str(str_pool).replace(
3268 regs[args.pop_unchecked()].as_str(str_pool),
3269 regs[args.pop_unchecked()].as_str(str_pool),
3270 ));
3271 }
3272 Instr::CallLibFunc(LibFunc::Split, source_register, dest_register) => {
3273 let source = regs[source_register];
3274 let separator = unsafe { args.pop_unchecked() };
3275 if source.is_string() {
3276 let output_id =
3277 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
3278 recursion_stack.0.push(Data::array(output_id));
3283 let source = source.as_str(str_pool);
3284 let separator_data = regs[separator];
3285 let separator = separator_data.as_str(str_pool);
3286 obj_pool
3287 .get_mut(output_id as usize)
3288 .reserve(source.len() / 4);
3289 let separator_len = separator.len();
3290 if separator_len == 0 {
3291 let output = obj_pool.get_mut(output_id as usize);
3298 for (byte_i, character) in source.char_indices() {
3299 output.push(Data::small_str(
3300 &source[byte_i..byte_i + character.len_utf8()],
3301 ));
3302 }
3303 } else {
3304 let mut i = 0;
3305 for part_i in memmem::find_iter(source.as_bytes(), separator.as_bytes()) {
3306 let part = string!(&source[i..part_i]);
3307 obj_pool.get_mut(output_id as usize).push(part);
3308 i = part_i + separator_len;
3309 }
3310 let part = string!(&source[i..]);
3311 obj_pool.get_mut(output_id as usize).push(part);
3312 }
3313 recursion_stack.0.pop();
3314 gc.note_bytes(obj_pool[output_id as usize].capacity() * LIST_ENTRY_BYTES);
3315 regs[dest_register] = Data::array(output_id);
3316 } else if source.is_array() {
3317 let source_array_id = source.as_array();
3318 let separator = regs[separator];
3319 let source_array = &obj_pool[source_array_id];
3320 let source_len = source_array.len();
3321
3322 let mut split_ranges: Vec<(usize, usize)> =
3323 Vec::with_capacity(source_array.len() + 1);
3324
3325 let mut start = 0;
3326 for (idx, item) in source_array.iter().enumerate() {
3327 if *item == separator {
3328 split_ranges.push((start, idx));
3329 start = idx + 1;
3330 }
3331 }
3332 split_ranges.push((start, source_array.len()));
3333
3334 let base = recursion_stack.len();
3341 for (start, end) in split_ranges {
3342 let dest_array_id =
3343 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc)
3344 as usize;
3345 unsafe {
3346 if dest_array_id < source_array_id {
3347 let (left, right) = obj_pool.split_at_mut(source_array_id);
3348 left.get_unchecked_mut(dest_array_id).extend_from_slice(
3349 right.get_unchecked(0).get_unchecked(start..end),
3350 );
3351 } else {
3352 let (left, right) = obj_pool.split_at_mut(dest_array_id);
3353 right.get_unchecked_mut(0).extend_from_slice(
3354 left.get_unchecked(source_array_id)
3355 .get_unchecked(start..end),
3356 );
3357 }
3358 }
3359 recursion_stack.0.push(Data::array(dest_array_id as u32));
3360 }
3361
3362 let array_id =
3363 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
3364 let parts = obj_pool.get_mut(array_id as usize);
3365 parts.extend(recursion_stack.0.drain(base..));
3366 gc.note_bytes(source_len * LIST_ENTRY_BYTES);
3367
3368 regs[dest_register] = Data::array(array_id);
3369 }
3370 }
3371 Instr::CallLibFunc(LibFunc::Range, max, dest) => {
3372 let min = args.pop().map_or(0, |reg_id| regs[reg_id].as_int());
3373 let max = regs[max].as_int();
3374 let output_array_id =
3375 alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
3376 let range_arr = obj_pool.get_mut(output_array_id as usize);
3377 range_arr.extend((min..max).map(Data::from));
3378 gc.note_bytes(range_arr.capacity() * LIST_ENTRY_BYTES);
3379 regs[dest] = Data::array(output_array_id);
3380 }
3381 Instr::CallLibFunc(LibFunc::JoinStringArray, tgt, dest) => {
3382 let temp_separator: Option<Data> = args.pop().map(|arg| regs[arg]);
3383 let separator = temp_separator.as_ref().map_or("", |d| d.as_str(str_pool));
3384 let array = &obj_pool[regs[tgt].as_array()];
3385 let total_len: usize = array
3386 .iter()
3387 .map(|x| x.as_str(str_pool).len())
3388 .sum::<usize>()
3389 + separator
3390 .len()
3391 .saturating_mul(array.len().saturating_sub(1));
3392 let mut output = String::with_capacity(total_len);
3393 for (i, x) in array.iter().enumerate() {
3394 if i > 0 {
3395 output.push_str(separator);
3396 }
3397 output.push_str(x.as_str(str_pool));
3398 }
3399 regs[dest] = string!(output);
3400 }
3401 Instr::CallLibFunc(LibFunc::FsRead, path, dest_reg_id) => {
3405 regs[dest_reg_id] =
3406 string!(match fs::read_to_string(regs[path].as_str(str_pool)) {
3407 Ok(p) => p,
3408 Err(e) => {
3409 cold_path();
3410 error_with_catch!(ErrType::from(e.kind()));
3411 }
3412 });
3413 }
3414 Instr::CallLibFunc(LibFunc::FsExists, path, dest_reg_id) => {
3415 regs[dest_reg_id] = match fs::exists(regs[path].as_str(str_pool)) {
3416 Ok(b) => b.into(),
3417 Err(e) => {
3418 error_with_catch!(ErrType::from(e.kind()));
3419 }
3420 }
3421 }
3422 Instr::CallLibFuncVoid(LibFuncVoid::FsWrite, path, contents) => {
3424 if let Err(e) =
3425 fs::write(regs[path].as_str(str_pool), regs[contents].as_str(str_pool))
3426 {
3427 error_with_catch!(ErrType::from(e.kind()));
3428 }
3429 }
3430 Instr::CallLibFuncVoid(LibFuncVoid::FsAppend, path, contents) => {
3432 match fs::OpenOptions::new()
3433 .append(true)
3434 .create(true)
3435 .open(regs[path].as_str(str_pool))
3436 {
3437 Ok(mut f) => {
3438 if let Err(e) = f.write_all(regs[contents].as_str(str_pool).as_bytes()) {
3439 error_with_catch!(ErrType::from(e.kind()));
3440 }
3441 }
3442 Err(e) => {
3443 error_with_catch!(ErrType::from(e.kind()));
3444 }
3445 }
3446 }
3447 Instr::CallLibFuncVoid(LibFuncVoid::FsDelete, path, _) => {
3449 if let Err(e) = fs::remove_file(regs[path].as_str(str_pool)) {
3450 error_with_catch!(ErrType::from(e.kind()));
3451 }
3452 }
3453 Instr::CallLibFuncVoid(LibFuncVoid::FsDeleteDir, path, _) => {
3455 if let Err(e) = fs::remove_dir(regs[path].as_str(str_pool)) {
3456 error_with_catch!(ErrType::from(e.kind()));
3457 }
3458 }
3459 #[cfg(target_arch = "wasm32")]
3460 Instr::CallLibFunc(LibFunc::Argv, _, dest) => {
3461 regs[dest] = Data::array(alloc_array(
3462 obj_pool,
3463 map_pool,
3464 str_pool,
3465 r,
3466 recursion_stack,
3467 gc,
3468 ))
3469 }
3470 #[cfg(not(target_arch = "wasm32"))]
3471 Instr::CallLibFunc(LibFunc::Argv, _, dest) => {
3472 let base = recursion_stack.len();
3476 for arg in std::env::args().skip(crate::rt::argv_skip()) {
3477 let s = string!(arg);
3478 recursion_stack.0.push(s);
3479 }
3480 let array_id = alloc_array(obj_pool, map_pool, str_pool, r, recursion_stack, gc);
3481 let list = obj_pool.get_mut(array_id as usize);
3482 list.extend(recursion_stack.0.drain(base..));
3483 regs[dest] = Data::array(array_id);
3484 }
3485 Instr::CallLibFuncVoid(LibFuncVoid::Sort, tgt, _) => {
3486 let id = regs[tgt].as_array();
3487 if gc.marking() {
3488 rescan_list(gc, id);
3489 }
3490 let array = obj_pool.get_mut(id);
3491 if !array.is_empty() {
3492 if array[0].is_int() {
3493 array.sort_unstable_by_key(|x| x.as_int());
3494 } else if array[0].is_float() {
3495 array.sort_unstable_by(|a, b| {
3496 a.as_float()
3497 .partial_cmp(&b.as_float())
3498 .unwrap_or(std::cmp::Ordering::Equal)
3499 });
3500 } else if array[0].is_string() {
3501 array.sort_unstable_by(|a, b| a.as_str(str_pool).cmp(b.as_str(str_pool)));
3502 }
3503 }
3504 }
3505 Instr::StartErrorCatch(jmp_size, err_reg_id) => {
3506 error_handles.push(ErrorCatch {
3507 catch_loc: (i as u32) + (jmp_size as u32),
3508 error_reg: err_reg_id,
3509 call_frames_len: call_frames.len() as u32,
3510 args_len: args.len() as u32,
3511 recursion_len: recursion_stack.len() as u32,
3512 });
3513 }
3514 Instr::StopErrorCatch => unsafe {
3515 error_handles.pop_unchecked();
3516 },
3517 Instr::ThrowError(error_reg_id) => {
3518 error_with_catch!(ErrType::Custom(regs[error_reg_id].as_str(str_pool)));
3519 }
3520 Instr::NativeExit => {
3521 i = RETURNED;
3522 break;
3523 }
3524 Instr::Halt(code) => {
3525 cold_path();
3526
3527 #[cfg(not(target_arch = "wasm32"))]
3528 if code != 0 {
3529 std::process::exit(regs[code].as_int() as i32);
3532 }
3533
3534 i = HALTED;
3535 break;
3536 }
3537 _ => unreachable!(),
3539 }
3540 i += 1;
3541 }
3542 i
3543}