1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
use super::{into_func::WasmTyList, Func};
use crate::{
core::UntypedVal,
engine::{CallParams, CallResults},
AsContext,
AsContextMut,
Error,
TypedResumableCall,
};
use core::{fmt, fmt::Debug, marker::PhantomData};
/// A typed [`Func`] instance.
///
/// # Note
///
/// This allows a more efficient execution by avoiding type checks
/// upon function call since those type checks are performed upon [`TypedFunc`]
/// construction and enforced by the Rust type system.
///
/// Use [`TypedFunc`] instead of [`Func`] if possible.
#[repr(transparent)]
pub struct TypedFunc<Params, Results> {
/// The parameter and result typed encoded in Rust type system.
signature: PhantomData<fn(Params) -> Results>,
/// The underlying [`Func`] instance.
func: Func,
}
impl<Params, Results> Debug for TypedFunc<Params, Results> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypedFunc")
.field("signature", &self.signature)
.field("func", &self.func)
.finish()
}
}
impl<Params, Results> Copy for TypedFunc<Params, Results> {}
impl<Params, Results> Clone for TypedFunc<Params, Results> {
fn clone(&self) -> TypedFunc<Params, Results> {
*self
}
}
impl<Params, Results> TypedFunc<Params, Results> {
/// Returns the underlying [`Func`].
///
/// # Note
///
/// This loses the static type information in the process.
pub fn func(&self) -> &Func {
&self.func
}
}
impl<Params, Results> TypedFunc<Params, Results>
where
Params: WasmParams,
Results: WasmResults,
{
/// Creates a new [`TypedFunc`] for the given [`Func`] using the static typing.
///
/// # Errors
///
/// If the provided static types `Params` and `Results` for the parameters
/// and result types of `func` mismatch the signature of `func`.
pub(crate) fn new(ctx: impl AsContext, func: Func) -> Result<Self, Error> {
let func_type = func.ty(&ctx);
let (actual_params, actual_results) = (
<Params as WasmTyList>::types(),
<Results as WasmTyList>::types(),
);
func_type.match_params(actual_params.as_ref())?;
func_type.match_results(actual_results.as_ref(), true)?;
Ok(Self {
signature: PhantomData,
func,
})
}
/// Calls this Wasm or host function with the specified parameters.
///
/// Returns either the results of the call, or a [`Error`] if one happened.
///
/// For more information, see the [`Func::typed`] and [`Func::call`]
/// documentation.
///
/// # Panics
///
/// Panics if `ctx` does not own this [`TypedFunc`].
///
/// # Errors
///
/// If the execution of the called Wasm function traps.
pub fn call(&self, mut ctx: impl AsContextMut, params: Params) -> Result<Results, Error> {
// Note: Cloning an [`Engine`] is intentionally a cheap operation.
ctx.as_context().store.engine().clone().execute_func(
ctx.as_context_mut(),
&self.func,
params,
<CallResultsTuple<Results>>::default(),
)
}
/// Calls this Wasm or host function with the specified parameters.
///
/// Returns a resumable handle to the function invocation upon
/// encountering host errors with which it is possible to handle
/// the error and continue the execution as if no error occurred.
///
/// # Note
///
/// This is a non-standard WebAssembly API and might not be available
/// at other WebAssembly engines. Please be aware that depending on this
/// feature might mean a lock-in to Wasmi for users.
///
/// # Errors
///
/// If the function returned a [`Error`] originating from WebAssembly.
pub fn call_resumable(
&self,
mut ctx: impl AsContextMut,
params: Params,
) -> Result<TypedResumableCall<Results>, Error> {
// Note: Cloning an [`Engine`] is intentionally a cheap operation.
ctx.as_context()
.store
.engine()
.clone()
.execute_func_resumable(
ctx.as_context_mut(),
&self.func,
params,
<CallResultsTuple<Results>>::default(),
)
.map(TypedResumableCall::new)
}
}
impl<Params> CallParams for Params
where
Params: WasmParams,
{
type Params = <Params as WasmTyList>::ValuesIter;
#[inline]
fn call_params(self) -> Self::Params {
<Params as WasmTyList>::values(self).into_iter()
}
}
/// Wrapper around the result tuple types of a [`TypedFunc`].
///
/// # Note
///
/// This type is a utility in order to provide an efficient implementation
/// of the [`CallResults`] trait required for executing the [`TypedFunc`]
/// via the [`Engine`].
///
/// [`Engine`]: [`crate::Engine`].
pub struct CallResultsTuple<Results> {
_marker: PhantomData<fn() -> Results>,
}
impl<Results> Default for CallResultsTuple<Results> {
fn default() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<Results> Copy for CallResultsTuple<Results> {}
impl<Results> Clone for CallResultsTuple<Results> {
fn clone(&self) -> Self {
*self
}
}
impl<Results> CallResults for CallResultsTuple<Results>
where
Results: WasmResults,
{
type Results = Results;
fn len_results(&self) -> usize {
<Results as WasmTyList>::LEN
}
fn call_results(self, results: &[UntypedVal]) -> Self::Results {
<Results as WasmTyList>::from_values(results)
.expect("unable to construct typed results from call results")
}
}
/// The typed parameters of a [`TypedFunc`].
pub trait WasmParams: WasmTyList {}
impl<T> WasmParams for T where T: WasmTyList {}
/// The typed results of a [`TypedFunc`].
pub trait WasmResults: WasmTyList {}
impl<T> WasmResults for T where T: WasmTyList {}