pub extern crate wren_sys;
use foreign_v2::ForeignItem;
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
use std::sync::mpsc::{channel, Receiver, Sender};
use wren_sys::{wrenGetUserData, WrenConfiguration, WrenHandle, WrenVM};
mod module_loader;
pub use module_loader::{BasicFileLoader, NullLoader};
pub mod foreign_v1;
pub mod foreign_v2;
use std::{any, ffi, marker, mem, os::raw};
mod runtime;
#[cfg(test)]
mod tests;
#[cfg(feature = "derive")]
pub use ruwren_macros::*;
#[derive(Debug)]
pub enum WrenError {
Compile(String, i32, String),
Runtime(String),
StackTrace(String, i32, String),
}
#[derive(Debug, Clone)]
pub enum VMError {
Compile {
module: String,
line: i32,
error: String,
},
Runtime {
error: String,
frames: Vec<VMStackFrameError>,
},
}
#[derive(Debug, Clone)]
pub struct VMStackFrameError {
pub module: String,
pub line: i32,
pub function: String,
}
#[cfg(not(target_arch = "wasm32"))]
pub fn handle_panic<F, O>(func: F) -> Result<O, Box<dyn Any + Send>>
where
F: FnOnce() -> O + std::panic::UnwindSafe,
{
std::panic::catch_unwind(func)
}
#[cfg(target_arch = "wasm32")]
pub fn handle_panic<F, O: 'static>(func: F) -> Result<O, Box<dyn Any + Send>>
where
F: FnOnce() -> O + std::panic::UnwindSafe,
{
match std::panic::catch_unwind(func) {
Ok(o) => Ok(o),
_ => unreachable!("non-unwinding platforms (like WASM) can't catch unwinds, so don't panic unless absolutely necessary"),
}
}
impl std::fmt::Display for VMError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
VMError::Compile {
module,
line,
error,
} => write!(fmt, "Compile Error ({}:{}): {}", module, line, error),
VMError::Runtime { error, frames } => {
writeln!(fmt, "Runtime Error: {}", error)?;
for frame in frames {
if frame.function.is_empty() {
writeln!(fmt, "\tin {}:{}: <constructor>", frame.module, frame.line)?;
} else {
writeln!(
fmt,
"\tin {}:{}: {}",
frame.module, frame.line, frame.function
)?;
}
}
Ok(())
}
}
}
}
impl std::error::Error for VMError {}
#[derive(Debug, PartialEq, Eq)]
pub struct Handle<'a> {
handle: *mut WrenHandle,
wvm: *mut WrenVM,
vm: marker::PhantomData<&'a VM>,
}
impl<'a> Drop for Handle<'a> {
fn drop(&mut self) {
unsafe {
wren_sys::wrenReleaseHandle(self.wvm, self.handle);
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct FunctionHandle<'a>(Handle<'a>);
#[derive(Debug, Clone, Default)]
pub struct ModuleLibrary {
modules: HashMap<String, Module>,
}
impl ModuleLibrary {
pub fn new() -> ModuleLibrary {
ModuleLibrary {
modules: HashMap::new(),
}
}
pub fn module<N: Into<String>>(&mut self, name: N, modl: Module) {
let module_name = name.into();
if let Some(module) = self.modules.get_mut(&module_name) {
module.classes.extend(modl.classes);
} else {
self.modules.insert(module_name, modl);
}
}
fn get_foreign_class<M: AsRef<str>, C: AsRef<str>>(
&self, module: M, class: C,
) -> Option<&RuntimeClass> {
self.modules
.get(module.as_ref())
.and_then(|md| md.classes.get(class.as_ref()))
}
}
#[derive(Debug, Clone)]
struct RuntimeClass {
construct: extern "C" fn(*mut WrenVM),
destruct: extern "C" fn(*mut ffi::c_void),
methods: ClassObjectPointers,
type_id: any::TypeId,
}
#[derive(Debug, Clone, Default)]
pub struct Module {
classes: HashMap<String, RuntimeClass>,
}
#[derive(Debug, Clone)]
pub struct ClassObjectPointers {
pub function_pointers: Vec<MethodPointer>,
}
#[derive(Debug, Clone)]
pub struct MethodPointer {
pub is_static: bool,
pub signature: FunctionSignature,
pub pointer: unsafe extern "C" fn(*mut WrenVM),
}
impl Module {
pub fn new() -> Module {
Module {
classes: HashMap::new(),
}
}
pub fn class<C: 'static + ClassObject, S: Into<String>>(&mut self, name: S) -> &mut Self {
let cp = C::generate_pointers();
let init = C::initialize_pointer();
let deinit = C::finalize_pointer();
self.classes.insert(
name.into(),
RuntimeClass {
construct: init,
destruct: deinit,
methods: cp,
type_id: any::TypeId::of::<C>(),
},
);
self
}
}
pub trait Class {
fn initialize(_: &VM) -> Self
where
Self: Sized;
}
pub trait ClassObject: Class {
fn initialize_pointer() -> extern "C" fn(*mut WrenVM)
where
Self: Sized;
fn finalize_pointer() -> extern "C" fn(*mut ffi::c_void)
where
Self: Sized;
fn generate_pointers() -> ClassObjectPointers
where
Self: Sized;
}
#[derive(Debug, Copy, Clone)]
pub struct ForeignObject<T> {
pub object: *mut T,
pub type_id: any::TypeId,
}
pub fn type_name_of<T>(_: &T) -> &'static str {
any::type_name::<T>()
}
pub trait ModuleScriptLoader {
fn load_script(&mut self, name: String) -> Option<String>;
}
impl<T> ModuleScriptLoader for T
where
T: FnMut(String) -> Option<String>,
{
fn load_script(&mut self, name: String) -> Option<String> {
(*self)(name)
}
}
type Evm = Rc<RefCell<VM>>;
pub trait Printer {
fn print(&mut self, s: String);
}
impl<T> Printer for T
where
T: FnMut(String),
{
fn print(&mut self, s: String) {
(*self)(s)
}
}
struct PrintlnPrinter;
impl Printer for PrintlnPrinter {
fn print(&mut self, s: String) {
print!("{}", s);
}
}
type ClassMap = RefCell<HashMap<TypeId, Rc<RefCell<Box<dyn Any>>>>>;
#[derive(Debug)]
pub struct VM {
pub vm: *mut WrenVM,
classes_v2: ClassMap,
error_recv: Receiver<WrenError>,
}
pub struct UserData {
error_channel: Sender<WrenError>,
printer: Box<dyn Printer>,
pub vm: Weak<RefCell<VM>>, library: Option<ModuleLibrary>,
loader: Box<dyn ModuleScriptLoader>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SlotType {
Num,
Bool,
List,
Map,
Null,
String,
Foreign,
Unknown,
}
pub type SlotId = usize;
#[derive(Debug, Clone)]
pub enum FunctionSignature {
Function { name: String, arity: usize },
Getter(String),
Setter(String),
}
impl FunctionSignature {
pub fn new_function<N: Into<String>>(name: N, arity: usize) -> FunctionSignature {
FunctionSignature::Function {
name: name.into(),
arity,
}
}
pub fn new_getter<N: Into<String>>(name: N) -> FunctionSignature {
FunctionSignature::Getter(name.into())
}
pub fn new_setter<N: Into<String>>(name: N) -> FunctionSignature {
FunctionSignature::Setter(name.into())
}
fn as_wren_string(&self) -> String {
match self {
FunctionSignature::Function { name, arity } => {
format!("{}({})", name, vec!["_".to_string(); *arity].join(","))
}
FunctionSignature::Getter(name) => name.clone(),
FunctionSignature::Setter(name) => format!("{}=(_)", name),
}
}
pub fn arity(&self) -> usize {
match self {
FunctionSignature::Function { arity, .. } => *arity,
FunctionSignature::Getter(_) => 0,
FunctionSignature::Setter(_) => 1,
}
}
}
#[derive(Debug, Clone)]
pub struct VMWrapper(Evm);
impl VMWrapper {
pub fn call(&self, signature: FunctionSignature) -> Result<(), VMError> {
let handle = self.make_call_handle(signature);
self.call_handle(&handle)
}
pub fn call_handle(&self, handle: &FunctionHandle) -> Result<(), VMError> {
let vm = self.0.borrow();
match unsafe { wren_sys::wrenCall(vm.vm, handle.0.handle) } {
wren_sys::WrenInterpretResult_WREN_RESULT_SUCCESS => Ok(()),
wren_sys::WrenInterpretResult_WREN_RESULT_COMPILE_ERROR => {
unreachable!("wrenCall doesn't compile anything")
}
wren_sys::WrenInterpretResult_WREN_RESULT_RUNTIME_ERROR => {
let mut error = "".to_string();
let mut frames = vec![];
while let Ok(err) = vm.error_recv.try_recv() {
match err {
WrenError::Runtime(msg) => {
error = msg;
}
WrenError::StackTrace(module, line, msg) => {
frames.push(VMStackFrameError {
module,
line,
function: msg,
});
}
_ => unreachable!(),
}
}
Err(VMError::Runtime { error, frames })
}
_ => unreachable!(),
}
}
pub fn interpret<M: AsRef<str>, C: AsRef<str>>(
&self, module: M, code: C,
) -> Result<(), VMError> {
let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
let code = ffi::CString::new(code.as_ref()).expect("code conversion failed");
let vm = self.0.borrow();
match unsafe { wren_sys::wrenInterpret(vm.vm, module.as_ptr(), code.as_ptr()) } {
wren_sys::WrenInterpretResult_WREN_RESULT_SUCCESS => Ok(()),
wren_sys::WrenInterpretResult_WREN_RESULT_COMPILE_ERROR => {
match vm.error_recv.try_recv() {
Ok(WrenError::Compile(module, line, msg)) => Err(VMError::Compile {
module,
line,
error: msg,
}),
_ => unreachable!(),
}
}
wren_sys::WrenInterpretResult_WREN_RESULT_RUNTIME_ERROR => {
let mut error = "".to_string();
let mut frames = vec![];
while let Ok(err) = vm.error_recv.try_recv() {
match err {
WrenError::Runtime(msg) => {
error = msg;
}
WrenError::StackTrace(module, line, msg) => {
frames.push(VMStackFrameError {
module,
line,
function: msg,
});
}
_ => unreachable!(),
}
}
Err(VMError::Runtime { error, frames })
}
_ => unreachable!(),
}
}
pub fn execute<T, F>(&self, f: F) -> T
where
F: FnOnce(&VM) -> T,
{
f(&self.0.borrow())
}
pub fn get_slot_handle(&self, slot: SlotId) -> Rc<Handle> {
Rc::new(Handle {
handle: unsafe { wren_sys::wrenGetSlotHandle(self.0.borrow().vm, slot as raw::c_int) },
wvm: self.0.borrow().vm,
vm: marker::PhantomData,
})
}
pub fn set_slot_handle(&self, slot: SlotId, handle: &Handle) {
unsafe {
wren_sys::wrenSetSlotHandle(self.0.borrow().vm, slot as raw::c_int, handle.handle)
}
}
pub fn make_call_handle(&self, signature: FunctionSignature) -> Rc<FunctionHandle> {
VM::make_call_handle(self.0.borrow().vm, signature)
}
pub fn collect_garbage(&self) {
unsafe { wren_sys::wrenCollectGarbage(self.0.borrow().vm) }
}
}
pub struct VMConfig {
printer: Box<dyn Printer>,
script_loader: Box<dyn ModuleScriptLoader>,
library: Option<ModuleLibrary>,
initial_heap_size: usize,
min_heap_size: usize,
heap_growth_percent: usize,
enable_relative_import: bool,
}
impl Default for VMConfig {
fn default() -> Self {
Self::new()
}
}
impl VMConfig {
pub fn new() -> VMConfig {
VMConfig {
printer: Box::new(PrintlnPrinter),
script_loader: Box::new(NullLoader),
library: None,
initial_heap_size: 1024 * 1024 * 10,
min_heap_size: 1024 * 1024,
heap_growth_percent: 50,
enable_relative_import: false,
}
}
pub fn printer<P: 'static + Printer>(mut self, p: P) -> Self {
self.printer = Box::new(p);
self
}
pub fn script_loader<L: 'static + ModuleScriptLoader>(mut self, l: L) -> Self {
self.script_loader = Box::new(l);
self
}
pub fn library(mut self, l: &ModuleLibrary) -> Self {
self.library = Some(l.clone());
self
}
pub fn no_library(mut self) -> Self {
self.library = None;
self
}
pub fn initial_heap_size(mut self, ihs: usize) -> Self {
self.initial_heap_size = ihs;
self
}
pub fn min_heap_size(mut self, mhs: usize) -> Self {
self.min_heap_size = mhs;
self
}
pub fn heap_growth_percent(mut self, hgp: usize) -> Self {
self.heap_growth_percent = hgp;
self
}
pub fn enable_relative_import(mut self, eri: bool) -> Self {
self.enable_relative_import = eri;
self
}
pub fn build(self) -> VMWrapper {
let (etx, erx) = channel();
let wvm = Rc::new(RefCell::new(VM {
vm: std::ptr::null_mut(),
classes_v2: RefCell::new(HashMap::new()),
error_recv: erx,
}));
let vm_config = Box::into_raw(Box::new(UserData {
error_channel: etx,
printer: self.printer,
vm: Rc::downgrade(&wvm),
loader: self.script_loader,
library: self.library,
}));
let mut config = unsafe {
let mut uconfig = mem::MaybeUninit::<WrenConfiguration>::zeroed();
wren_sys::wrenInitConfiguration(uconfig.as_mut_ptr());
let mut config = uconfig.assume_init();
config.errorFn = Some(runtime::wren_error);
config.writeFn = Some(runtime::wren_print);
config.reallocateFn = Some(runtime::wren_realloc);
config.bindForeignMethodFn = Some(runtime::wren_bind_foreign_method);
config.bindForeignClassFn = Some(runtime::wren_bind_foreign_class);
config.loadModuleFn = Some(runtime::wren_load_module);
config.resolveModuleFn = if self.enable_relative_import {
Some(runtime::wren_canonicalize)
} else {
None
};
config.initialHeapSize = self.initial_heap_size;
config.minHeapSize = self.min_heap_size;
config.heapGrowthPercent = self.heap_growth_percent as raw::c_int;
config.userData = vm_config as *mut ffi::c_void;
config
};
let vm = unsafe { wren_sys::wrenNewVM(&mut config) };
wvm.borrow_mut().vm = vm;
VMWrapper(wvm)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ForeignSendError {
NoForeignClass,
NoWrenClass,
NoMemory,
ClassMismatch,
}
impl std::fmt::Display for ForeignSendError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ForeignSendError::NoForeignClass => write!(fmt, "no foreign class"),
ForeignSendError::NoWrenClass => write!(fmt, "no Wren class"),
ForeignSendError::NoMemory => write!(fmt, "unable to allocate memory"),
ForeignSendError::ClassMismatch => write!(fmt, "class mismatch"),
}
}
}
impl std::error::Error for ForeignSendError {}
impl VM {
pub fn ensure_slots(&self, count: usize) {
unsafe { wren_sys::wrenEnsureSlots(self.vm, count as raw::c_int) }
}
pub fn get_slot_count(&self) -> usize {
unsafe { wren_sys::wrenGetSlotCount(self.vm) as usize }
}
pub fn set_slot_bool(&self, slot: SlotId, val: bool) {
self.ensure_slots(slot + 1);
unsafe { wren_sys::wrenSetSlotBool(self.vm, slot as raw::c_int, val) }
}
pub fn set_slot_double(&self, slot: SlotId, val: f64) {
self.ensure_slots(slot + 1);
unsafe { wren_sys::wrenSetSlotDouble(self.vm, slot as raw::c_int, val) }
}
pub fn set_slot_null(&self, slot: SlotId) {
self.ensure_slots(slot + 1);
unsafe { wren_sys::wrenSetSlotNull(self.vm, slot as raw::c_int) }
}
pub fn set_slot_bytes(&self, slot: SlotId, bytes: &[u8]) {
self.ensure_slots(slot + 1);
unsafe {
wren_sys::wrenSetSlotBytes(
self.vm,
slot as raw::c_int,
bytes as *const _ as *const raw::c_char,
bytes.len(),
);
}
}
pub fn set_slot_string<S: AsRef<str>>(&self, slot: SlotId, string: S) {
self.ensure_slots(slot + 1);
let string = string.as_ref();
unsafe {
wren_sys::wrenSetSlotBytes(
self.vm,
slot as raw::c_int,
string.as_ptr() as *const _,
string.len(),
);
}
}
pub fn get_slot_bool(&self, slot: SlotId) -> Option<bool> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) != SlotType::Bool {
None
} else {
unsafe { Some(wren_sys::wrenGetSlotBool(self.vm, slot as raw::c_int)) }
}
}
pub fn get_slot_double(&self, slot: SlotId) -> Option<f64> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) != SlotType::Num {
None
} else {
unsafe { Some(wren_sys::wrenGetSlotDouble(self.vm, slot as raw::c_int)) }
}
}
pub fn get_slot_bytes(&self, slot: SlotId) -> Option<Vec<u8>> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) != SlotType::String {
None
} else {
let mut length = 0 as raw::c_int;
let ptr = unsafe {
wren_sys::wrenGetSlotBytes(self.vm, slot as raw::c_int, &mut length as *mut _)
};
let mut bytes = vec![];
for offset in 0..length {
unsafe { bytes.push(*ptr.offset(offset as isize) as u8) }
}
Some(bytes)
}
}
pub fn get_slot_string(&self, slot: SlotId) -> Option<String> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) != SlotType::String {
None
} else {
let ptr = unsafe { wren_sys::wrenGetSlotString(self.vm, slot as raw::c_int) };
let cstr = unsafe { ffi::CStr::from_ptr(ptr) };
Some(cstr.to_string_lossy().to_string())
}
}
pub fn get_slot_type(&self, slot: SlotId) -> SlotType {
self.ensure_slots(slot + 1);
match unsafe { wren_sys::wrenGetSlotType(self.vm, slot as raw::c_int) } {
wren_sys::WrenType_WREN_TYPE_NUM => SlotType::Num,
wren_sys::WrenType_WREN_TYPE_BOOL => SlotType::Bool,
wren_sys::WrenType_WREN_TYPE_LIST => SlotType::List,
wren_sys::WrenType_WREN_TYPE_MAP => SlotType::Map,
wren_sys::WrenType_WREN_TYPE_NULL => SlotType::Null,
wren_sys::WrenType_WREN_TYPE_STRING => SlotType::String,
wren_sys::WrenType_WREN_TYPE_FOREIGN => SlotType::Foreign,
wren_sys::WrenType_WREN_TYPE_UNKNOWN => SlotType::Unknown,
_ => unreachable!(),
}
}
pub fn get_variable<M: AsRef<str>, N: AsRef<str>>(
&self, module: M, name: N, slot: SlotId,
) -> bool {
self.ensure_slots(slot + 1);
if !self.has_variable(&module, &name) {
return false;
}
let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
let name = ffi::CString::new(name.as_ref()).expect("variable name conversion failed");
unsafe {
wren_sys::wrenGetVariable(self.vm, module.as_ptr(), name.as_ptr(), slot as raw::c_int)
}
true
}
pub fn has_variable<M: AsRef<str>, N: AsRef<str>>(&self, module: M, name: N) -> bool {
if !self.has_module(&module) {
return false;
}
let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
let name = ffi::CString::new(name.as_ref()).expect("variable name conversion failed");
unsafe { wren_sys::wrenHasVariable(self.vm, module.as_ptr(), name.as_ptr()) }
}
pub fn has_module<M: AsRef<str>>(&self, module: M) -> bool {
let module = ffi::CString::new(module.as_ref()).expect("module name conversion failed");
unsafe { wren_sys::wrenHasModule(self.vm, module.as_ptr()) }
}
pub fn set_slot_new_list(&self, slot: SlotId) {
self.ensure_slots(slot + 1);
unsafe { wren_sys::wrenSetSlotNewList(self.vm, slot as raw::c_int) }
}
pub fn get_list_count(&self, slot: SlotId) -> Option<usize> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) == SlotType::List {
Some(unsafe { wren_sys::wrenGetListCount(self.vm, slot as raw::c_int) as usize })
} else {
None
}
}
pub fn insert_in_list(&self, list_slot: SlotId, index: i32, element_slot: SlotId) {
self.ensure_slots(element_slot + 1);
self.ensure_slots(list_slot + 1);
unsafe {
wren_sys::wrenInsertInList(
self.vm,
list_slot as raw::c_int,
index as raw::c_int,
element_slot as raw::c_int,
)
}
}
pub fn get_list_element(&self, list_slot: SlotId, index: i32, element_slot: SlotId) {
self.ensure_slots(element_slot + 1);
self.ensure_slots(list_slot + 1);
unsafe {
wren_sys::wrenGetListElement(
self.vm,
list_slot as raw::c_int,
index as raw::c_int,
element_slot as raw::c_int,
)
}
}
pub fn set_list_element(&self, list_slot: SlotId, index: i32, element_slot: SlotId) {
self.ensure_slots(element_slot + 1);
self.ensure_slots(list_slot + 1);
unsafe {
wren_sys::wrenSetListElement(
self.vm,
list_slot as raw::c_int,
index as raw::c_int,
element_slot as raw::c_int,
)
}
}
pub fn set_slot_new_map(&self, slot: SlotId) {
self.ensure_slots(slot + 1);
unsafe { wren_sys::wrenSetSlotNewMap(self.vm, slot as raw::c_int) }
}
pub fn get_map_count(&self, slot: SlotId) -> Option<usize> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) == SlotType::Map {
Some(unsafe { wren_sys::wrenGetMapCount(self.vm, slot as raw::c_int) as usize })
} else {
None
}
}
pub fn get_map_contains_key(&self, map_slot: SlotId, key_slot: SlotId) -> Option<bool> {
self.ensure_slots(map_slot + 1);
self.ensure_slots(key_slot + 1);
if self.get_slot_type(map_slot) == SlotType::Map {
Some(unsafe {
wren_sys::wrenGetMapContainsKey(
self.vm,
map_slot as raw::c_int,
key_slot as raw::c_int,
)
})
} else {
None
}
}
pub fn get_map_value(&self, map_slot: SlotId, key_slot: SlotId, value_slot: SlotId) {
self.ensure_slots(map_slot + 1);
self.ensure_slots(key_slot + 1);
self.ensure_slots(value_slot + 1);
unsafe {
wren_sys::wrenGetMapValue(
self.vm,
map_slot as raw::c_int,
key_slot as raw::c_int,
value_slot as raw::c_int,
)
}
}
pub fn set_map_value(&self, map_slot: SlotId, key_slot: SlotId, value_slot: SlotId) {
self.ensure_slots(map_slot + 1);
self.ensure_slots(key_slot + 1);
self.ensure_slots(value_slot + 1);
unsafe {
wren_sys::wrenSetMapValue(
self.vm,
map_slot as raw::c_int,
key_slot as raw::c_int,
value_slot as raw::c_int,
)
}
}
pub fn remove_map_value(&self, map_slot: SlotId, key_slot: SlotId, removed_value_slot: SlotId) {
self.ensure_slots(map_slot + 1);
self.ensure_slots(key_slot + 1);
self.ensure_slots(removed_value_slot + 1);
unsafe {
wren_sys::wrenRemoveMapValue(
self.vm,
map_slot as raw::c_int,
key_slot as raw::c_int,
removed_value_slot as raw::c_int,
)
}
}
pub fn get_slot_foreign<T: 'static + ClassObject>(&self, slot: SlotId) -> Option<&T> {
self.ensure_slots(slot + 1);
self.get_slot_foreign_mut(slot).map(|mr| &*mr)
}
pub fn get_slot_foreign_mut<T: 'static + ClassObject>(&self, slot: SlotId) -> Option<&mut T> {
self.ensure_slots(slot + 1);
if self.get_slot_type(slot) != SlotType::Foreign {
return None;
}
unsafe {
let ptr = wren_sys::wrenGetSlotForeign(self.vm, slot as raw::c_int);
if !ptr.is_null() {
let fo = std::ptr::read_unaligned(ptr as *mut ForeignObject<T>);
let ret = if fo.type_id == any::TypeId::of::<T>() {
fo.object.as_mut()
} else {
None
};
std::ptr::write_unaligned(ptr as *mut ForeignObject<T>, fo);
ret
} else {
None
}
}
}
pub fn use_class<T: ForeignItem + 'static, F, O>(&self, f: F) -> O
where
F: FnOnce(&VM, Option<&T::Class>) -> O,
{
let (update, class) = match self.classes_v2.borrow_mut().get_mut(&TypeId::of::<T>()) {
Some(cls) => (false, cls.clone()),
None => {
use crate::foreign_v2::V2ClassAllocator;
let class = Rc::new(RefCell::new(Box::new(T::Class::allocate()) as Box<dyn Any>));
(true, class)
}
};
let ret = f(self, class.borrow().downcast_ref());
if update {
self.classes_v2
.borrow_mut()
.insert(TypeId::of::<T>(), class);
}
ret
}
pub fn use_class_mut<T: ForeignItem + 'static, F, O>(&self, f: F) -> O
where
F: FnOnce(&VM, Option<&mut T::Class>) -> O,
{
let (update, class) = match self.classes_v2.borrow_mut().get_mut(&TypeId::of::<T>()) {
Some(cls) => (false, cls.clone()),
None => {
use crate::foreign_v2::V2ClassAllocator;
let class = Rc::new(RefCell::new(Box::new(T::Class::allocate()) as Box<dyn Any>));
(true, class)
}
};
let ret = f(self, class.borrow_mut().downcast_mut());
if update {
self.classes_v2
.borrow_mut()
.insert(TypeId::of::<T>(), class);
}
ret
}
pub fn set_slot_new_foreign<M: AsRef<str>, C: AsRef<str>, T: 'static + ClassObject>(
&self, module: M, class: C, object: T, slot: SlotId,
) -> Result<&mut T, ForeignSendError> {
self.set_slot_new_foreign_scratch(module, class, object, slot, 0)
}
pub fn set_slot_new_foreign_scratch<M: AsRef<str>, C: AsRef<str>, T: 'static + ClassObject>(
&self, module: M, class: C, object: T, slot: SlotId, scratch: SlotId,
) -> Result<&mut T, ForeignSendError> {
self.ensure_slots(slot.max(scratch) + 1);
let conf = unsafe {
std::ptr::read_unaligned(wren_sys::wrenGetUserData(self.vm) as *mut UserData)
};
self.ensure_slots(slot.max(scratch) + 1);
let ret = match conf
.library
.as_ref()
.and_then(|lib| lib.get_foreign_class(module.as_ref(), class.as_ref()))
{
None => Err(ForeignSendError::NoForeignClass), Some(runtime_class) => {
if runtime_class.type_id == any::TypeId::of::<T>() {
let new_obj = ForeignObject {
object: Box::into_raw(Box::new(object)),
type_id: any::TypeId::of::<T>(),
};
self.get_variable(module, class, scratch);
match self.get_slot_type(scratch) {
SlotType::Null => Err(ForeignSendError::NoWrenClass), SlotType::Unknown => unsafe {
let wptr = wren_sys::wrenSetSlotNewForeign(
self.vm,
slot as raw::c_int,
scratch as raw::c_int,
mem::size_of::<ForeignObject<T>>(),
);
if !wptr.is_null() {
std::ptr::write_unaligned(wptr as *mut _, new_obj);
}
match (wptr as *mut ForeignObject<T>).as_mut() {
Some(ptr) => Ok(ptr.object.as_mut().unwrap()),
None => Err(ForeignSendError::NoMemory),
}
},
_ => Err(ForeignSendError::NoWrenClass),
}
} else {
Err(ForeignSendError::ClassMismatch)
}
}
};
unsafe {
std::ptr::write_unaligned(wrenGetUserData(self.vm) as *mut UserData, conf);
}
ret
}
fn make_call_handle<'b>(
vm: *mut WrenVM, signature: FunctionSignature,
) -> Rc<FunctionHandle<'b>> {
let signature =
ffi::CString::new(signature.as_wren_string()).expect("signature conversion failed");
Rc::new(FunctionHandle(Handle {
handle: unsafe { wren_sys::wrenMakeCallHandle(vm, signature.as_ptr()) },
wvm: vm,
vm: marker::PhantomData,
}))
}
pub fn abort_fiber(&self, slot: SlotId) {
unsafe { wren_sys::wrenAbortFiber(self.vm, slot as raw::c_int) }
}
pub fn get_version_number(&self) -> i32 {
unsafe { wren_sys::wrenGetVersionNumber() }
}
}
impl Drop for VM {
fn drop(&mut self) {
unsafe {
let conf = wren_sys::wrenGetUserData(self.vm);
let _: Box<UserData> = Box::from_raw(conf as *mut _); wren_sys::wrenFreeVM(self.vm);
}
}
}