wasmi/func/
mod.rs

1mod caller;
2mod error;
3mod func_type;
4mod funcref;
5mod into_func;
6mod typed_func;
7
8pub(crate) use self::typed_func::CallResultsTuple;
9pub use self::{
10    caller::Caller,
11    error::FuncError,
12    func_type::FuncType,
13    funcref::FuncRef,
14    into_func::{IntoFunc, WasmRet, WasmTy, WasmTyList},
15    typed_func::{TypedFunc, WasmParams, WasmResults},
16};
17use super::{
18    engine::{CompiledFunc, DedupFuncType, FuncFinished, FuncParams},
19    AsContext,
20    AsContextMut,
21    Instance,
22    StoreContext,
23    Stored,
24};
25use crate::{collections::arena::ArenaIndex, engine::ResumableCall, Error, Val};
26use core::{fmt, fmt::Debug, num::NonZeroU32};
27use std::{boxed::Box, sync::Arc};
28
29/// A raw index to a function entity.
30#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
31pub struct FuncIdx(NonZeroU32);
32
33impl ArenaIndex for FuncIdx {
34    fn into_usize(self) -> usize {
35        self.0.get().wrapping_sub(1) as usize
36    }
37
38    fn from_usize(index: usize) -> Self {
39        index
40            .try_into()
41            .ok()
42            .map(|index: u32| index.wrapping_add(1))
43            .and_then(NonZeroU32::new)
44            .map(Self)
45            .unwrap_or_else(|| panic!("out of bounds func index {index}"))
46    }
47}
48
49/// A raw index to a host function trampoline entity.
50#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
51pub struct TrampolineIdx(usize);
52
53impl ArenaIndex for TrampolineIdx {
54    fn into_usize(self) -> usize {
55        self.0
56    }
57
58    fn from_usize(index: usize) -> Self {
59        Self(index)
60    }
61}
62
63/// A host function reference.
64#[derive(Debug, Copy, Clone)]
65#[repr(transparent)]
66pub struct Trampoline(Stored<TrampolineIdx>);
67
68impl Trampoline {
69    /// Creates a new host function reference.
70    pub(super) fn from_inner(stored: Stored<TrampolineIdx>) -> Self {
71        Self(stored)
72    }
73
74    /// Returns the underlying stored representation.
75    pub(super) fn as_inner(&self) -> &Stored<TrampolineIdx> {
76        &self.0
77    }
78}
79
80/// A Wasm or host function instance.
81#[derive(Debug)]
82pub enum FuncEntity {
83    /// A Wasm function.
84    Wasm(WasmFuncEntity),
85    /// A host function.
86    Host(HostFuncEntity),
87}
88
89impl From<WasmFuncEntity> for FuncEntity {
90    fn from(func: WasmFuncEntity) -> Self {
91        Self::Wasm(func)
92    }
93}
94
95impl From<HostFuncEntity> for FuncEntity {
96    fn from(func: HostFuncEntity) -> Self {
97        Self::Host(func)
98    }
99}
100
101/// A host function reference and its function type.
102#[derive(Debug, Copy, Clone)]
103pub struct HostFuncEntity {
104    /// The function type of the host function.
105    ty: DedupFuncType,
106    /// A reference to the trampoline of the host function.
107    func: Trampoline,
108}
109
110impl HostFuncEntity {
111    /// Creates a new [`HostFuncEntity`].
112    pub fn new(ty: DedupFuncType, func: Trampoline) -> Self {
113        Self { ty, func }
114    }
115
116    /// Returns the signature of the host function.
117    pub fn ty_dedup(&self) -> &DedupFuncType {
118        &self.ty
119    }
120
121    /// Returns the [`Trampoline`] of the host function.
122    pub fn trampoline(&self) -> &Trampoline {
123        &self.func
124    }
125}
126
127impl FuncEntity {
128    /// Returns the signature of the Wasm function.
129    pub fn ty_dedup(&self) -> &DedupFuncType {
130        match self {
131            Self::Wasm(func) => func.ty_dedup(),
132            Self::Host(func) => func.ty_dedup(),
133        }
134    }
135}
136
137/// A Wasm function instance.
138#[derive(Debug, Clone)]
139pub struct WasmFuncEntity {
140    /// The function type of the Wasm function.
141    ty: DedupFuncType,
142    /// The compiled function body of the Wasm function.
143    body: CompiledFunc,
144    /// The instance associated to the Wasm function.
145    instance: Instance,
146}
147
148impl WasmFuncEntity {
149    /// Creates a new Wasm function from the given raw parts.
150    pub fn new(signature: DedupFuncType, body: CompiledFunc, instance: Instance) -> Self {
151        Self {
152            ty: signature,
153            body,
154            instance,
155        }
156    }
157
158    /// Returns the signature of the Wasm function.
159    pub fn ty_dedup(&self) -> &DedupFuncType {
160        &self.ty
161    }
162
163    /// Returns the instance where the [`Func`] belong to.
164    pub fn instance(&self) -> &Instance {
165        &self.instance
166    }
167
168    /// Returns the Wasm function body of the [`Func`].
169    pub fn func_body(&self) -> CompiledFunc {
170        self.body
171    }
172}
173
174/// A host function instance.
175pub struct HostFuncTrampolineEntity<T> {
176    /// The type of the associated host function.
177    ty: FuncType,
178    /// The trampoline of the associated host function.
179    trampoline: TrampolineEntity<T>,
180}
181
182impl<T> Clone for HostFuncTrampolineEntity<T> {
183    fn clone(&self) -> Self {
184        Self {
185            ty: self.ty.clone(),
186            trampoline: self.trampoline.clone(),
187        }
188    }
189}
190
191impl<T> Debug for HostFuncTrampolineEntity<T> {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        Debug::fmt(&self.ty, f)
194    }
195}
196
197impl<T> HostFuncTrampolineEntity<T> {
198    /// Creates a new host function trampoline from the given dynamically typed closure.
199    pub fn new(
200        // engine: &Engine,
201        ty: FuncType,
202        func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Error> + Send + Sync + 'static,
203    ) -> Self {
204        // Preprocess parameters and results buffers so that we can reuse those
205        // computations within the closure implementation. We put both parameters
206        // and results into a single buffer which we can split to minimize the
207        // amount of allocations per trampoline invokation.
208        let params_iter = ty.params().iter().copied().map(Val::default);
209        let results_iter = ty.results().iter().copied().map(Val::default);
210        let len_params = ty.params().len();
211        let params_results: Box<[Val]> = params_iter.chain(results_iter).collect();
212        let trampoline = <TrampolineEntity<T>>::new(move |caller, args| {
213            // We are required to clone the buffer because we are operating within a `Fn`.
214            // This way the trampoline closure only has to own a single slice buffer.
215            // Note: An alternative solution is to use interior mutability but that solution
216            //       comes with its own downsides.
217            let mut params_results = params_results.clone();
218            let (params, results) = params_results.split_at_mut(len_params);
219            let func_results = args.decode_params_into_slice(params).unwrap();
220            func(caller, params, results)?;
221            Ok(func_results.encode_results_from_slice(results).unwrap())
222        });
223        // let ty = engine.alloc_func_type(ty.clone());
224        Self { ty, trampoline }
225    }
226
227    /// Creates a new host function trampoline from the given statically typed closure.
228    pub fn wrap<Params, Results>(func: impl IntoFunc<T, Params, Results>) -> Self {
229        let (ty, trampoline) = func.into_func();
230        // let ty = engine.alloc_func_type(signature);
231        Self { ty, trampoline }
232    }
233
234    /// Returns the [`FuncType`] of the host function.
235    pub fn func_type(&self) -> &FuncType {
236        &self.ty
237    }
238
239    /// Returns the trampoline of the host function.
240    pub fn trampoline(&self) -> &TrampolineEntity<T> {
241        &self.trampoline
242    }
243}
244
245type TrampolineFn<T> =
246    dyn Fn(Caller<T>, FuncParams) -> Result<FuncFinished, Error> + Send + Sync + 'static;
247
248pub struct TrampolineEntity<T> {
249    closure: Arc<TrampolineFn<T>>,
250}
251
252impl<T> Debug for TrampolineEntity<T> {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        f.debug_struct("TrampolineEntity").finish()
255    }
256}
257
258impl<T> TrampolineEntity<T> {
259    /// Creates a new [`TrampolineEntity`] from the given host function.
260    pub fn new<F>(trampoline: F) -> Self
261    where
262        F: Fn(Caller<T>, FuncParams) -> Result<FuncFinished, Error> + Send + Sync + 'static,
263    {
264        Self {
265            closure: Arc::new(trampoline),
266        }
267    }
268
269    /// Calls the host function trampoline with the given inputs.
270    ///
271    /// The result is written back into the `outputs` buffer.
272    pub fn call(
273        &self,
274        mut ctx: impl AsContextMut<Data = T>,
275        instance: Option<&Instance>,
276        params: FuncParams,
277    ) -> Result<FuncFinished, Error> {
278        let caller = <Caller<T>>::new(&mut ctx, instance);
279        (self.closure)(caller, params)
280    }
281}
282
283impl<T> Clone for TrampolineEntity<T> {
284    fn clone(&self) -> Self {
285        Self {
286            closure: self.closure.clone(),
287        }
288    }
289}
290
291/// A Wasm or host function reference.
292#[derive(Debug, Copy, Clone)]
293#[repr(transparent)]
294pub struct Func(Stored<FuncIdx>);
295
296impl Func {
297    /// Creates a new Wasm or host function reference.
298    pub(super) fn from_inner(stored: Stored<FuncIdx>) -> Self {
299        Self(stored)
300    }
301
302    /// Returns the underlying stored representation.
303    pub(super) fn as_inner(&self) -> &Stored<FuncIdx> {
304        &self.0
305    }
306
307    /// Creates a new [`Func`] with the given arguments.
308    ///
309    /// This is typically used to create a host-defined function to pass as an import to a Wasm module.
310    ///
311    /// - `ty`: the signature that the given closure adheres to,
312    ///         used to indicate what the inputs and outputs are.
313    /// - `func`: the native code invoked whenever this Func will be called.
314    ///           The closure is provided a [`Caller`] as its first argument
315    ///           which allows it to query information about the [`Instance`]
316    ///           that is associated to the call.
317    ///
318    /// # Note
319    ///
320    /// - The given [`FuncType`] `ty` must match the parameters and results otherwise
321    ///   the resulting host [`Func`] might trap during execution.
322    /// - It is the responsibility of the caller of [`Func::new`] to guarantee that
323    ///   the correct amount and types of results are written into the results buffer
324    ///   from the `func` closure. If an incorrect amount of results or types of results
325    ///   is written into the buffer then the remaining computation may fail in unexpected
326    ///   ways. This footgun can be avoided by using the typed [`Func::wrap`] method instead.
327    /// - Prefer using [`Func::wrap`] over this method if possible since [`Func`] instances
328    ///   created using this constructor have runtime overhead for every invocation that
329    ///   can be avoided by using [`Func::wrap`].
330    pub fn new<T>(
331        mut ctx: impl AsContextMut<Data = T>,
332        ty: FuncType,
333        func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Error> + Send + Sync + 'static,
334    ) -> Self {
335        let engine = ctx.as_context().store.engine();
336        let host_func = HostFuncTrampolineEntity::new(ty, func);
337        let ty_dedup = engine.alloc_func_type(host_func.func_type().clone());
338        let trampoline = host_func.trampoline().clone();
339        let func = ctx.as_context_mut().store.alloc_trampoline(trampoline);
340        ctx.as_context_mut()
341            .store
342            .inner
343            .alloc_func(HostFuncEntity::new(ty_dedup, func).into())
344    }
345
346    /// Creates a new host function from the given closure.
347    pub fn wrap<T, Params, Results>(
348        mut ctx: impl AsContextMut<Data = T>,
349        func: impl IntoFunc<T, Params, Results>,
350    ) -> Self {
351        let engine = ctx.as_context().store.engine();
352        let host_func = HostFuncTrampolineEntity::wrap(func);
353        let ty_dedup = engine.alloc_func_type(host_func.func_type().clone());
354        let trampoline = host_func.trampoline().clone();
355        let func = ctx.as_context_mut().store.alloc_trampoline(trampoline);
356        ctx.as_context_mut()
357            .store
358            .inner
359            .alloc_func(HostFuncEntity::new(ty_dedup, func).into())
360    }
361
362    /// Returns the signature of the function.
363    pub(crate) fn ty_dedup<'a, T: 'a>(
364        &self,
365        ctx: impl Into<StoreContext<'a, T>>,
366    ) -> &'a DedupFuncType {
367        ctx.into().store.inner.resolve_func(self).ty_dedup()
368    }
369
370    /// Returns the function type of the [`Func`].
371    pub fn ty(&self, ctx: impl AsContext) -> FuncType {
372        ctx.as_context()
373            .store
374            .inner
375            .resolve_func_type(self.ty_dedup(&ctx))
376    }
377
378    /// Calls the Wasm or host function with the given inputs.
379    ///
380    /// The result is written back into the `outputs` buffer.
381    ///
382    /// # Errors
383    ///
384    /// - If the function returned a [`Error`].
385    /// - If the types of the `inputs` do not match the expected types for the
386    ///   function signature of `self`.
387    /// - If the number of input values does not match the expected number of
388    ///   inputs required by the function signature of `self`.
389    /// - If the number of output values does not match the expected number of
390    ///   outputs required by the function signature of `self`.
391    pub fn call<T>(
392        &self,
393        mut ctx: impl AsContextMut<Data = T>,
394        inputs: &[Val],
395        outputs: &mut [Val],
396    ) -> Result<(), Error> {
397        self.verify_and_prepare_inputs_outputs(ctx.as_context(), inputs, outputs)?;
398        // Note: Cloning an [`Engine`] is intentionally a cheap operation.
399        ctx.as_context().store.engine().clone().execute_func(
400            ctx.as_context_mut(),
401            self,
402            inputs,
403            outputs,
404        )?;
405        Ok(())
406    }
407
408    /// Calls the Wasm or host function with the given inputs.
409    ///
410    /// The result is written back into the `outputs` buffer.
411    ///
412    /// Returns a resumable handle to the function invocation upon
413    /// encountering host errors with which it is possible to handle
414    /// the error and continue the execution as if no error occurred.
415    ///
416    /// # Note
417    ///
418    /// This is a non-standard WebAssembly API and might not be available
419    /// at other WebAssembly engines. Please be aware that depending on this
420    /// feature might mean a lock-in to Wasmi for users.
421    ///
422    /// # Errors
423    ///
424    /// - If the function returned a Wasm [`Error`].
425    /// - If the types of the `inputs` do not match the expected types for the
426    ///   function signature of `self`.
427    /// - If the number of input values does not match the expected number of
428    ///   inputs required by the function signature of `self`.
429    /// - If the number of output values does not match the expected number of
430    ///   outputs required by the function signature of `self`.
431    pub fn call_resumable<T>(
432        &self,
433        mut ctx: impl AsContextMut<Data = T>,
434        inputs: &[Val],
435        outputs: &mut [Val],
436    ) -> Result<ResumableCall, Error> {
437        self.verify_and_prepare_inputs_outputs(ctx.as_context(), inputs, outputs)?;
438        // Note: Cloning an [`Engine`] is intentionally a cheap operation.
439        ctx.as_context()
440            .store
441            .engine()
442            .clone()
443            .execute_func_resumable(ctx.as_context_mut(), self, inputs, outputs)
444            .map_err(Into::into)
445            .map(ResumableCall::new)
446    }
447
448    /// Verify that the `inputs` and `outputs` value types match the function signature.
449    ///
450    /// Since [`Func`] is a dynamically typed function instance there is
451    /// a need to verify that the given input parameters match the required
452    /// types and that the given output slice matches the expected length.
453    ///
454    /// These checks can be avoided using the [`TypedFunc`] API.
455    ///
456    /// # Errors
457    ///
458    /// - If the `inputs` value types do not match the function input types.
459    /// - If the number of `inputs` do not match the function input types.
460    /// - If the number of `outputs` do not match the function output types.
461    fn verify_and_prepare_inputs_outputs(
462        &self,
463        ctx: impl AsContext,
464        inputs: &[Val],
465        outputs: &mut [Val],
466    ) -> Result<(), FuncError> {
467        let fn_type = self.ty_dedup(ctx.as_context());
468        ctx.as_context()
469            .store
470            .inner
471            .resolve_func_type_with(fn_type, |func_type| {
472                func_type.match_params(inputs)?;
473                func_type.match_results(outputs, false)?;
474                func_type.prepare_outputs(outputs);
475                Ok(())
476            })
477    }
478
479    /// Creates a new [`TypedFunc`] from this [`Func`].
480    ///
481    /// # Note
482    ///
483    /// This performs static type checks given `Params` as parameter types
484    /// to [`Func`] and `Results` as result types of [`Func`] so that those
485    /// type checks can be avoided when calling the created [`TypedFunc`].
486    ///
487    /// # Errors
488    ///
489    /// If the function signature of `self` does not match `Params` and `Results`
490    /// as parameter types and result types respectively.
491    pub fn typed<Params, Results>(
492        &self,
493        ctx: impl AsContext,
494    ) -> Result<TypedFunc<Params, Results>, Error>
495    where
496        Params: WasmParams,
497        Results: WasmResults,
498    {
499        TypedFunc::new(ctx, *self)
500    }
501}