1pub extern crate wren_sys;
3
4use foreign_v2::ForeignItem;
5use std::any::{Any, TypeId};
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::rc::{Rc, Weak};
9use std::sync::mpsc::{channel, Receiver, Sender};
10use wren_sys::{wrenGetUserData, WrenConfiguration, WrenHandle, WrenVM};
11
12mod module_loader;
13pub use module_loader::{BasicFileLoader, NullLoader};
14
15pub mod foreign_v1;
16pub mod foreign_v2;
17
18use std::{any, ffi, marker, mem, os::raw};
19
20mod runtime;
21#[cfg(test)]
22mod tests;
23#[cfg(feature = "derive")]
24pub use ruwren_macros::*;
25
26#[derive(Debug)]
27pub enum WrenError {
29 Compile(String, i32, String),
30 Runtime(String),
31 StackTrace(String, i32, String),
32}
33
34#[derive(Debug, Clone)]
35pub enum VMError {
37 Compile {
38 module: String,
39 line: i32,
40 error: String,
41 },
42 Runtime {
43 error: String,
44 frames: Vec<VMStackFrameError>,
45 },
46}
47
48#[derive(Debug, Clone)]
49pub struct VMStackFrameError {
50 pub module: String,
51 pub line: i32,
52 pub function: String,
53}
54
55#[cfg(not(target_arch = "wasm32"))]
56pub fn handle_panic<F, O>(func: F) -> Result<O, Box<dyn Any + Send>>
57where
58 F: FnOnce() -> O + std::panic::UnwindSafe,
59{
60 std::panic::catch_unwind(func)
61}
62
63#[cfg(target_arch = "wasm32")]
64pub fn handle_panic<F, O: 'static>(func: F) -> Result<O, Box<dyn Any + Send>>
65where
66 F: FnOnce() -> O + std::panic::UnwindSafe,
67{
68 match std::panic::catch_unwind(func) {
69 Ok(o) => Ok(o),
70 _ => unreachable!("non-unwinding platforms (like WASM) can't catch unwinds, so don't panic unless absolutely necessary"),
71 }
72}
73
74impl std::fmt::Display for VMError {
75 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
76 match self {
77 VMError::Compile {
78 module,
79 line,
80 error,
81 } => write!(fmt, "Compile Error ({}:{}): {}", module, line, error),
82 VMError::Runtime { error, frames } => {
83 writeln!(fmt, "Runtime Error: {}", error)?;
84 for frame in frames {
85 if frame.function.is_empty() {
86 writeln!(fmt, "\tin {}:{}: <constructor>", frame.module, frame.line)?;
87 } else {
88 writeln!(
89 fmt,
90 "\tin {}:{}: {}",
91 frame.module, frame.line, frame.function
92 )?;
93 }
94 }
95 Ok(())
96 }
97 }
98 }
99}
100
101impl std::error::Error for VMError {}
102
103#[derive(Debug, PartialEq, Eq)]
105pub struct Handle<'a> {
106 handle: *mut WrenHandle,
107 wvm: *mut WrenVM,
108 vm: marker::PhantomData<&'a VM>,
109}
110
111impl<'a> Drop for Handle<'a> {
112 fn drop(&mut self) {
113 unsafe {
114 wren_sys::wrenReleaseHandle(self.wvm, self.handle);
115 }
116 }
117}
118
119#[derive(Debug, PartialEq, Eq)]
121pub struct FunctionHandle<'a>(Handle<'a>);
122
123#[derive(Debug, Clone, Default)]
125pub struct ModuleLibrary {
126 modules: HashMap<String, Module>,
127}
128
129impl ModuleLibrary {
130 pub fn new() -> ModuleLibrary {
132 ModuleLibrary {
133 modules: HashMap::new(),
134 }
135 }
136
137 pub fn module<N: Into<String>>(&mut self, name: N, modl: Module) {
139 let module_name = name.into();
140 if let Some(module) = self.modules.get_mut(&module_name) {
141 module.classes.extend(modl.classes);
142 } else {
143 self.modules.insert(module_name, modl);
144 }
145 }
146
147 fn get_foreign_class<M: AsRef<str>, C: AsRef<str>>(
149 &self, module: M, class: C,
150 ) -> Option<&RuntimeClass> {
151 self.modules
152 .get(module.as_ref())
153 .and_then(|md| md.classes.get(class.as_ref()))
154 }
155}
156
157#[derive(Debug, Clone)]
158struct RuntimeClass {
160 construct: extern "C" fn(*mut WrenVM),
161 destruct: extern "C" fn(*mut ffi::c_void),
162 methods: ClassObjectPointers,
163
164 type_id: any::TypeId,
166}
167
168#[derive(Debug, Clone, Default)]
169pub struct Module {
171 classes: HashMap<String, RuntimeClass>,
172}
173
174#[derive(Debug, Clone)]
175pub struct ClassObjectPointers {
177 pub function_pointers: Vec<MethodPointer>,
178}
179
180#[derive(Debug, Clone)]
181pub struct MethodPointer {
182 pub is_static: bool,
183 pub signature: FunctionSignature,
184 pub pointer: unsafe extern "C" fn(*mut WrenVM),
185}
186
187impl Module {
188 pub fn new() -> Module {
190 Module {
191 classes: HashMap::new(),
192 }
193 }
194
195 pub fn class<C: 'static + ClassObject, S: Into<String>>(&mut self, name: S) -> &mut Self {
197 let cp = C::generate_pointers();
198 let init = C::initialize_pointer();
199 let deinit = C::finalize_pointer();
200 self.classes.insert(
201 name.into(),
202 RuntimeClass {
203 construct: init,
204 destruct: deinit,
205 methods: cp,
206 type_id: any::TypeId::of::<C>(),
207 },
208 );
209 self
210 }
211}
212
213pub trait Class {
215 fn initialize(_: &VM) -> Self
216 where
217 Self: Sized;
218}
219
220pub trait ClassObject: Class {
222 fn initialize_pointer() -> extern "C" fn(*mut WrenVM)
223 where
224 Self: Sized;
225 fn finalize_pointer() -> extern "C" fn(*mut ffi::c_void)
226 where
227 Self: Sized;
228 fn generate_pointers() -> ClassObjectPointers
229 where
230 Self: Sized;
231}
232
233#[derive(Debug, Copy, Clone)]
234pub struct ForeignObject<T> {
236 pub object: *mut T,
237 pub type_id: any::TypeId,
238}
239
240pub fn type_name_of<T>(_: &T) -> &'static str {
241 any::type_name::<T>()
242}
243
244pub trait ModuleScriptLoader {
246 fn load_script(&mut self, name: String) -> Option<String>;
252}
253
254impl<T> ModuleScriptLoader for T
255where
256 T: FnMut(String) -> Option<String>,
257{
258 fn load_script(&mut self, name: String) -> Option<String> {
259 (*self)(name)
260 }
261}
262
263type Evm = Rc<RefCell<VM>>;
264
265pub trait Printer {
267 fn print(&mut self, s: String);
269}
270
271impl<T> Printer for T
272where
273 T: FnMut(String),
274{
275 fn print(&mut self, s: String) {
276 (*self)(s)
277 }
278}
279
280struct PrintlnPrinter;
281impl Printer for PrintlnPrinter {
282 fn print(&mut self, s: String) {
283 print!("{}", s);
284 }
285}
286
287type ClassMap = RefCell<HashMap<TypeId, Rc<RefCell<Box<dyn Any>>>>>;
288
289#[derive(Debug)]
290pub struct VM {
291 pub vm: *mut WrenVM,
292 classes_v2: ClassMap,
293 error_recv: Receiver<WrenError>,
294}
295
296pub struct UserData {
298 error_channel: Sender<WrenError>,
299 printer: Box<dyn Printer>,
300 pub vm: Weak<RefCell<VM>>, library: Option<ModuleLibrary>,
302 loader: Box<dyn ModuleScriptLoader>,
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
307pub enum SlotType {
308 Num,
309 Bool,
310 List,
311 Map,
312 Null,
313 String,
314 Foreign,
315 Unknown,
316}
317
318pub type SlotId = usize;
319
320#[derive(Debug, Clone)]
322pub enum FunctionSignature {
323 Function { name: String, arity: usize },
324 Getter(String),
325 Setter(String),
326}
327
328impl FunctionSignature {
329 pub fn new_function<N: Into<String>>(name: N, arity: usize) -> FunctionSignature {
330 FunctionSignature::Function {
331 name: name.into(),
332 arity,
333 }
334 }
335
336 pub fn new_getter<N: Into<String>>(name: N) -> FunctionSignature {
337 FunctionSignature::Getter(name.into())
338 }
339
340 pub fn new_setter<N: Into<String>>(name: N) -> FunctionSignature {
341 FunctionSignature::Setter(name.into())
342 }
343
344 fn as_wren_string(&self) -> String {
345 match self {
346 FunctionSignature::Function { name, arity } => {
347 format!("{}({})", name, vec!["_".to_string(); *arity].join(","))
348 }
349 FunctionSignature::Getter(name) => name.clone(),
350 FunctionSignature::Setter(name) => format!("{}=(_)", name),
351 }
352 }
353
354 pub fn arity(&self) -> usize {
356 match self {
357 FunctionSignature::Function { arity, .. } => *arity,
358 FunctionSignature::Getter(_) => 0,
359 FunctionSignature::Setter(_) => 1,
360 }
361 }
362}
363
364#[derive(Debug, Clone)]
366pub struct VMWrapper(Evm);
367
368impl VMWrapper {
369 pub fn call(&self, signature: FunctionSignature) -> Result<(), VMError> {
371 let handle = self.make_call_handle(signature);
372 self.call_handle(&handle)
373 }
374
375 pub fn call_handle(&self, handle: &FunctionHandle) -> Result<(), VMError> {
377 let vm = self.0.borrow();
378 match unsafe { wren_sys::wrenCall(vm.vm, handle.0.handle) } {
379 wren_sys::WrenInterpretResult_WREN_RESULT_SUCCESS => Ok(()),
380 wren_sys::WrenInterpretResult_WREN_RESULT_COMPILE_ERROR => {
381 unreachable!("wrenCall doesn't compile anything")
382 }
383 wren_sys::WrenInterpretResult_WREN_RESULT_RUNTIME_ERROR => {
384 let mut error = "".to_string();
385 let mut frames = vec![];
386 while let Ok(err) = vm.error_recv.try_recv() {
387 match err {
388 WrenError::Runtime(msg) => {
389 error = msg;
390 }
391 WrenError::StackTrace(module, line, msg) => {
392 frames.push(VMStackFrameError {
393 module,
394 line,
395 function: msg,
396 });
397 }
398 _ => unreachable!(),
399 }
400 }
401 Err(VMError::Runtime { error, frames })
402 }
403 _ => unreachable!(),
404 }
405 }
406
407 pub fn interpret<M: AsRef<str>, C: AsRef<str>>(
409 &self, module: M, code: C,
410 ) -> Result<(), VMError> {
411 let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
412 let code = ffi::CString::new(code.as_ref()).expect("code conversion failed");
413 let vm = self.0.borrow();
414 match unsafe { wren_sys::wrenInterpret(vm.vm, module.as_ptr(), code.as_ptr()) } {
415 wren_sys::WrenInterpretResult_WREN_RESULT_SUCCESS => Ok(()),
416 wren_sys::WrenInterpretResult_WREN_RESULT_COMPILE_ERROR => {
417 match vm.error_recv.try_recv() {
418 Ok(WrenError::Compile(module, line, msg)) => Err(VMError::Compile {
419 module,
420 line,
421 error: msg,
422 }),
423 _ => unreachable!(),
424 }
425 }
426 wren_sys::WrenInterpretResult_WREN_RESULT_RUNTIME_ERROR => {
427 let mut error = "".to_string();
428 let mut frames = vec![];
429 while let Ok(err) = vm.error_recv.try_recv() {
430 match err {
431 WrenError::Runtime(msg) => {
432 error = msg;
433 }
434 WrenError::StackTrace(module, line, msg) => {
435 frames.push(VMStackFrameError {
436 module,
437 line,
438 function: msg,
439 });
440 }
441 _ => unreachable!(),
442 }
443 }
444 Err(VMError::Runtime { error, frames })
445 }
446 _ => unreachable!(),
447 }
448 }
449
450 pub fn execute<T, F>(&self, f: F) -> T
452 where
453 F: FnOnce(&VM) -> T,
454 {
455 f(&self.0.borrow())
456 }
457
458 pub fn get_slot_handle(&self, slot: SlotId) -> Rc<Handle> {
460 Rc::new(Handle {
461 handle: unsafe { wren_sys::wrenGetSlotHandle(self.0.borrow().vm, slot as raw::c_int) },
462 wvm: self.0.borrow().vm,
463 vm: marker::PhantomData,
464 })
465 }
466
467 pub fn set_slot_handle(&self, slot: SlotId, handle: &Handle) {
469 unsafe {
470 wren_sys::wrenSetSlotHandle(self.0.borrow().vm, slot as raw::c_int, handle.handle)
471 }
472 }
473
474 pub fn make_call_handle(&self, signature: FunctionSignature) -> Rc<FunctionHandle> {
476 VM::make_call_handle(self.0.borrow().vm, signature)
477 }
478
479 pub fn collect_garbage(&self) {
481 unsafe { wren_sys::wrenCollectGarbage(self.0.borrow().vm) }
482 }
483}
484
485pub struct VMConfig {
487 printer: Box<dyn Printer>,
488 script_loader: Box<dyn ModuleScriptLoader>,
489 library: Option<ModuleLibrary>,
490 initial_heap_size: usize,
491 min_heap_size: usize,
492 heap_growth_percent: usize,
493
494 enable_relative_import: bool,
496}
497
498impl Default for VMConfig {
499 fn default() -> Self {
500 Self::new()
501 }
502}
503
504impl VMConfig {
505 pub fn new() -> VMConfig {
506 VMConfig {
507 printer: Box::new(PrintlnPrinter),
508 script_loader: Box::new(NullLoader),
509 library: None,
510 initial_heap_size: 1024 * 1024 * 10,
511 min_heap_size: 1024 * 1024,
512 heap_growth_percent: 50,
513 enable_relative_import: false,
514 }
515 }
516
517 pub fn printer<P: 'static + Printer>(mut self, p: P) -> Self {
518 self.printer = Box::new(p);
519 self
520 }
521
522 pub fn script_loader<L: 'static + ModuleScriptLoader>(mut self, l: L) -> Self {
523 self.script_loader = Box::new(l);
524 self
525 }
526
527 pub fn library(mut self, l: &ModuleLibrary) -> Self {
528 self.library = Some(l.clone());
529 self
530 }
531
532 pub fn no_library(mut self) -> Self {
533 self.library = None;
534 self
535 }
536
537 pub fn initial_heap_size(mut self, ihs: usize) -> Self {
538 self.initial_heap_size = ihs;
539 self
540 }
541
542 pub fn min_heap_size(mut self, mhs: usize) -> Self {
543 self.min_heap_size = mhs;
544 self
545 }
546
547 pub fn heap_growth_percent(mut self, hgp: usize) -> Self {
548 self.heap_growth_percent = hgp;
549 self
550 }
551
552 pub fn enable_relative_import(mut self, eri: bool) -> Self {
553 self.enable_relative_import = eri;
554 self
555 }
556
557 pub fn build(self) -> VMWrapper {
558 let (etx, erx) = channel();
559
560 let wvm = Rc::new(RefCell::new(VM {
562 vm: std::ptr::null_mut(),
563 classes_v2: RefCell::new(HashMap::new()),
564 error_recv: erx,
565 }));
566
567 let vm_config = Box::into_raw(Box::new(UserData {
568 error_channel: etx,
569 printer: self.printer,
570 vm: Rc::downgrade(&wvm),
571 loader: self.script_loader,
572 library: self.library,
573 }));
574
575 let mut config = unsafe {
577 let mut uconfig = mem::MaybeUninit::<WrenConfiguration>::zeroed();
578 wren_sys::wrenInitConfiguration(uconfig.as_mut_ptr());
579 let mut config = uconfig.assume_init();
580 config.errorFn = Some(runtime::wren_error);
581 config.writeFn = Some(runtime::wren_print);
582 config.reallocateFn = Some(runtime::wren_realloc);
583 config.bindForeignMethodFn = Some(runtime::wren_bind_foreign_method);
584 config.bindForeignClassFn = Some(runtime::wren_bind_foreign_class);
585 config.loadModuleFn = Some(runtime::wren_load_module);
586 config.resolveModuleFn = if self.enable_relative_import {
587 Some(runtime::wren_canonicalize)
588 } else {
589 None
590 };
591 config.initialHeapSize = self.initial_heap_size;
592 config.minHeapSize = self.min_heap_size;
593 config.heapGrowthPercent = self.heap_growth_percent as raw::c_int;
594 config.userData = vm_config as *mut ffi::c_void;
595 config
596 };
597
598 let vm = unsafe { wren_sys::wrenNewVM(&mut config) };
599 wvm.borrow_mut().vm = vm;
600 VMWrapper(wvm)
601 }
602}
603
604#[derive(Debug, Clone, Copy, PartialEq, Eq)]
605pub enum ForeignSendError {
607 NoForeignClass,
609 NoWrenClass,
611 NoMemory,
613 ClassMismatch,
615}
616
617impl std::fmt::Display for ForeignSendError {
618 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
619 match self {
620 ForeignSendError::NoForeignClass => write!(fmt, "no foreign class"),
621 ForeignSendError::NoWrenClass => write!(fmt, "no Wren class"),
622 ForeignSendError::NoMemory => write!(fmt, "unable to allocate memory"),
623 ForeignSendError::ClassMismatch => write!(fmt, "class mismatch"),
624 }
625 }
626}
627
628impl std::error::Error for ForeignSendError {}
629
630impl VM {
631 pub fn ensure_slots(&self, count: usize) {
633 unsafe { wren_sys::wrenEnsureSlots(self.vm, count as raw::c_int) }
634 }
635
636 pub fn get_slot_count(&self) -> usize {
637 unsafe { wren_sys::wrenGetSlotCount(self.vm) as usize }
638 }
639
640 pub fn set_slot_bool(&self, slot: SlotId, val: bool) {
641 self.ensure_slots(slot + 1);
642 unsafe { wren_sys::wrenSetSlotBool(self.vm, slot as raw::c_int, val) }
643 }
644
645 pub fn set_slot_double(&self, slot: SlotId, val: f64) {
646 self.ensure_slots(slot + 1);
647 unsafe { wren_sys::wrenSetSlotDouble(self.vm, slot as raw::c_int, val) }
648 }
649
650 pub fn set_slot_null(&self, slot: SlotId) {
651 self.ensure_slots(slot + 1);
652 unsafe { wren_sys::wrenSetSlotNull(self.vm, slot as raw::c_int) }
653 }
654
655 pub fn set_slot_bytes(&self, slot: SlotId, bytes: &[u8]) {
656 self.ensure_slots(slot + 1);
657 unsafe {
658 wren_sys::wrenSetSlotBytes(
659 self.vm,
660 slot as raw::c_int,
661 bytes as *const _ as *const raw::c_char,
662 bytes.len(),
663 );
664 }
665 }
666
667 pub fn set_slot_string<S: AsRef<str>>(&self, slot: SlotId, string: S) {
668 self.ensure_slots(slot + 1);
669 let string = string.as_ref();
670 unsafe {
671 wren_sys::wrenSetSlotBytes(
672 self.vm,
673 slot as raw::c_int,
674 string.as_ptr() as *const _,
675 string.len(),
676 );
677 }
678 }
679
680 pub fn get_slot_bool(&self, slot: SlotId) -> Option<bool> {
681 self.ensure_slots(slot + 1);
682 if self.get_slot_type(slot) != SlotType::Bool {
683 None
684 } else {
685 unsafe { Some(wren_sys::wrenGetSlotBool(self.vm, slot as raw::c_int)) }
686 }
687 }
688
689 pub fn get_slot_double(&self, slot: SlotId) -> Option<f64> {
690 self.ensure_slots(slot + 1);
691 if self.get_slot_type(slot) != SlotType::Num {
692 None
693 } else {
694 unsafe { Some(wren_sys::wrenGetSlotDouble(self.vm, slot as raw::c_int)) }
695 }
696 }
697
698 pub fn get_slot_bytes(&self, slot: SlotId) -> Option<Vec<u8>> {
699 self.ensure_slots(slot + 1);
700 if self.get_slot_type(slot) != SlotType::String {
701 None
702 } else {
703 let mut length = 0 as raw::c_int;
704 let ptr = unsafe {
705 wren_sys::wrenGetSlotBytes(self.vm, slot as raw::c_int, &mut length as *mut _)
706 };
707 let mut bytes = vec![];
708
709 for offset in 0..length {
711 unsafe { bytes.push(*ptr.offset(offset as isize) as u8) }
712 }
713
714 Some(bytes)
715 }
716 }
717
718 pub fn get_slot_string(&self, slot: SlotId) -> Option<String> {
719 self.ensure_slots(slot + 1);
720 if self.get_slot_type(slot) != SlotType::String {
721 None
722 } else {
723 let ptr = unsafe { wren_sys::wrenGetSlotString(self.vm, slot as raw::c_int) };
724
725 let cstr = unsafe { ffi::CStr::from_ptr(ptr) };
726
727 Some(cstr.to_string_lossy().to_string())
728 }
729 }
730
731 pub fn get_slot_type(&self, slot: SlotId) -> SlotType {
732 self.ensure_slots(slot + 1);
733 match unsafe { wren_sys::wrenGetSlotType(self.vm, slot as raw::c_int) } {
734 wren_sys::WrenType_WREN_TYPE_NUM => SlotType::Num,
735 wren_sys::WrenType_WREN_TYPE_BOOL => SlotType::Bool,
736 wren_sys::WrenType_WREN_TYPE_LIST => SlotType::List,
737 wren_sys::WrenType_WREN_TYPE_MAP => SlotType::Map,
738 wren_sys::WrenType_WREN_TYPE_NULL => SlotType::Null,
739 wren_sys::WrenType_WREN_TYPE_STRING => SlotType::String,
740 wren_sys::WrenType_WREN_TYPE_FOREIGN => SlotType::Foreign,
741 wren_sys::WrenType_WREN_TYPE_UNKNOWN => SlotType::Unknown,
742 _ => unreachable!(),
743 }
744 }
745
746 pub fn get_variable<M: AsRef<str>, N: AsRef<str>>(
750 &self, module: M, name: N, slot: SlotId,
751 ) -> bool {
752 self.ensure_slots(slot + 1);
753 if !self.has_variable(&module, &name) {
754 return false;
755 }
756 let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
757 let name = ffi::CString::new(name.as_ref()).expect("variable name conversion failed");
758 unsafe {
759 wren_sys::wrenGetVariable(self.vm, module.as_ptr(), name.as_ptr(), slot as raw::c_int)
760 }
761 true
762 }
763
764 pub fn has_variable<M: AsRef<str>, N: AsRef<str>>(&self, module: M, name: N) -> bool {
765 if !self.has_module(&module) {
766 return false;
767 }
768 let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
769 let name = ffi::CString::new(name.as_ref()).expect("variable name conversion failed");
770 unsafe { wren_sys::wrenHasVariable(self.vm, module.as_ptr(), name.as_ptr()) }
771 }
772
773 pub fn has_module<M: AsRef<str>>(&self, module: M) -> bool {
774 let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
775 unsafe { wren_sys::wrenHasModule(self.vm, module.as_ptr()) }
776 }
777
778 pub fn set_slot_new_list(&self, slot: SlotId) {
779 self.ensure_slots(slot + 1);
780 unsafe { wren_sys::wrenSetSlotNewList(self.vm, slot as raw::c_int) }
781 }
782
783 pub fn get_list_count(&self, slot: SlotId) -> Option<usize> {
784 self.ensure_slots(slot + 1);
785 if self.get_slot_type(slot) == SlotType::List {
786 Some(unsafe { wren_sys::wrenGetListCount(self.vm, slot as raw::c_int) as usize })
787 } else {
788 None
789 }
790 }
791
792 pub fn insert_in_list(&self, list_slot: SlotId, index: i32, element_slot: SlotId) {
793 self.ensure_slots(element_slot + 1);
794 self.ensure_slots(list_slot + 1);
795 unsafe {
796 wren_sys::wrenInsertInList(
797 self.vm,
798 list_slot as raw::c_int,
799 index as raw::c_int,
800 element_slot as raw::c_int,
801 )
802 }
803 }
804
805 pub fn get_list_element(&self, list_slot: SlotId, index: i32, element_slot: SlotId) {
806 self.ensure_slots(element_slot + 1);
807 self.ensure_slots(list_slot + 1);
808 unsafe {
809 wren_sys::wrenGetListElement(
810 self.vm,
811 list_slot as raw::c_int,
812 index as raw::c_int,
813 element_slot as raw::c_int,
814 )
815 }
816 }
817
818 pub fn set_list_element(&self, list_slot: SlotId, index: i32, element_slot: SlotId) {
819 self.ensure_slots(element_slot + 1);
820 self.ensure_slots(list_slot + 1);
821 unsafe {
822 wren_sys::wrenSetListElement(
823 self.vm,
824 list_slot as raw::c_int,
825 index as raw::c_int,
826 element_slot as raw::c_int,
827 )
828 }
829 }
830
831 pub fn set_slot_new_map(&self, slot: SlotId) {
832 self.ensure_slots(slot + 1);
833 unsafe { wren_sys::wrenSetSlotNewMap(self.vm, slot as raw::c_int) }
834 }
835
836 pub fn get_map_count(&self, slot: SlotId) -> Option<usize> {
837 self.ensure_slots(slot + 1);
838 if self.get_slot_type(slot) == SlotType::Map {
839 Some(unsafe { wren_sys::wrenGetMapCount(self.vm, slot as raw::c_int) as usize })
840 } else {
841 None
842 }
843 }
844
845 pub fn get_map_contains_key(&self, map_slot: SlotId, key_slot: SlotId) -> Option<bool> {
846 self.ensure_slots(map_slot + 1);
847 self.ensure_slots(key_slot + 1);
848 if self.get_slot_type(map_slot) == SlotType::Map {
849 Some(unsafe {
850 wren_sys::wrenGetMapContainsKey(
851 self.vm,
852 map_slot as raw::c_int,
853 key_slot as raw::c_int,
854 )
855 })
856 } else {
857 None
858 }
859 }
860
861 pub fn get_map_value(&self, map_slot: SlotId, key_slot: SlotId, value_slot: SlotId) {
862 self.ensure_slots(map_slot + 1);
863 self.ensure_slots(key_slot + 1);
864 self.ensure_slots(value_slot + 1);
865 unsafe {
866 wren_sys::wrenGetMapValue(
867 self.vm,
868 map_slot as raw::c_int,
869 key_slot as raw::c_int,
870 value_slot as raw::c_int,
871 )
872 }
873 }
874
875 pub fn set_map_value(&self, map_slot: SlotId, key_slot: SlotId, value_slot: SlotId) {
876 self.ensure_slots(map_slot + 1);
877 self.ensure_slots(key_slot + 1);
878 self.ensure_slots(value_slot + 1);
879 unsafe {
880 wren_sys::wrenSetMapValue(
881 self.vm,
882 map_slot as raw::c_int,
883 key_slot as raw::c_int,
884 value_slot as raw::c_int,
885 )
886 }
887 }
888
889 pub fn remove_map_value(&self, map_slot: SlotId, key_slot: SlotId, removed_value_slot: SlotId) {
890 self.ensure_slots(map_slot + 1);
891 self.ensure_slots(key_slot + 1);
892 self.ensure_slots(removed_value_slot + 1);
893 unsafe {
894 wren_sys::wrenRemoveMapValue(
895 self.vm,
896 map_slot as raw::c_int,
897 key_slot as raw::c_int,
898 removed_value_slot as raw::c_int,
899 )
900 }
901 }
902
903 pub fn get_slot_foreign<T: 'static + ClassObject>(&self, slot: SlotId) -> Option<&T> {
904 self.ensure_slots(slot + 1);
905 self.get_slot_foreign_mut(slot).map(|mr| &*mr)
906 }
907
908 pub fn get_slot_foreign_mut<T: 'static + ClassObject>(&self, slot: SlotId) -> Option<&mut T> {
909 self.ensure_slots(slot + 1);
910 if self.get_slot_type(slot) != SlotType::Foreign {
911 return None;
912 }
913 unsafe {
914 let ptr = wren_sys::wrenGetSlotForeign(self.vm, slot as raw::c_int);
915 if !ptr.is_null() {
916 let fo = std::ptr::read_unaligned(ptr as *mut ForeignObject<T>);
917 let ret = if fo.type_id == any::TypeId::of::<T>() {
918 fo.object.as_mut()
920 } else {
921 None
923 };
924 std::ptr::write_unaligned(ptr as *mut ForeignObject<T>, fo);
925 ret
926 } else {
927 None
928 }
929 }
930 }
931
932 pub fn use_class<T: ForeignItem + 'static, F, O>(&self, f: F) -> O
934 where
935 F: FnOnce(&VM, Option<&T::Class>) -> O,
936 {
937 let (update, class) = match self.classes_v2.borrow_mut().get_mut(&TypeId::of::<T>()) {
938 Some(cls) => (false, cls.clone()),
939 None => {
940 use crate::foreign_v2::V2ClassAllocator;
941
942 let class = Rc::new(RefCell::new(Box::new(T::Class::allocate()) as Box<dyn Any>));
944 (true, class)
945 }
946 };
947
948 let ret = f(self, class.borrow().downcast_ref());
949
950 if update {
951 self.classes_v2
952 .borrow_mut()
953 .insert(TypeId::of::<T>(), class);
954 }
955
956 ret
957 }
958
959 pub fn use_class_mut<T: ForeignItem + 'static, F, O>(&self, f: F) -> O
961 where
962 F: FnOnce(&VM, Option<&mut T::Class>) -> O,
963 {
964 let (update, class) = match self.classes_v2.borrow_mut().get_mut(&TypeId::of::<T>()) {
965 Some(cls) => (false, cls.clone()),
966 None => {
967 use crate::foreign_v2::V2ClassAllocator;
968
969 let class = Rc::new(RefCell::new(Box::new(T::Class::allocate()) as Box<dyn Any>));
971 (true, class)
972 }
973 };
974
975 let ret = f(self, class.borrow_mut().downcast_mut());
976
977 if update {
978 self.classes_v2
979 .borrow_mut()
980 .insert(TypeId::of::<T>(), class);
981 }
982
983 ret
984 }
985
986 pub fn set_slot_new_foreign<M: AsRef<str>, C: AsRef<str>, T: 'static + ClassObject>(
991 &self, module: M, class: C, object: T, slot: SlotId,
992 ) -> Result<&mut T, ForeignSendError> {
993 self.set_slot_new_foreign_scratch(module, class, object, slot, 0)
994 }
995
996 pub fn set_slot_new_foreign_scratch<M: AsRef<str>, C: AsRef<str>, T: 'static + ClassObject>(
1001 &self, module: M, class: C, object: T, slot: SlotId, scratch: SlotId,
1002 ) -> Result<&mut T, ForeignSendError> {
1003 self.ensure_slots(slot.max(scratch) + 1);
1004 let conf = unsafe {
1005 std::ptr::read_unaligned(wren_sys::wrenGetUserData(self.vm) as *mut UserData)
1006 };
1007
1008 self.ensure_slots(slot.max(scratch) + 1);
1010 let ret = match conf
1012 .library
1013 .as_ref()
1014 .and_then(|lib| lib.get_foreign_class(module.as_ref(), class.as_ref()))
1015 {
1016 None => Err(ForeignSendError::NoForeignClass), Some(runtime_class) => {
1018 if runtime_class.type_id == any::TypeId::of::<T>() {
1019 let new_obj = ForeignObject {
1024 object: Box::into_raw(Box::new(object)),
1025 type_id: any::TypeId::of::<T>(),
1026 };
1027
1028 self.get_variable(module, class, scratch);
1030
1031 match self.get_slot_type(scratch) {
1033 SlotType::Null => Err(ForeignSendError::NoWrenClass), SlotType::Unknown => unsafe {
1035 let wptr = wren_sys::wrenSetSlotNewForeign(
1038 self.vm,
1039 slot as raw::c_int,
1040 scratch as raw::c_int,
1041 mem::size_of::<ForeignObject<T>>(),
1042 );
1043
1044 if !wptr.is_null() {
1045 std::ptr::write_unaligned(wptr as *mut _, new_obj);
1047 }
1048
1049 match (wptr as *mut ForeignObject<T>).as_mut() {
1051 Some(ptr) => Ok(ptr.object.as_mut().unwrap()),
1052 None => Err(ForeignSendError::NoMemory),
1053 }
1054 },
1055 _ => Err(ForeignSendError::NoWrenClass),
1056 }
1057 } else {
1058 Err(ForeignSendError::ClassMismatch)
1060 }
1061 }
1062 };
1063
1064 unsafe {
1065 std::ptr::write_unaligned(wrenGetUserData(self.vm) as *mut UserData, conf);
1066 }
1067 ret
1068 }
1069
1070 fn make_call_handle<'b>(
1071 vm: *mut WrenVM, signature: FunctionSignature,
1072 ) -> Rc<FunctionHandle<'b>> {
1073 let signature =
1074 ffi::CString::new(signature.as_wren_string()).expect("signature conversion failed");
1075 Rc::new(FunctionHandle(Handle {
1076 handle: unsafe { wren_sys::wrenMakeCallHandle(vm, signature.as_ptr()) },
1077 wvm: vm,
1078 vm: marker::PhantomData,
1079 }))
1080 }
1081
1082 pub fn abort_fiber(&self, slot: SlotId) {
1083 unsafe { wren_sys::wrenAbortFiber(self.vm, slot as raw::c_int) }
1084 }
1085
1086 pub fn get_version_number(&self) -> i32 {
1087 unsafe { wren_sys::wrenGetVersionNumber() }
1088 }
1089}
1090
1091impl Drop for VM {
1092 fn drop(&mut self) {
1093 unsafe {
1094 let conf = wren_sys::wrenGetUserData(self.vm);
1095 let _: Box<UserData> = Box::from_raw(conf as *mut _); wren_sys::wrenFreeVM(self.vm);
1097 }
1098 }
1099}