use std::sync::Arc;
use wasmtime::Caller;
use crate::{error, trace, CurrentPlugin, Error};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
#[repr(C)]
pub enum ValType {
I32,
I64,
F32,
F64,
V128,
FuncRef,
ExternRef,
}
pub const PTR: ValType = ValType::I64;
impl From<wasmtime::ValType> for ValType {
fn from(value: wasmtime::ValType) -> Self {
use wasmtime::ValType::*;
match value {
I32 => ValType::I32,
I64 => ValType::I64,
F32 => ValType::F32,
F64 => ValType::F64,
V128 => ValType::V128,
Ref(t) => {
if t.heap_type().is_func() {
ValType::FuncRef
} else {
ValType::ExternRef
}
}
}
}
}
impl From<ValType> for wasmtime::ValType {
fn from(value: ValType) -> Self {
use ValType::*;
match value {
I32 => wasmtime::ValType::I32,
I64 => wasmtime::ValType::I64,
F32 => wasmtime::ValType::F32,
F64 => wasmtime::ValType::F64,
V128 => wasmtime::ValType::V128,
FuncRef => wasmtime::ValType::FUNCREF,
ExternRef => wasmtime::ValType::EXTERNREF,
}
}
}
pub type Val = wasmtime::Val;
#[derive(Debug)]
pub struct CPtr {
ptr: *mut std::ffi::c_void,
free: Option<extern "C" fn(_: *mut std::ffi::c_void)>,
}
#[derive(Clone)]
pub(crate) enum UserDataHandle {
#[allow(dead_code)]
C(Arc<CPtr>),
#[allow(dead_code)]
Rust(Arc<std::sync::Mutex<dyn std::any::Any>>),
}
unsafe impl Send for UserDataHandle {}
unsafe impl Sync for UserDataHandle {}
#[derive(Debug)]
pub enum UserData<T: Sized> {
C(Arc<CPtr>),
Rust(Arc<std::sync::Mutex<T>>),
}
impl<T: Default> Default for UserData<T> {
fn default() -> Self {
UserData::new(T::default())
}
}
impl<T> Clone for UserData<T> {
fn clone(&self) -> Self {
match self {
UserData::C(ptr) => UserData::C(ptr.clone()),
UserData::Rust(data) => UserData::Rust(data.clone()),
}
}
}
impl<T> UserData<T> {
pub(crate) fn new_pointer(
ptr: *mut std::ffi::c_void,
free: Option<extern "C" fn(_: *mut std::ffi::c_void)>,
) -> Self {
UserData::C(Arc::new(CPtr { ptr, free }))
}
pub(crate) fn as_ptr(&self) -> *mut std::ffi::c_void {
match self {
UserData::C(ptr) => ptr.ptr,
_ => {
error!("Rust UserData cannot be used by C");
std::ptr::null_mut()
}
}
}
pub fn new(x: T) -> Self {
let data = Arc::new(std::sync::Mutex::new(x));
UserData::Rust(data)
}
pub fn get(&self) -> Result<Arc<std::sync::Mutex<T>>, Error> {
match self {
UserData::C { .. } => anyhow::bail!("C UserData should not be used from Rust"),
UserData::Rust(data) => Ok(data.clone()),
}
}
}
impl Drop for CPtr {
fn drop(&mut self) {
if !self.ptr.is_null() {
if let Some(free_data) = &self.free {
free_data(self.ptr);
self.ptr = std::ptr::null_mut();
}
}
}
}
unsafe impl<T> Send for UserData<T> {}
unsafe impl<T> Sync for UserData<T> {}
unsafe impl Send for CPtr {}
unsafe impl Sync for CPtr {}
type FunctionInner = dyn Fn(wasmtime::Caller<CurrentPlugin>, &[wasmtime::Val], &mut [wasmtime::Val]) -> Result<(), Error>
+ Sync
+ Send;
#[derive(Clone)]
pub struct Function {
pub(crate) name: String,
pub(crate) namespace: Option<String>,
pub(crate) params: Vec<ValType>,
pub(crate) results: Vec<ValType>,
pub(crate) f: Arc<FunctionInner>,
pub(crate) _user_data: UserDataHandle,
}
impl Function {
pub fn new<T: 'static, F>(
name: impl Into<String>,
params: impl IntoIterator<Item = ValType>,
results: impl IntoIterator<Item = ValType>,
user_data: UserData<T>,
f: F,
) -> Function
where
F: 'static
+ Fn(&mut CurrentPlugin, &[Val], &mut [Val], UserData<T>) -> Result<(), Error>
+ Sync
+ Send,
{
let data = user_data.clone();
let name = name.into();
let params = params.into_iter().collect();
let results = results.into_iter().collect();
trace!("Creating function {name}: params={params:?}, results={results:?}");
Function {
name,
params,
results,
f: Arc::new(
move |mut caller: Caller<_>, inp: &[Val], outp: &mut [Val]| {
let x = data.clone();
f(caller.data_mut(), inp, outp, x)
},
),
namespace: None,
_user_data: match &user_data {
UserData::C(ptr) => UserDataHandle::C(ptr.clone()),
UserData::Rust(x) => UserDataHandle::Rust(x.clone()),
},
}
}
pub(crate) fn ty(&self, engine: &wasmtime::Engine) -> wasmtime::FuncType {
wasmtime::FuncType::new(
engine,
self.params
.iter()
.cloned()
.map(wasmtime::ValType::from)
.collect::<Vec<_>>(),
self.results
.iter()
.cloned()
.map(wasmtime::ValType::from)
.collect::<Vec<_>>(),
)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn namespace(&self) -> Option<&str> {
self.namespace.as_deref()
}
pub fn set_namespace(&mut self, namespace: impl Into<String>) {
let ns = namespace.into();
trace!("Setting namespace for {} to {ns}", self.name);
self.namespace = Some(ns);
}
pub fn with_namespace(mut self, namespace: impl Into<String>) -> Self {
self.set_namespace(namespace);
self
}
pub fn params(&self) -> &[ValType] {
&self.params
}
pub fn results(&self) -> &[ValType] {
&self.results
}
}
#[macro_export]
macro_rules! host_fn {
($pub:vis $name: ident ($($arg:ident : $argty:ty),*) $(-> $ret:ty)? $b:block) => {
$crate::host_fn!($pub $name (user_data: (); $($arg : $argty),*) $(-> $ret)? {$b});
};
($pub:vis $name: ident ($user_data:ident : $dataty:ty; $($arg:ident : $argty:ty),*) $(-> $ret:ty)? $b:block) => {
$pub fn $name(
plugin: &mut $crate::CurrentPlugin,
inputs: &[$crate::Val],
outputs: &mut [$crate::Val],
#[allow(unused)]
mut $user_data: $crate::UserData<$dataty>,
) -> Result<(), $crate::Error> {
let output = {
let mut index = 0;
$(
let $arg: $argty = plugin.memory_get_val(&inputs[index])?;
#[allow(unused_assignments)]
{
index += 1;
}
)*
move || -> Result<_, $crate::Error> { $b }
};
let output = output()?;
let output: $crate::convert::MemoryHandle = plugin.memory_new(&output)?;
if !outputs.is_empty() {
outputs[0] = plugin.memory_to_val(output);
}
Ok(())
}
};
}