use super::{
super::engine::{FuncFinished, FuncParams, FuncResults},
TrampolineEntity,
};
use crate::{
core::{DecodeUntypedSlice, EncodeUntypedSlice, UntypedVal, ValType, F32, F64},
Caller,
Error,
ExternRef,
FuncRef,
FuncType,
};
use core::{array, iter::FusedIterator};
pub trait IntoFunc<T, Params, Results>: Send + Sync + 'static {
#[doc(hidden)]
type Params: WasmTyList;
#[doc(hidden)]
type Results: WasmTyList;
#[doc(hidden)]
fn into_func(self) -> (FuncType, TrampolineEntity<T>);
}
macro_rules! impl_into_func {
( $n:literal $( $tuple:ident )* ) => {
impl<T, F, $($tuple,)* R> IntoFunc<T, ($($tuple,)*), R> for F
where
F: Fn($($tuple),*) -> R,
F: Send + Sync + 'static,
$(
$tuple: WasmTy,
)*
R: WasmRet,
{
type Params = ($($tuple,)*);
type Results = <R as WasmRet>::Ok;
#[allow(non_snake_case)]
fn into_func(self) -> (FuncType, TrampolineEntity<T>) {
IntoFunc::into_func(
move |
_: Caller<'_, T>,
$(
$tuple: $tuple,
)*
| {
(self)($($tuple),*)
}
)
}
}
impl<T, F, $($tuple,)* R> IntoFunc<T, (Caller<'_, T>, $($tuple),*), R> for F
where
F: Fn(Caller<T>, $($tuple),*) -> R,
F: Send + Sync + 'static,
$(
$tuple: WasmTy,
)*
R: WasmRet,
{
type Params = ($($tuple,)*);
type Results = <R as WasmRet>::Ok;
#[allow(non_snake_case)]
fn into_func(self) -> (FuncType, TrampolineEntity<T>) {
let signature = FuncType::new(
<Self::Params as WasmTyList>::types(),
<Self::Results as WasmTyList>::types(),
);
let trampoline = TrampolineEntity::new(
move |caller: Caller<T>, params_results: FuncParams| -> Result<FuncFinished, Error> {
let (($($tuple,)*), func_results): (Self::Params, FuncResults) = params_results.decode_params();
let results: Self::Results =
(self)(caller, $($tuple),*).into_fallible()?;
Ok(func_results.encode_results(results))
},
);
(signature, trampoline)
}
}
};
}
for_each_tuple!(impl_into_func);
pub trait WasmRet {
#[doc(hidden)]
type Ok: WasmTyList;
#[doc(hidden)]
fn into_fallible(self) -> Result<<Self as WasmRet>::Ok, Error>;
}
impl<T1> WasmRet for T1
where
T1: WasmTy,
{
type Ok = T1;
#[inline]
fn into_fallible(self) -> Result<Self::Ok, Error> {
Ok(self)
}
}
impl<T1> WasmRet for Result<T1, Error>
where
T1: WasmTy,
{
type Ok = T1;
#[inline]
fn into_fallible(self) -> Result<<Self as WasmRet>::Ok, Error> {
self
}
}
macro_rules! impl_wasm_return_type {
( $n:literal $( $tuple:ident )* ) => {
impl<$($tuple),*> WasmRet for ($($tuple,)*)
where
$(
$tuple: WasmTy
),*
{
type Ok = ($($tuple,)*);
#[inline]
fn into_fallible(self) -> Result<Self::Ok, Error> {
Ok(self)
}
}
impl<$($tuple),*> WasmRet for Result<($($tuple,)*), Error>
where
$(
$tuple: WasmTy
),*
{
type Ok = ($($tuple,)*);
#[inline]
fn into_fallible(self) -> Result<<Self as WasmRet>::Ok, Error> {
self
}
}
};
}
for_each_tuple!(impl_wasm_return_type);
pub trait WasmTy: From<UntypedVal> + Into<UntypedVal> + Send {
#[doc(hidden)]
fn ty() -> ValType;
}
macro_rules! impl_wasm_type {
( $( type $rust_type:ty = $wasmi_type:ident );* $(;)? ) => {
$(
impl WasmTy for $rust_type {
#[inline]
fn ty() -> ValType {
ValType::$wasmi_type
}
}
)*
};
}
impl_wasm_type! {
type u32 = I32;
type u64 = I64;
type i32 = I32;
type i64 = I64;
type F32 = F32;
type F64 = F64;
type f32 = F32;
type f64 = F64;
type FuncRef = FuncRef;
type ExternRef = ExternRef;
}
pub trait WasmTyList: DecodeUntypedSlice + EncodeUntypedSlice + Sized + Send {
#[doc(hidden)]
const LEN: usize;
#[doc(hidden)]
type Types: IntoIterator<IntoIter = Self::TypesIter, Item = ValType>
+ AsRef<[ValType]>
+ AsMut<[ValType]>
+ Copy
+ Clone;
#[doc(hidden)]
type TypesIter: ExactSizeIterator<Item = ValType> + DoubleEndedIterator + FusedIterator;
#[doc(hidden)]
type Values: IntoIterator<IntoIter = Self::ValuesIter, Item = UntypedVal>
+ AsRef<[UntypedVal]>
+ AsMut<[UntypedVal]>
+ Copy
+ Clone;
#[doc(hidden)]
type ValuesIter: ExactSizeIterator<Item = UntypedVal> + DoubleEndedIterator + FusedIterator;
#[doc(hidden)]
fn types() -> Self::Types;
#[doc(hidden)]
fn values(self) -> Self::Values;
#[doc(hidden)]
fn from_values(values: &[UntypedVal]) -> Option<Self>;
}
impl<T1> WasmTyList for T1
where
T1: WasmTy,
{
const LEN: usize = 1;
type Types = [ValType; 1];
type TypesIter = array::IntoIter<ValType, 1>;
type Values = [UntypedVal; 1];
type ValuesIter = array::IntoIter<UntypedVal, 1>;
#[inline]
fn types() -> Self::Types {
[<T1 as WasmTy>::ty()]
}
#[inline]
fn values(self) -> Self::Values {
[<T1 as Into<UntypedVal>>::into(self)]
}
#[inline]
fn from_values(values: &[UntypedVal]) -> Option<Self> {
if let [value] = *values {
return Some(value.into());
}
None
}
}
macro_rules! impl_wasm_type_list {
( $n:literal $( $tuple:ident )* ) => {
impl<$($tuple),*> WasmTyList for ($($tuple,)*)
where
$(
$tuple: WasmTy
),*
{
const LEN: usize = $n;
type Types = [ValType; $n];
type TypesIter = array::IntoIter<ValType, $n>;
type Values = [UntypedVal; $n];
type ValuesIter = array::IntoIter<UntypedVal, $n>;
#[inline]
fn types() -> Self::Types {
[$(
<$tuple as WasmTy>::ty()
),*]
}
#[inline]
#[allow(non_snake_case)]
fn values(self) -> Self::Values {
let ($($tuple,)*) = self;
[$(
<$tuple as Into<UntypedVal>>::into($tuple)
),*]
}
#[inline]
#[allow(non_snake_case)]
fn from_values(values: &[UntypedVal]) -> Option<Self> {
if let [$($tuple),*] = *values {
return Some(
( $( Into::into($tuple), )* )
)
}
None
}
}
};
}
for_each_tuple!(impl_wasm_type_list);
#[cfg(test)]
mod tests {
use super::*;
use std::string::String;
pub struct ImplementsWasmRet<T> {
marker: core::marker::PhantomData<fn() -> T>,
}
pub trait ImplementsWasmRetFallback {
const VALUE: bool = false;
}
impl<T> ImplementsWasmRetFallback for ImplementsWasmRet<T> {}
impl<T> ImplementsWasmRet<T>
where
T: WasmRet,
{
pub const VALUE: bool = true;
}
#[macro_export]
#[doc(hidden)]
macro_rules! implements_wasm_results {
( $T:ty $(,)? ) => {{
#[allow(unused_imports)]
use ImplementsWasmRetFallback as _;
ImplementsWasmRet::<$T>::VALUE
}};
}
#[test]
fn into_func_trait_impls() {
assert!(!implements_wasm_results!(String));
assert!(!implements_wasm_results!(Option<i32>));
assert!(implements_wasm_results!(()));
assert!(implements_wasm_results!(i32));
assert!(implements_wasm_results!((i32,)));
assert!(implements_wasm_results!((i32, u32, i64, u64, F32, F64)));
assert!(implements_wasm_results!(Result<(), Error>));
assert!(implements_wasm_results!(Result<i32, Error>));
assert!(implements_wasm_results!(Result<(i32,), Error>));
assert!(implements_wasm_results!(Result<(i32, u32, i64, u64, F32, F64), Error>));
}
}